Thread library should source from the service catalog (61 colors) + tier 2 user colors + tier 3 bulk self-source import — drop the 20-color fictional fallback #126

Closed
opened 2026-10-03 02:39:08 +00:00 by Mark · 0 comments
Owner

Finding

The thread library is built on a 20-color fictional fallback (DEFAULT_THREAD_LIBRARY in src/components/workspace/threadLibrary.ts) that the orchestrator inherited from a hardcoded BROTHER_PR10_COLORS const. The fallback is dead weight:

  • The service at GET /v1/threads/catalog returns 61 real thread colors. The local 20 doesn't match.
  • 13 of the 20 fallback entries don't exist in the service at all.
  • 5 share names with service entries but have different hex values.
  • Only 2 (White, Black) match exactly.
  • The fallback pretends to cover an offline case nobody has asked for.

The frontend should not carry its own thread data. The service is the source of truth.

Where

  • Service endpoint: GET /v1/threads/catalog (already wired via getThreadCatalog() in src/lib/easystitch-client.ts:585-598)
  • Client fallback: src/components/workspace/threadLibrary.ts:18-39 (DEFAULT_THREAD_LIBRARY)
  • Storage: tc_thread_library_v1 (localStorage key in threadLibrary.ts:16)
  • Hook: useThreadLibrary() in src/components/workspace/threadLibrary.ts:161-223
  • Settings modal: src/components/workspace/SettingsModal.tsx

Suggested fix shape

Three tiers of color in the library, merged in useThreadLibrary():

Tier 1: Service threads (read-only, ~61 entries)

  • Fetched once on app mount via getThreadCatalog(). Cached in memory for the session.
  • Each entry tagged source: 'service', readOnly: true.
  • The Settings modal renders these with a "Brother" badge and no delete/edit buttons.
  • The "Reset to defaults" button re-fetches the service catalog.

Tier 2: User threads (read-write, free-form)

  • Stored in localStorage under a new key: tc_thread_user_v1 (version-bump from the old tc_thread_library_v1).
  • Each entry tagged source: 'user', readOnly: false.
  • Settings modal renders these with a "Custom" badge and delete/edit buttons.
  • The existing addColor / removeColor / updateColor / reorderColors API stays; it just writes to the user store now.

Tier 3: Self-source import (bulk paste)

  • A textarea in the Settings modal where the user can paste a list of name, hex pairs (one per line) to add multiple user colors at once.
  • Format: Madeira Aerofil 1100, #FFFFFF or Madeira 1100 #FFFFFF White or just a hex on its own (#FF00FF).
  • Preview the parsed list, show validation errors (invalid hex, duplicate names), and let the user commit.
  • This is the value-add over today: a user with a Madeira spool rack can paste 50 colors in 30 seconds.

Service unreachable

  • If getThreadCatalog() fails, the Settings modal shows: "Cannot reach Brother thread library. Add your own colors below to continue."
  • The app still works for the user's local colors. No silent fallback to fake data.

Data model

type ThreadColor = { id: string; name: string; hex: string };
type LibraryEntry = ThreadColor & {
  source: 'service' | 'user';
  readOnly: boolean;
};

The hook returns LibraryEntry[]. Consumers don't care about the source flag for rendering, only the Settings modal does (for the badge + delete/edit affordances).

Migration

  • Old tc_thread_library_v1 data is the 20-color fictional fallback. Wipe it on first load of the new code; do not import. (Importing would perpetuate the wrong hex values.)
  • Bump the user-store key to tc_thread_user_v1 so the old key is cleanly orphaned.
  • The hook's migration path: on mount, check for the old key; if present, delete it; do not migrate its contents.

Discovered by

Discussion round (2026-10-02) about whether the 20-color client fallback made sense. It doesn't.

  • #120 (closed) — replaced the hardcoded const with the CRUD library. Superseded by this issue for the data model; the CRUD API itself stays.
  • #76 (closed) — Help FAQ promised thread palette management. Shipped, but against fictional data.
  • #94 (closed) — Editor thread bar wiring. Library is now real (post #120), but the data is still fictional (this issue).
## Finding The thread library is built on a 20-color fictional fallback (`DEFAULT_THREAD_LIBRARY` in `src/components/workspace/threadLibrary.ts`) that the orchestrator inherited from a hardcoded `BROTHER_PR10_COLORS` const. The fallback is dead weight: - The service at `GET /v1/threads/catalog` returns **61 real thread colors**. The local 20 doesn't match. - 13 of the 20 fallback entries don't exist in the service at all. - 5 share names with service entries but have different hex values. - Only 2 (White, Black) match exactly. - The fallback pretends to cover an offline case nobody has asked for. The frontend should not carry its own thread data. The service is the source of truth. ## Where - Service endpoint: `GET /v1/threads/catalog` (already wired via `getThreadCatalog()` in `src/lib/easystitch-client.ts:585-598`) - Client fallback: `src/components/workspace/threadLibrary.ts:18-39` (`DEFAULT_THREAD_LIBRARY`) - Storage: `tc_thread_library_v1` (localStorage key in `threadLibrary.ts:16`) - Hook: `useThreadLibrary()` in `src/components/workspace/threadLibrary.ts:161-223` - Settings modal: `src/components/workspace/SettingsModal.tsx` ## Suggested fix shape Three tiers of color in the library, merged in `useThreadLibrary()`: ### Tier 1: Service threads (read-only, ~61 entries) - Fetched once on app mount via `getThreadCatalog()`. Cached in memory for the session. - Each entry tagged `source: 'service'`, `readOnly: true`. - The Settings modal renders these with a "Brother" badge and no delete/edit buttons. - The "Reset to defaults" button re-fetches the service catalog. ### Tier 2: User threads (read-write, free-form) - Stored in localStorage under a new key: `tc_thread_user_v1` (version-bump from the old `tc_thread_library_v1`). - Each entry tagged `source: 'user'`, `readOnly: false`. - Settings modal renders these with a "Custom" badge and delete/edit buttons. - The existing `addColor` / `removeColor` / `updateColor` / `reorderColors` API stays; it just writes to the user store now. ### Tier 3: Self-source import (bulk paste) - A textarea in the Settings modal where the user can paste a list of `name, hex` pairs (one per line) to add multiple user colors at once. - Format: `Madeira Aerofil 1100, #FFFFFF` or `Madeira 1100 #FFFFFF White` or just a hex on its own (`#FF00FF`). - Preview the parsed list, show validation errors (invalid hex, duplicate names), and let the user commit. - This is the value-add over today: a user with a Madeira spool rack can paste 50 colors in 30 seconds. ### Service unreachable - If `getThreadCatalog()` fails, the Settings modal shows: "Cannot reach Brother thread library. Add your own colors below to continue." - The app still works for the user's local colors. No silent fallback to fake data. ## Data model ```ts type ThreadColor = { id: string; name: string; hex: string }; type LibraryEntry = ThreadColor & { source: 'service' | 'user'; readOnly: boolean; }; ``` The hook returns `LibraryEntry[]`. Consumers don't care about the source flag for rendering, only the Settings modal does (for the badge + delete/edit affordances). ## Migration - Old `tc_thread_library_v1` data is the 20-color fictional fallback. Wipe it on first load of the new code; do not import. (Importing would perpetuate the wrong hex values.) - Bump the user-store key to `tc_thread_user_v1` so the old key is cleanly orphaned. - The hook's migration path: on mount, check for the old key; if present, delete it; do not migrate its contents. ## Discovered by Discussion round (2026-10-02) about whether the 20-color client fallback made sense. It doesn't. ## Related - #120 (closed) — replaced the hardcoded const with the CRUD library. Superseded by this issue for the data model; the CRUD API itself stays. - #76 (closed) — Help FAQ promised thread palette management. Shipped, but against fictional data. - #94 (closed) — Editor thread bar wiring. Library is now real (post #120), but the data is still fictional (this issue).
Sign in to join this conversation.
No description provided.