Skip to content

UXCore: mobile enhancements#146

Merged
MaryWylde merged 45 commits into
mainfrom
dev
Jun 16, 2026
Merged

UXCore: mobile enhancements#146
MaryWylde merged 45 commits into
mainfrom
dev

Conversation

@MaryWylde

Copy link
Copy Markdown
Contributor

No description provided.

MaryWylde and others added 30 commits June 1, 2026 13:36
Import the keepsimple Library feature source from
github.com/keepsimpleio/library into keepsimple's existing folder
structure, namespaced under `library/` to avoid collisions:

- components atoms/molecules/organisms -> src/components/library/
- templates                            -> src/layouts/library/
- types (+ d.ts type shims)            -> src/local-types/library/
- utils                                -> src/utils/library/
- hooks                                -> src/hooks/library/
- constants (+ config/seo.config.ts)   -> src/constants/library/
- contexts                             -> src/components/Context/library/
- axios/cookie libs                    -> src/lib/library/
- client services (src/api + app/api)  -> src/api/library/
- code-imported assets                 -> src/assets/library/
- shared scss                          -> src/styles/library/
- AGENT.md -> src/components/library/LIBRARY_AGENT.md

Excluded per task: package.json/lockfile, node_modules, CI/workflows,
.claude/.husky/.idea, eslint/tsconfig, README/docs, Storybook (.storybook
+ *.stories.*), App-Router routing/infra (app/page, app/layout, loading,
app/auth), all NextAuth setup, and route handlers (libraries proxy,
geoip /api/user, test-login mock). Imports/deps/auth/route are fixed in
later steps.
Copy Library's public/images into public/library/images so they don't
collide with keepsimple's public root, and rewrite the three absolute
public references in the imported code from /images/... to
/library/images/...:

- atoms/Icon/Icon.tsx       — SVG sprite href (/library/images/icons/all.svg)
- constants/library/seo.config.ts — default OG image path
- layouts/library/Home/Home.tsx   — landing cover image

JS module imports of assets (@/assets/images/*) are alias rewrites,
handled in Step 4.
Add only the runtime packages the migrated Library code imports that
keepsimple was missing (versions from Library, since these are new —
no shared-version conflict). Shared packages (react, react-dom, next,
next-auth, classnames, react-tooltip, date-fns, sass, geoip-lite) keep
keepsimple's existing versions.

Added (dependencies):
- axios ^1.13.4            — Library Strapi client (src/lib/library/axios)
- js-cookie ^3.0.5         — cookie helper (src/lib/library/cookie)
- zod ^4.3.6               — form/validation schemas (src/utils/library/schema)
- react-hook-form ^7.71.1  — forms in modals
- @hookform/resolvers ^5.2.2 — zod<->react-hook-form bridge
- react-dropzone ^15.0.0   — ImageDropzone molecule
- react-day-picker ^10.0.0 — DatePicker molecule
- @dnd-kit/core ^6.3.1, @dnd-kit/sortable ^10.0.0, @dnd-kit/utilities ^3.2.2
                           — ReorderGrid drag-and-drop

Added (devDependencies):
- @types/js-cookie ^3.0.6  — js-cookie ships no types

Not added: commander/fs-extra (Library generators — not migrated),
Storybook/vitest/playwright tooling (excluded). Single yarn install run
with yarn classic (v1) to keep yarn.lock in its canonical v1 format.
Rewrite Library's @/* aliases to keepsimple's per-folder aliases, relocate
assets under @iCons, strip 'use client', convert next/navigation to
next/router, and rewire auth to keepsimple's NextAuth + @api/auth. Make
the next-auth.d.ts a proper module augmentation so it no longer clobbers
next-auth's default export.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Add Pages Router entry points for the migrated Library feature:
/library (Home) and /library/[username] (dashboard). Both use
getServerSideProps for SSR and reuse keepsimple's SeoGenerator
instead of next/head blocks. Scope a sassOptions.additionalData
injection in next.config.js to library SCSS modules only, so the
placeholder selectors the original app injected globally resolve
without touching keepsimple's own styles.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Add the Library nav item (flagged via isLibraryEnabled), a "My Library"
shortcut in the user-profile dropdown, and notFound guards on the library
pages when the feature is disabled.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Switch the sticky global header to #F8F1E5 on /library paths (including the
dark-theme variant, since the library has no dark mode) so it blends with the
library surface.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Retire the old library Header organism in favor of a sticky LibraryToolbar:
shelf jump-pills with overflow arrows, a Search Everywhere input, and an Add
shelf control that disables at the 21-shelf cap. Adds Input onKeyDown for the
search and MAX_SHELVES_PER_LIBRARY.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Move the library's global CSS behind a single library-global entry imported
from _app, scope resets under .library, and wrap portaled menus/modals in a
.library container so they inherit the tokens. Self-host Source Serif 4 (the
Google Fonts @import is blocked by CSP) and declare font-family vars.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Override SVGO's removeViewBox in the SVGR loader so downscaled shelf icons keep
their coordinate system instead of cropping, and strip stray blue frame-outline
artifacts from the book and video glyphs.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Edit a shelf name by clicking it (drop the settings-menu edit entry), drop the
doubled brown border on the settings-trigger dropdown, append new shelves at the
end with an inline loader instead of a full-screen swap, hide private shelves
from non-owners, give overflowing object rows a 12px #C0B6AE scrollbar with 40px
top headroom, and glide cards to their new slots after a reorder via a FLIP
animation.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Add the reorderObjects/reorderShelves endpoints and wire the step-2 ReorderGrid
to persist on save. Resolve the shelf id from a passed-in fallback so edit mode
no longer silently skips, and sort populated shelves/objects by order:asc so the
saved sequence survives a refetch. Stop swallowing reorder errors — log status +
body, roll back the optimistic order, and message the user. Also derive the
ImageDropzone preview synchronously so the cover no longer flashes empty.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Prefer the optimistically updated currentShelves over the stale one-shot
library fetch for the sidebar book/video/song totals so counts bump on upload,
and load the tag list on mount so the Tags panel isn't empty until the first
mutation.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Always render the tag column (even when empty) and fix container dimensions so
book/audio/video cards keep a consistent width with or without tags, and paint
the book cover gradient immediately so freshly uploaded covers don't flash blank.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Stamp publishedAt when creating a tag (the content type is draftAndPublish), so
newly created tags don't vanish on the next load.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- flatten src/api/library/library/ up one level and split strapi.ts into
  per-call files (getLibrariesList, getLibrariesPaginated, getSingleLibrary,
  user/getUserInfo, shared libraryCardPopulate); drop dead getShelves
- remove App-Router leftovers: molecules/SEO, molecules/SignInModal,
  molecules/UserDropDown, utils/library/seo.ts (host reuses SeoGenerator,
  login modal, and keepsimple header)
- consolidate library fonts into globals.scss; delete styles/library/fonts.scss
- Dropdown: move inline styles into module.scss classes
- AudioCard: replace hardcoded focus color with --focus-ring var
- AuthContext: reset in-memory token/account on logout, use js-cookie helpers
- GlobalStateContext: drop unconditional first fetch (double-load), type
  libraries as StrapiLibrariesResponse; simplify Sidebar narrowing
- createTag: explicit response type; getTagsList: drop dead SSR header branch
- UserProfile: fill in hy (Armenian) menu labels
- gitignore LIBRARY_TODO.md scratch file

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- gate /library/:username create UI + right panel on the can-create-library
  flag from /api/users/me
- warn instead of crashing when a shelf, object, or tag name already exists
- bundles in-progress object-select, shelf-rename, and share-URL work
  already in the tree

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- navigate the sidebar library dropdown by username slug (?? id) so it
  lands on /library/[username] instead of a 404 at /library/[numericId];
  key selectedLibrary lookup on the same slug so live counts resolve
- suffix tag slugs with Date.now() instead of a hardcoded -1 so names
  that normalize alike no longer collide on Strapi's unique-slug constraint
- sanitize object description with sanitizeHtml before dangerouslySetInnerHTML
  to close a stored-XSS vector on user-supplied CKEditor content

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Drop NEXT_PUBLIC_ENABLE_LIBRARY and gate the Library feature on the shared
NEXT_PUBLIC_ENV (enabled on dev + staging, hidden on prod). Removes a
redundant build-time flag — the env var already drives other env-specific
gating (admin copilot pages, _app), so a second flag was dead weight.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
library: gate on NEXT_PUBLIC_ENV instead of a dedicated flag
…ists

Decide the Sidebar panel by whether the viewed library is mine: my own
shows my account identity and is editable, anyone else's (or logged out)
shows that library's public data only. Ownership now matches the loaded
owner's account id/username, and stale owner data is dropped on navigation
so my identity never bleeds onto someone else's page.

Let a permitted owner edit their About panel before any library row
exists — the row is bootstrapped lazily on first save (like adding the
first shelf), and the page reloads by direct id instead of the restricted
owner relation-filter.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Owner-only flow to mint a share link for a curated set of public-shelf
objects: a context-backed selection (one link spans the whole library,
capped at 21 objects), a Select chip on the cards, a ShareSelectionPanel
to reorder/remove/clear, and a SharedWithYou modal for the recipient.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Replace the single `[username].tsx` page with an optional catch-all so
`/library/[username]` and `/library/[username]/[slug]` share one module.
The opened object is addressed by the URL (slugified title + id), and
open/close are shallow `router.push` transitions so the overview modal
appears over the already-loaded library with no refetch or remount.
The shelf that actually holds the object resolves it from the slug's
trailing id, keeping its real shelf context.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
`/library/[username]/share/[token]` is a literal sibling of the object
catch-all (and wins for that path). It resolves the token via
getShareLink and renders the curated objects in the SharedWithYou view.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
The dropdown sourced options from a global `/single-shelves` fetch, so
it offered every user's public shelves (and the backend then 403s the
move). Drive it from the viewed library's own shelves (currentShelves
in GlobalState), filtered to the object's type, and exclude the object's
current shelf — falling back to defaultShelfId since a PUT response
doesn't populate the shelf relation. Disable the dropdown when no
eligible shelf remains.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
`currentLibraryId` was the last path segment of asPath, which on the new
object (`/[slug]`) and share (`/share/[token]`) routes is the slug/token,
not the username — breaking the sidebar's share-library URL and the
ownership fallback. Read `[username]` from the router params instead.
Also remove the temporary getMyLibrary debug effect that logged account
data and fired an extra request on every authenticated page load.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Wire the previously no-op "Select shelf" button to bulk-toggle the whole
shelf into the share selection (new selectMany/removeMany context
helpers, capped at 21). It's owner- and public-only like the per-card
chip, and disabled when the shelf is empty or at the cap. Switching a
shelf to private now strips its objects from the selection, so a link is
never minted with objects the backend would reject.

Also relocate the new objectSlug/mapSharedObject utils from the legacy
src/utils tree to src/lib per AGENTS.md.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
library: source right panel by ownership and edit before a library exsts
MaryWylde and others added 15 commits June 10, 2026 20:13
…ects on shelves

- next.config: add SVGO prefixIds so SVGR-inlined icons (book, video, their
  shadows, delete, edit) stop colliding on minified ids on the Library page.
- BookCard/AudioCard: drop the BookShadowIcon + alpha overlay and use the
  book-cover.png / audio-cover.png placeholders (which bake in their own
  spine/shadow), filling the card; covers loading state too.
- Shelf: raise the shelf board above the cards (z-index) so each object rests
  on the shelf with its base behind the front edge instead of floating over it.
…om scrollbar, upload size errors

- Shelf: drop scroll arrows, ride custom #C0B6AE scrollbar on the board, fix card bleed-through
- Object modal: reflect shelf renames instantly from live GlobalState shelves
- Cards: book/audio placeholders + sizing, video drop shadow
- StepIndicator: larger circles, green double-ring active state, long connector
- Guest mode: hide owner-only edit/add controls via viewAsOwner
- Uploads: pre-flight 5 MB size error instead of a raw network failure
- TagMultiSelect: keep the menu anchored to the trigger when chips appear

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
library: fix broken icons + replace book/audio placeholders, seat objects on shelves
- Replace the plain "Loading…" text with the library Loader spinner, centered in the content area
- Hide WebKit's native search-cancel button so it no longer doubles up with the custom clear icon

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…c case rework

- bias modal on mobile is a full-height bottom sheet (dvh-safe on iOS), header icons aligned, vertical use-case switcher
- restore swipe left/right bias navigation directly in UXCoreModal (legacy UXCoreModalMobile stopped rendering after repo merge)
- rating block moved inside the scrollable body instead of a sticky footer
- Ask widget pill: dark surface in dark theme, no pulse on touch devices
- offsec cases #2-#4 reworked for distinct scenarios; left/right wording replaced with first/second for stacked mobile layout
…e, bias popup, route loaders

Revive the orphaned uxcore useMobile hook (dead since the UXCoreOSS fold-in,
so phones got desktop layout) and pin slug pages to the new modals. UXCG
question modal goes fullscreen on mobile with sticky rating row and nav
pills; add theme toggle to its header; restyle the mobile bias popup as a
centered card with dark-theme support and same-tab bias links. Route
loading overlay on UXCG/Table navigation, tooltip viewport clamp, dark
widget pill, UXCAT guest-profile clipping fixes.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Page-element highlighting fired on every new answer regardless of widget
state, so a returning visitor with a collapsed pill saw host elements glow
with no obvious cause. Highlight is now allowed only while the panel is
open (active); collapsing clears it, reopening re-applies it. Same path on
mobile and desktop.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
The "how useful…" NPS row sat outside the scrollable modal body and stayed
pinned above the nav controls. Moved it inside the body so it scrolls with
the answer; only Previous/Next stay sticky at the bottom.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
The Cybersecurity (OffSec) layer is still work-in-progress. Surface its
switcher and view only when NEXT_PUBLIC_ENV is dev, and treat a persisted
isOffsecView flag as inactive off-dev so a prior dev session can't leak the
half-baked layer onto staging or prod. Building continues on dev as before.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Add client-side search that filters shelf objects by title, author, and
tag name with a smooth card-enter animation and empty-results state.
Wire up title autocomplete and autofill (books/video via Google APIs,
audio via iTunes) with server-side API routes. Make object modals
responsive with visible custom scrollbars, fix mobile toolbar/header
overlap, keep the Select-shelf button visible on private shelves, add a
mobile trigger for the right info panel, gate the Tags Edit button on
having at least one tag, and derive object Source from the form URL.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
# Conflicts:
#	src/uxcore/components/UXCoreModal/UXCoreModal.tsx
library: spinner loader + hide native search clear button
@MaryWylde MaryWylde merged commit 3b964ea into main Jun 16, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants