@weasel-js/labkit 1.4.4 → 1.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +13 -2
- package/dist/_dts/{CanvasStackContext-kTEZvEgE.d.ts → CanvasStackContext-CqkxkFJW.d.ts} +1 -1
- package/dist/_dts/ToggleBar.d-DbHoWKIA.d.ts +116 -0
- package/dist/_dts/{frac-z7ker2Vx.d.ts → frac-8R6UmlvN.d.ts} +253 -31
- package/dist/_dts/index-B8uu2ba7.d.ts +237 -0
- package/dist/_dts/{index-Blv9uQzu.d.ts → index-retnAut7.d.ts} +18 -10
- package/dist/_dts/{types-B9_zrHmb.d.ts → types-Ca2LCnPq.d.ts} +65 -2
- package/dist/_dts/usePanZoom-nS798rOF.d.ts +160 -0
- package/dist/_dts/useTrialState-jcx_TdU6.d.ts +212 -0
- package/dist/canvas/index.d.ts +17 -128
- package/dist/canvas/index.js +4 -2
- package/dist/chrome/index.d.ts +4 -92
- package/dist/chrome/index.js +9 -5
- package/dist/chunk-67SJLMC7.js +704 -0
- package/dist/chunk-67SJLMC7.js.map +1 -0
- package/dist/chunk-6ZDGOZQV.js +310 -0
- package/dist/chunk-6ZDGOZQV.js.map +1 -0
- package/dist/{chunk-MOM3GOVY.js → chunk-EKIICY6X.js} +111 -25
- package/dist/chunk-EKIICY6X.js.map +1 -0
- package/dist/{chunk-CTRKTLYZ.js → chunk-H6ZAOWNE.js} +11 -11
- package/dist/chunk-H6ZAOWNE.js.map +1 -0
- package/dist/{chunk-SMHP6XZ4.js → chunk-KJALCDWE.js} +9 -7
- package/dist/chunk-KJALCDWE.js.map +1 -0
- package/dist/{chunk-BKFVHKJH.js → chunk-MUKOW3TC.js} +3 -3
- package/dist/{chunk-BKFVHKJH.js.map → chunk-MUKOW3TC.js.map} +1 -1
- package/dist/chunk-NDRYLVOW.js +15 -0
- package/dist/chunk-NDRYLVOW.js.map +1 -0
- package/dist/{chunk-64ZCN3DA.js → chunk-NS64DXMN.js} +226 -159
- package/dist/chunk-NS64DXMN.js.map +1 -0
- package/dist/{chunk-I6JCVE24.js → chunk-PGETSEDK.js} +135 -13
- package/dist/chunk-PGETSEDK.js.map +1 -0
- package/dist/{chunk-FQMJUVHJ.js → chunk-QLSV2N3G.js} +32 -37
- package/dist/chunk-QLSV2N3G.js.map +1 -0
- package/dist/chunk-RWNDDKOH.js +682 -0
- package/dist/chunk-RWNDDKOH.js.map +1 -0
- package/dist/chunk-S2SHZRD7.js +7681 -0
- package/dist/chunk-S2SHZRD7.js.map +1 -0
- package/dist/{chunk-U3IYHIAE.js → chunk-TQBBYTAF.js} +19 -82
- package/dist/chunk-TQBBYTAF.js.map +1 -0
- package/dist/chunk-UU3NJO6N.js +72 -0
- package/dist/chunk-UU3NJO6N.js.map +1 -0
- package/dist/chunk-WIG6XHJ7.js +35 -0
- package/dist/chunk-WIG6XHJ7.js.map +1 -0
- package/dist/{chunk-TJ7QY3OC.js → chunk-ZPH5WTWF.js} +21 -15
- package/dist/chunk-ZPH5WTWF.js.map +1 -0
- package/dist/config/index.d.ts +232 -0
- package/dist/config/index.js +6 -0
- package/dist/config/index.js.map +1 -0
- package/dist/controls/index.d.ts +2 -3
- package/dist/controls/index.js +3 -2
- package/dist/dragdrop/index.d.ts +2 -4
- package/dist/index.d.ts +110 -381
- package/dist/index.js +691 -817
- package/dist/index.js.map +1 -1
- package/dist/layers/index.d.ts +3 -5
- package/dist/layers/index.js +2 -2
- package/dist/loupe/index.d.ts +6 -8
- package/dist/loupe/index.js +2 -2
- package/dist/passthrough/weasel-ui.d.ts +164 -122
- package/dist/passthrough/weasel-ui.js +1 -1
- package/dist/primitives/index.d.ts +53 -6
- package/dist/primitives/index.js +6 -4
- package/dist/state/index.d.ts +12 -7
- package/dist/state/index.js +16 -13
- package/dist/state/index.js.map +1 -1
- package/dist/styles.css +84 -50
- package/dist/surface/index.d.ts +44 -7
- package/dist/surface/index.js +2 -1
- package/dist/ui/layers/index.js +1 -1
- package/dist/undo/index.d.ts +2 -4
- package/package.json +15 -8
- package/src/annotations/AnnotationOverlay.tsx +53 -5
- package/src/annotations/Annotations.overlay.test.tsx +85 -6
- package/src/annotations/drawOne.test.ts +1 -1
- package/src/annotations/drawOne.ts +2 -2
- package/src/annotations/paint.test.ts +2 -2
- package/src/annotations/paint.ts +1 -1
- package/src/annotations/preload.ts +6 -0
- package/src/annotations/store.test.ts +18 -0
- package/src/annotations/store.ts +5 -4
- package/src/annotations/svgNodes.test.ts +2 -2
- package/src/annotations/toolMap.ts +3 -3
- package/src/annotations/types.ts +6 -3
- package/src/canvas/CameraWheelContext.ts +12 -0
- package/src/canvas/CanvasStack.test.tsx +25 -0
- package/src/canvas/CanvasStack.tsx +31 -4
- package/src/canvas/Stage.less +18 -0
- package/src/canvas/Stage.test.tsx +186 -0
- package/src/canvas/Stage.tsx +134 -0
- package/src/canvas/camera.test.ts +17 -2
- package/src/canvas/camera.ts +9 -9
- package/src/canvas/index.ts +4 -0
- package/src/canvas/usePanZoom.ts +10 -5
- package/src/chrome/ChromeRegions.stories.tsx +0 -3
- package/src/chrome/LabChrome.tsx +114 -0
- package/src/chrome/builtins.tsx +20 -31
- package/src/chrome/index.ts +15 -0
- package/src/chrome/labRegions.test.tsx +208 -0
- package/src/chrome/labTypes.ts +50 -0
- package/src/chrome/merge.ts +7 -9
- package/src/chrome/regions/PaletteRegion.test.tsx +6 -4
- package/src/chrome/regions/PaletteRegion.tsx +21 -9
- package/src/chrome/regions/SidebarRegion.tsx +25 -13
- package/src/chrome/regions/StatusRegion.tsx +15 -8
- package/src/chrome/regions/ToolbarRegion.tsx +30 -16
- package/src/chrome/types.ts +47 -20
- package/src/config/builder.test.ts +4 -0
- package/src/config/builder.ts +16 -10
- package/src/config/declarationEmit.fixture.ts +11 -0
- package/src/config/declarationEmit.test.ts +51 -0
- package/src/config/entry.test.ts +46 -0
- package/src/config/index.ts +13 -1
- package/src/config/nodeClasses.test.ts +18 -0
- package/src/config/types.ts +3 -1
- package/src/controls/ControlPanel.test.tsx +16 -0
- package/src/controls/ControlPanel.tsx +5 -2
- package/src/fake-indexeddb-auto.d.ts +5 -0
- package/src/index.test.ts +7 -0
- package/src/index.ts +38 -5
- package/src/instrument/serializers.test.ts +22 -0
- package/src/instrument/serializers.ts +11 -0
- package/src/instrument/types.ts +25 -1
- package/src/lab/Lab.less +4 -13
- package/src/lab/Lab.persist.test.tsx +147 -0
- package/src/lab/Lab.stories.tsx +0 -2
- package/src/lab/Lab.surface.test.tsx +66 -3
- package/src/lab/Lab.test.tsx +91 -17
- package/src/lab/Lab.tsx +337 -124
- package/src/lab/LabFit.stories.less +18 -0
- package/src/lab/LabFit.stories.tsx +219 -0
- package/src/lab/LabFullChrome.stories.tsx +0 -3
- package/src/lab/LabHeader.test.tsx +40 -9
- package/src/lab/LabHeader.tsx +7 -13
- package/src/lab/LabPalette.tsx +13 -26
- package/src/lab/LabShell.less +49 -12
- package/src/lab/LabSwitcher.less +8 -1
- package/src/lab/Workspace.surface.test.tsx +2 -1
- package/src/lab/fitCheck.test.ts +57 -0
- package/src/lab/fitCheck.ts +125 -0
- package/src/loupe/AGENTS.md +1 -1
- package/src/loupe/Loupe.less +1 -1
- package/src/loupe/canvasLens.ts +1 -1
- package/src/loupe/types.ts +1 -1
- package/src/loupe/useLoupe.test.tsx +1 -1
- package/src/loupe/useLoupe.ts +1 -1
- package/src/passthrough/weasel-ui.ts +9 -0
- package/src/primitives/FloatingPanel.stories.tsx +13 -1
- package/src/primitives/FloatingPanel.test.tsx +47 -16
- package/src/primitives/FloatingPanel.tsx +11 -25
- package/src/primitives/Split.less +14 -0
- package/src/primitives/Split.test.tsx +108 -0
- package/src/primitives/Split.tsx +159 -0
- package/src/primitives/ZoomControl.tsx +5 -2
- package/src/primitives/index.ts +2 -0
- package/src/state/Persistence.stories.tsx +75 -0
- package/src/state/Persistence.tsx +33 -0
- package/src/state/SingletonExperiment.test.tsx +47 -53
- package/src/state/SingletonExperiment.tsx +24 -13
- package/src/state/adapterContract.ts +116 -0
- package/src/state/adapters.test.ts +113 -61
- package/src/state/adapters.ts +328 -69
- package/src/state/document.test.ts +71 -96
- package/src/state/document.ts +66 -52
- package/src/state/helpers.test.ts +1 -1
- package/src/state/index.ts +8 -0
- package/src/state/labRecords.test.ts +105 -0
- package/src/state/labRecords.ts +160 -0
- package/src/state/openLabStore.test.ts +658 -0
- package/src/state/openLabStore.ts +403 -0
- package/src/state/records.test.ts +234 -0
- package/src/state/records.ts +219 -0
- package/src/state/store.test.ts +104 -635
- package/src/state/store.ts +116 -157
- package/src/state/toolSlot.test.ts +1 -1
- package/src/state/types.ts +39 -15
- package/src/state/useOpenOnce.ts +68 -0
- package/src/state/usePersistedState.test.tsx +125 -0
- package/src/state/usePersistedState.ts +66 -0
- package/src/state/useTrialState.test.tsx +2 -3
- package/src/state/view.test.ts +49 -15
- package/src/state/view.ts +18 -1
- package/src/styles.less +2 -0
- package/src/surface/AGENTS.md +39 -7
- package/src/surface/SurfaceContext.ts +26 -3
- package/src/surface/index.ts +2 -0
- package/src/surface/useSurfaceTile.test.tsx +2 -1
- package/src/surface/useSurfaceTile.ts +6 -5
- package/src/surface/useTiledSurface.test.tsx +175 -0
- package/src/surface/useTiledSurface.ts +54 -5
- package/src/theme/Interstellar.stories.tsx +7 -7
- package/src/theme/interstellar.test.ts +1 -1
- package/src/theme/interstellar.tokens.json +2 -2
- package/src/tools/labTool.ts +11 -0
- package/src/trial/Trial.annotations.persist.test.tsx +100 -58
- package/src/trial/Trial.annotations.test.tsx +47 -4
- package/src/trial/Trial.job.test.tsx +3 -3
- package/src/trial/Trial.less +1 -26
- package/src/trial/Trial.stories.tsx +1 -2
- package/src/trial/Trial.test.tsx +28 -4
- package/src/trial/Trial.trialId.test.tsx +23 -1
- package/src/trial/Trial.tsx +118 -38
- package/src/trial/TrialBody.tsx +17 -141
- package/src/trial/TrialChrome.tsx +7 -4
- package/src/trial/trialOps.ts +5 -3
- package/dist/_dts/types-BP2OCcpg.d.ts +0 -155
- package/dist/_dts/types-x92Kfeme.d.ts +0 -62
- package/dist/_dts/useTrialState-D6Vb3T-g.d.ts +0 -99
- package/dist/chunk-2P6PP5N4.js +0 -553
- package/dist/chunk-2P6PP5N4.js.map +0 -1
- package/dist/chunk-64ZCN3DA.js.map +0 -1
- package/dist/chunk-CTRKTLYZ.js.map +0 -1
- package/dist/chunk-FQMJUVHJ.js.map +0 -1
- package/dist/chunk-I6JCVE24.js.map +0 -1
- package/dist/chunk-MOM3GOVY.js.map +0 -1
- package/dist/chunk-SMHP6XZ4.js.map +0 -1
- package/dist/chunk-TJ7QY3OC.js.map +0 -1
- package/dist/chunk-U3IYHIAE.js.map +0 -1
- package/dist/chunk-W3ECWC2K.js +0 -7335
- package/dist/chunk-W3ECWC2K.js.map +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/state/document.ts","../src/state/undock.ts","../src/instrument/serializers.ts","../src/state/store.ts","../src/state/openLabStore.ts","../src/state/useTrialState.ts"],"names":[],"mappings":"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","file":"chunk-67SJLMC7.js","sourcesContent":["import { labStorageKey } from './helpers';\nimport type {\n LabDocument,\n LabMode,\n Migration,\n SavedSnapshot,\n SerializedTrial,\n StorageAdapter,\n} from './types';\nimport type { UndockedPanels } from './undock';\n\n/** Bumped whenever the persisted shape changes; every bump needs a migration. */\nexport const CURRENT_DOCUMENT_VERSION = 4;\n\n/** Where a version-3 lab kept its one document. Read once, to fold it into\n * records, then deleted. */\nexport function labDocumentKey(storageKey: string): string {\n return `lk:${storageKey}:doc`;\n}\n\n/** Where a document that failed to migrate is set aside, so a bad migration\n * loses state loudly rather than silently. */\nexport function quarantineKey(storageKey: string): string {\n return `lk:${storageKey}:quarantine`;\n}\n\n/** A fresh document at the current version. */\nexport function emptyDocument(mode: LabMode): LabDocument {\n return {\n version: CURRENT_DOCUMENT_VERSION,\n undockedPanels: {},\n trials: [],\n saves: [],\n layout: {},\n mode,\n };\n}\n\n/** What a migration run produced: the migrated document, or why it refused. */\nexport type MigrationOutcome =\n | { ok: true; doc: Record<string, unknown>; migrated: boolean }\n | { ok: false; reason: 'future' | 'failed'; error?: unknown };\n\n/** Walk a document forward to `target`, one migration at a time. A version\n * above `target` is refused rather than parsed under a shape it may not\n * have. */\nexport function runMigrations(\n raw: Record<string, unknown>,\n migrations: Migration[],\n target: number,\n): MigrationOutcome {\n const from = typeof raw.version === 'number' ? raw.version : 0;\n if (from > target) return { ok: false, reason: 'future' };\n if (from === target) return { ok: true, doc: raw, migrated: false };\n\n let doc = raw;\n try {\n for (let v = from; v < target; v++) {\n const migration = migrations[v];\n if (!migration) throw new Error(`[labkit] no migration from version ${v}`);\n doc = migration(doc);\n }\n } catch (error) {\n return { ok: false, reason: 'failed', error };\n }\n return { ok: true, doc, migrated: true };\n}\n\nconst LEGACY_BUCKETS = ['workspaces', 'saves', 'layout', 'theme'] as const;\n\n/** The four keys a lab used before it was one document. */\nexport function legacyKeys(storageKey: string): string[] {\n return LEGACY_BUCKETS.map((bucket) => labStorageKey(storageKey, bucket));\n}\n\n/** A value some substrate returned for an old key: already parsed, or a JSON\n * string from a store that does not parse. `undefined` when it is neither. */\nexport function parseStored(value: unknown): unknown {\n if (typeof value !== 'string') return value;\n try {\n return JSON.parse(value);\n } catch {\n return undefined;\n }\n}\n\nfunction bucketOr<T>(value: unknown, fallback: T, bucket: string, shape: 'array' | 'object'): T {\n if (value === undefined) return fallback;\n const parsed = parseStored(value);\n const fits = shape === 'array' ? Array.isArray(parsed) : !!parsed && typeof parsed === 'object';\n if (fits) return parsed as T;\n console.warn(`[labkit] unparseable legacy bucket \"${bucket}\", dropping it`);\n return fallback;\n}\n\n/** Assemble a version-0 document out of the four pre-document keys, or null\n * if none of them is present. `fallbackMode` is used when the theme key is\n * absent or holds something unrecognized; `interstellar` passes through\n * untouched for `migrateV0toV1` to coerce. */\nexport async function readLegacyDocument(\n read: (key: string) => Promise<unknown>,\n storageKey: string,\n fallbackMode: LabMode,\n): Promise<Record<string, unknown> | null> {\n const [workspaces, saves, layout, theme] = await Promise.all(\n LEGACY_BUCKETS.map((bucket) => read(labStorageKey(storageKey, bucket))),\n );\n if ([workspaces, saves, layout, theme].every((v) => v === undefined)) return null;\n\n const mode =\n theme === 'light' || theme === 'dark' || theme === 'auto' || theme === 'interstellar'\n ? theme\n : fallbackMode;\n\n return {\n version: 0,\n workspaces: bucketOr(workspaces, [], 'workspaces', 'array'),\n saves: bucketOr(saves, [], 'saves', 'array'),\n layout: bucketOr(layout, {}, 'layout', 'object'),\n mode,\n };\n}\n\n/** Whether two stored values hold the same data. Stored values are\n * structured-clone copies, so identity says nothing. */\nexport function sameStored(a: unknown, b: unknown): boolean {\n return JSON.stringify(a) === JSON.stringify(b);\n}\n\n/** Delete `keys` and read each back, so an adapter whose delete silently did\n * nothing is caught. `true` only once all of them are gone. */\nexport async function deleteConfirmed(storage: StorageAdapter, keys: string[]): Promise<boolean> {\n try {\n await Promise.all(keys.map((key) => storage.delete(key)));\n const left = await Promise.all(keys.map((key) => storage.get(key)));\n return left.every((v) => v === undefined);\n } catch {\n return false;\n }\n}\n\n/** Set `value` aside under the quarantine key, reading it back to confirm it\n * landed. Returns `false` when it did not — a full disk is exactly when a\n * document goes unreadable — so the caller can leave the original alone\n * rather than overwrite the only copy of it. */\nexport async function quarantineDocument(\n storage: StorageAdapter,\n storageKey: string,\n value: unknown,\n): Promise<boolean> {\n try {\n await storage.set(quarantineKey(storageKey), value);\n return sameStored(await storage.get(quarantineKey(storageKey)), value);\n } catch {\n return false;\n }\n}\n\n/** Fill in whatever a document is missing or holds the wrong shape of, so a\n * hydrated store never sees `undefined` where a section should be. Applied to\n * every document, migrated or already current. */\nexport function normalizeDocument(\n doc: Record<string, unknown>,\n fallbackMode: LabMode,\n): LabDocument {\n const mode =\n doc.mode === 'light' || doc.mode === 'dark' || doc.mode === 'auto' ? doc.mode : fallbackMode;\n return {\n version: CURRENT_DOCUMENT_VERSION,\n trials: Array.isArray(doc.trials) ? (doc.trials as SerializedTrial[]) : [],\n saves: Array.isArray(doc.saves) ? (doc.saves as SavedSnapshot[]) : [],\n layout:\n doc.layout && typeof doc.layout === 'object' ? (doc.layout as Record<string, unknown>) : {},\n undockedPanels:\n doc.undockedPanels && typeof doc.undockedPanels === 'object'\n ? (doc.undockedPanels as UndockedPanels)\n : {},\n mode,\n };\n}\n\n/** Version 0 (four loose keys) to version 1 (one document). Normalizes the\n * mode, including `interstellar` — the dark mode's name back when it was a\n * theme. Coerces against version 1's own field names rather than reusing\n * `normalizeDocument`, which produces the current shape and would drop\n * `workspaces` from a document it labels version 1. */\nexport function migrateV0toV1(doc: Record<string, unknown>): Record<string, unknown> {\n const raw = doc.mode === 'interstellar' ? 'dark' : doc.mode;\n const mode = raw === 'light' || raw === 'dark' || raw === 'auto' ? raw : 'auto';\n return {\n version: 1,\n workspaces: Array.isArray(doc.workspaces) ? doc.workspaces : [],\n saves: Array.isArray(doc.saves) ? doc.saves : [],\n layout: doc.layout && typeof doc.layout === 'object' ? doc.layout : {},\n mode,\n };\n}\n\n/** Version 1 to version 2: the vocabulary refresh renamed a tile from\n * workspace to trial. Only the records field moves; a record's own fields are\n * unchanged. */\nexport function migrateV1toV2(doc: Record<string, unknown>): Record<string, unknown> {\n const { workspaces, ...rest } = doc;\n return { ...rest, trials: Array.isArray(workspaces) ? workspaces : [], version: 2 };\n}\n\n/** Version 2 to version 3: undocked sidebar panels became persisted state.\n * Nothing existing moves; the field simply starts empty. */\nexport function migrateV2toV3(doc: Record<string, unknown>): Record<string, unknown> {\n return { ...doc, undockedPanels: {}, version: 3 };\n}\n\n/** Version 3 to version 4: a lab is stored as records under one prefix\n * instead of one document. The joined shape does not change. */\nexport function migrateV3toV4(doc: Record<string, unknown>): Record<string, unknown> {\n return { ...doc, version: 4 };\n}\n\n/** Index `i` migrates a version-`i` document to version `i + 1`. */\nexport const MIGRATIONS: Migration[] = [migrateV0toV1, migrateV1toV2, migrateV2toV3, migrateV3toV4];\n","/** A sidebar section torn out of its trial, and where it went. */\nexport interface UndockedPanel {\n trialId: string;\n sectionId: string;\n as: 'tile' | 'floating';\n}\n\n/** Undocked panels, keyed by {@link panelKey}. */\nexport type UndockedPanels = Record<string, UndockedPanel>;\n\nexport function panelKey(trialId: string, sectionId: string): string {\n return `${trialId}::${sectionId}`;\n}\n\nexport function undockPanel(\n panels: UndockedPanels,\n trialId: string,\n sectionId: string,\n as: UndockedPanel['as'] = 'tile',\n): UndockedPanels {\n return { ...panels, [panelKey(trialId, sectionId)]: { trialId, sectionId, as } };\n}\n\n/** Dock one section back, or — with no `sectionId` — every panel the trial\n * owns, which is what closing a trial wants. */\nexport function dockPanel(\n panels: UndockedPanels,\n trialId: string,\n sectionId?: string,\n): UndockedPanels {\n const out: UndockedPanels = {};\n for (const [key, panel] of Object.entries(panels)) {\n if (panel.trialId === trialId && (sectionId === undefined || panel.sectionId === sectionId)) {\n continue;\n }\n out[key] = panel;\n }\n return out;\n}\n","import type { InstrumentSerializers } from '../state/types';\nimport type { InstrumentList } from './types';\n\n/**\n * Each instrument's default config, keyed by name — what `createLabStore`\n * fills a stored config's gaps from, so one saved before its schema grew a\n * branch still loads.\n */\nexport function configDefaultsOf(instruments: InstrumentList): Record<string, () => unknown> {\n const out: Record<string, () => unknown> = {};\n for (const instrument of instruments) out[instrument.name] = () => instrument.defaultConfig();\n return out;\n}\n\n/** Each instrument's `migrateConfig`, keyed by name, for those that declare one. */\nexport function configMigrationsOf(\n instruments: InstrumentList,\n): Record<string, (stored: unknown) => unknown> {\n const out: Record<string, (stored: unknown) => unknown> = {};\n for (const { name, migrateConfig } of instruments) {\n if (migrateConfig) out[name] = (stored) => migrateConfig(stored);\n }\n return out;\n}\n\n/**\n * The `serialize` / `deserialize` an instrument list declares, keyed by\n * instrument name — what `createLabStore` needs to rebuild a trial's state\n * from storage.\n *\n * An instrument declaring neither gets no entry, which is what leaves a\n * JSON-safe state to pass through untouched.\n */\nexport function serializersOf(instruments: InstrumentList): InstrumentSerializers {\n const out: InstrumentSerializers = {};\n for (const instrument of instruments) {\n const { name, serialize, deserialize } = instrument;\n if (!serialize && !deserialize) continue;\n out[name] = {\n ...(serialize ? { serialize: (state: unknown) => serialize(state) } : {}),\n ...(deserialize\n ? { deserialize: (data: unknown, config: unknown) => deserialize(data, config) }\n : {}),\n };\n }\n return out;\n}\n","import { createStore, type StoreApi } from 'zustand/vanilla';\nimport { fillConfigDefaults, withValueAtPath } from '../config/path';\nimport { configDefaultsOf, configMigrationsOf, serializersOf } from '../instrument/serializers';\nimport type { InstrumentList } from '../instrument/types';\nimport { emptyDocument } from './document';\nimport { deserializeTrials, emptyUndoStack, newId } from './helpers';\nimport type {\n CreateLabStoreOptions,\n InstrumentHooks,\n InstrumentSerializers,\n LabMode,\n LabStoreState,\n SavedSnapshot,\n SerializedTrial,\n TrialRecord,\n} from './types';\nimport {\n dockPanel as dockPanelIn,\n type UndockedPanel,\n undockPanel as undockPanelIn,\n} from './undock';\n\n/** Every mutation a lab store supports: managing trials, saving and\n * restoring snapshots, and setting the color mode. */\nexport interface LabStoreActions {\n addTrial: (record: Omit<TrialRecord, 'undoStack'>) => void;\n removeTrial: (id: string) => void;\n updateTrialState: <TS>(id: string, next: TS | ((prev: TS) => TS)) => void;\n /** Write one config value. `path` is dotted for a value nested under an\n * `f.group`, and the bare key for one at the root. */\n updateTrialConfig: (id: string, path: string, value: unknown) => void;\n updateTrialView: (id: string, view: unknown) => void;\n updateTrialSidebarWidth: (id: string, width: number) => void;\n /** Retitle a trial. `null` returns it to its instrument's name. */\n setTrialTitle: (id: string, title: string | null) => void;\n /** Fold or unfold one of a trial's sections. */\n setTrialSectionCollapsed: (id: string, key: string, collapsed: boolean) => void;\n updateTrialAnnotations: (id: string, doc: unknown) => void;\n updateTrialUndoStack: (\n id: string,\n next: TrialRecord['undoStack'] | ((prev: TrialRecord['undoStack']) => TrialRecord['undoStack']),\n ) => void;\n setTrialInstrument: (id: string, instrumentName: string) => void;\n saveSnapshot: (trialId: string, name: string) => void;\n loadSnapshot: (snapshotId: string, trialId: string) => void;\n deleteSnapshot: (snapshotId: string) => void;\n listSnapshots: (trialId?: string) => SavedSnapshot[];\n setMode: (mode: LabMode) => void;\n setLabTool: (id: string | null) => void;\n setTrialTool: (trialId: string, id: string | null) => void;\n setLayout: (layout: Record<string, unknown>) => void;\n /** Tear a sidebar section out of its trial. Default target is a workspace tile. */\n undockPanel: (trialId: string, sectionId: string, as?: UndockedPanel['as']) => void;\n /** Put one section back, or — with no `sectionId` — every panel the trial owns. */\n dockPanel: (trialId: string, sectionId?: string) => void;\n /** Replace the lab's instruments. Trials and saves of an instrument that is\n * new or a different object are brought up to date against it in the same\n * write; every other trial keeps its object. */\n setInstruments: (instruments: InstrumentList) => void;\n /** The per-instrument hooks in force now. Read them at use; never keep them. */\n instrumentHooks: () => InstrumentHooks;\n}\n\n/** A lab's store: its state and its actions. How each instrument's state is\n * serialized comes in through `CreateLabStoreOptions.instruments`. */\nexport type LabStore = StoreApi<LabStoreState & LabStoreActions>;\n\n/** Build a lab store holding `initial`, or an empty lab. It knows nothing\n * about storage; `openLabStore` reads a stored lab and binds one of these to\n * it. */\nexport function createLabStore(options: CreateLabStoreOptions = {}): LabStore {\n let hooks: InstrumentHooks = options.instruments\n ? hooksOf(options.instruments)\n : {\n serializers: options.serializers ?? {},\n configDefaults: options.configDefaults ?? {},\n configMigrations: options.configMigrations ?? {},\n };\n const initial = options.initial ?? emptyDocument(options.initialMode ?? 'auto');\n\n const store = createStore<LabStoreState & LabStoreActions>()((set, get) => ({\n trials: hydrateTrials(initial.trials, hooks.serializers, hooks),\n savedSnapshots: hydrateSnapshots(initial.saves, hooks),\n mode: initial.mode,\n layout: initial.layout,\n undockedPanels: initial.undockedPanels,\n activeToolId: null,\n instruments: options.instruments ?? null,\n\n addTrial: (record) => {\n set((s) => ({\n trials: [...s.trials, { ...record, undoStack: emptyUndoStack() }],\n }));\n },\n\n removeTrial: (id) => {\n set((s) => ({\n trials: s.trials.filter((w) => w.id !== id),\n undockedPanels: dockPanelIn(s.undockedPanels, id),\n }));\n },\n\n updateTrialState: (id, next) => {\n set((s) => ({\n trials: s.trials.map((w) => {\n if (w.id !== id) return w;\n const nextState =\n typeof next === 'function' ? (next as (prev: unknown) => unknown)(w.state) : next;\n // An updater that returns its input means \"nothing changed\", and must not\n // cost a new record: the trial re-renders on record identity, so\n // allocating here turns the standard React bail-out into a render loop.\n if (Object.is(nextState, w.state)) return w;\n return { ...w, state: nextState };\n }),\n }));\n },\n\n updateTrialConfig: (id, path, value) => {\n set((s) => ({\n trials: s.trials.map((w) =>\n w.id === id ? { ...w, config: withValueAtPath(w.config, path, value) } : w,\n ),\n }));\n },\n\n updateTrialView: (id, view) => {\n set((s) => ({\n trials: s.trials.map((w) => (w.id === id && !Object.is(view, w.view) ? { ...w, view } : w)),\n }));\n },\n\n updateTrialSidebarWidth: (id, width) => {\n set((s) => ({\n trials: s.trials.map((w) =>\n w.id === id && w.sidebarWidth !== width ? { ...w, sidebarWidth: width } : w,\n ),\n }));\n },\n\n setTrialTitle: (id, title) => {\n set((s) => ({\n trials: s.trials.map((w) => {\n if (w.id !== id) return w;\n const next = title === null ? undefined : title;\n if ((w.title ?? undefined) === next) return w;\n return { ...w, title: next };\n }),\n }));\n },\n\n setTrialSectionCollapsed: (id, key, collapsed) => {\n set((s) => ({\n trials: s.trials.map((w) => {\n if (w.id !== id) return w;\n if (w.collapsedSections?.[key] === collapsed) return w;\n return { ...w, collapsedSections: { ...w.collapsedSections, [key]: collapsed } };\n }),\n }));\n },\n\n updateTrialAnnotations: (id, doc) => {\n set((s) => ({\n trials: s.trials.map((w) =>\n w.id === id && !Object.is(doc, w.annotations) ? { ...w, annotations: doc } : w,\n ),\n }));\n },\n\n updateTrialUndoStack: (id, next) => {\n set((s) => ({\n trials: s.trials.map((w) => {\n if (w.id !== id) return w;\n const undoStack =\n typeof next === 'function'\n ? (next as (prev: TrialRecord['undoStack']) => TrialRecord['undoStack'])(w.undoStack)\n : next;\n return { ...w, undoStack };\n }),\n }));\n },\n\n setTrialInstrument: (id, instrumentName) => {\n set((s) => ({\n trials: s.trials.map((w) => (w.id === id ? { ...w, instrumentName } : w)),\n }));\n },\n\n saveSnapshot: (trialId, name) => {\n const trial = get().trials.find((w) => w.id === trialId);\n if (!trial) return;\n const reg = hooks.serializers[trial.instrumentName];\n const serializedState = reg?.serialize\n ? reg.serialize(trial.state)\n : structuredClone(trial.state);\n const clonedConfig = structuredClone(trial.config);\n const lastAt = get().savedSnapshots.reduce((m, sn) => (sn.savedAt > m ? sn.savedAt : m), 0);\n const savedAt = Math.max(Date.now(), lastAt + 1);\n const snapshot: SavedSnapshot = {\n id: newId(),\n name,\n trialId,\n instrumentName: trial.instrumentName,\n config: clonedConfig,\n state: serializedState,\n savedAt,\n };\n set((s) => ({ savedSnapshots: [...s.savedSnapshots, snapshot] }));\n },\n\n loadSnapshot: (snapshotId, trialId) => {\n const snapshot = get().savedSnapshots.find((sn) => sn.id === snapshotId);\n if (!snapshot) return;\n const trial = get().trials.find((w) => w.id === trialId);\n if (!trial) return;\n if (snapshot.instrumentName !== trial.instrumentName) {\n console.warn(\n `[labkit] loadSnapshot: instrument mismatch (snapshot=${snapshot.instrumentName}, trial=${trial.instrumentName}); refusing to load`,\n );\n return;\n }\n const reg = hooks.serializers[snapshot.instrumentName];\n const restoredState = reg?.deserialize\n ? reg.deserialize(snapshot.state, snapshot.config)\n : snapshot.state;\n set((s) => ({\n trials: s.trials.map((w) =>\n w.id === trialId ? { ...w, state: restoredState, config: snapshot.config } : w,\n ),\n }));\n },\n\n deleteSnapshot: (snapshotId) => {\n set((s) => ({\n savedSnapshots: s.savedSnapshots.filter((sn) => sn.id !== snapshotId),\n }));\n },\n\n listSnapshots: (trialId) => {\n const all = get().savedSnapshots;\n const filtered = trialId ? all.filter((sn) => sn.trialId === trialId) : all;\n return [...filtered].sort((a, b) => b.savedAt - a.savedAt);\n },\n\n setMode: (mode) => {\n set({ mode });\n },\n\n setLabTool: (id) => set({ activeToolId: id }),\n\n setTrialTool: (trialId, id) =>\n set((s) => ({\n trials: s.trials.map((t) => (t.id === trialId ? { ...t, activeToolId: id } : t)),\n })),\n\n undockPanel: (trialId, sectionId, as) => {\n set((s) => ({ undockedPanels: undockPanelIn(s.undockedPanels, trialId, sectionId, as) }));\n },\n\n dockPanel: (trialId, sectionId) => {\n set((s) => ({ undockedPanels: dockPanelIn(s.undockedPanels, trialId, sectionId) }));\n },\n\n setLayout: (layout) => {\n set({ layout });\n },\n\n setInstruments: (instruments) => {\n const before = get().instruments;\n if (before === instruments) return;\n const next = mergedHooks(hooks, instruments);\n const previous = new Map((before ?? []).map((i) => [i.name, i]));\n const changed = new Set(\n instruments.filter((i) => previous.get(i.name) !== i).map((i) => i.name),\n );\n if (changed.size === 0) {\n hooks = next;\n set({ instruments });\n return;\n }\n const s = get();\n const trials = s.trials.map((w) =>\n changed.has(w.instrumentName)\n ? { ...w, config: hydratedConfig(next, w.instrumentName, w.config) }\n : w,\n );\n const savedSnapshots = s.savedSnapshots.map((sn) =>\n changed.has(sn.instrumentName)\n ? { ...sn, config: hydratedConfig(next, sn.instrumentName, sn.config) }\n : sn,\n );\n hooks = next;\n set({ instruments, trials, savedSnapshots });\n },\n\n instrumentHooks: () => hooks,\n }));\n\n return store;\n}\n\nfunction hooksOf(instruments: InstrumentList): InstrumentHooks {\n return {\n serializers: serializersOf(instruments),\n configDefaults: configDefaultsOf(instruments),\n configMigrations: configMigrationsOf(instruments),\n };\n}\n\n/** Hooks for `instruments`, keeping those of any instrument no longer listed:\n * its trials stay open, and their state still has to round-trip. A listed\n * name takes its hooks wholly from the new list. */\nfunction mergedHooks(old: InstrumentHooks, instruments: InstrumentList): InstrumentHooks {\n const listed = new Set(instruments.map((i) => i.name));\n const fresh = hooksOf(instruments);\n const unlisted = <T>(entries: Record<string, T>): Record<string, T> =>\n Object.fromEntries(Object.entries(entries).filter(([name]) => !listed.has(name)));\n return {\n serializers: { ...unlisted(old.serializers), ...fresh.serializers },\n configDefaults: { ...unlisted(old.configDefaults), ...fresh.configDefaults },\n configMigrations: { ...unlisted(old.configMigrations), ...fresh.configMigrations },\n };\n}\n\n/** How a stored config is brought up to date on the way in. */\nexport type ConfigHydration = Pick<CreateLabStoreOptions, 'configDefaults' | 'configMigrations'>;\n\nfunction hydratedConfig(\n hydration: ConfigHydration,\n instrumentName: string,\n config: unknown,\n): unknown {\n const migrate = hydration.configMigrations?.[instrumentName];\n const moved = migrate ? migrate(config) : config;\n const defaults = hydration.configDefaults?.[instrumentName];\n return defaults ? fillConfigDefaults(moved, defaults()) : moved;\n}\n\n/** Rebuild stored trials: migrate each config, fill its gaps from its\n * instrument's defaults, then run the deserializer against the result. */\nexport function hydrateTrials(\n trials: SerializedTrial[],\n serializers: InstrumentSerializers,\n hydration: ConfigHydration = {},\n): TrialRecord[] {\n return deserializeTrials(\n trials.map((t) => ({ ...t, config: hydratedConfig(hydration, t.instrumentName, t.config) })),\n serializers,\n );\n}\n\n/** Bring each stored snapshot's config up to date the way `hydrateTrials` does a trial's. */\nexport function hydrateSnapshots(\n saves: SavedSnapshot[],\n hydration: ConfigHydration = {},\n): SavedSnapshot[] {\n return saves.map((sn) => ({\n ...sn,\n config: hydratedConfig(hydration, sn.instrumentName, sn.config),\n }));\n}\n","import {\n CURRENT_DOCUMENT_VERSION,\n deleteConfirmed,\n emptyDocument,\n labDocumentKey,\n legacyKeys,\n MIGRATIONS,\n normalizeDocument,\n parseStored,\n quarantineDocument,\n readLegacyDocument,\n runMigrations,\n sameStored,\n} from './document';\nimport { serializeTrials } from './helpers';\nimport {\n compareTrialOrder,\n documentOfRecords,\n LAYOUT_RECORD,\n type LabMeta,\n labPrefix,\n META_RECORD,\n parseRecordName,\n recordsOfDocument,\n saveRecord,\n trialRecord,\n trialValuesPrefix,\n UNDOCK_RECORD,\n} from './labRecords';\nimport { type OwnedRecordCache, openRecords, type RecordCache } from './records';\nimport { createLabStore, hydrateSnapshots, hydrateTrials, type LabStore } from './store';\nimport type {\n CreateLabStoreOptions,\n LabDocument,\n LabMode,\n SavedSnapshot,\n SerializedTrial,\n StorageAdapter,\n TrialRecord,\n} from './types';\n\n/** Options for `openLabStore`. `storageKey` names the lab, so two labs on one\n * origin keep separate records. */\nexport interface OpenLabStoreOptions extends Omit<CreateLabStoreOptions, 'initial'> {\n storageKey: string;\n storage: StorageAdapter;\n}\n\n/** A lab store bound to its records. */\nexport interface OpenedLabStore {\n store: LabStore;\n /** Every record of the lab, `usePersistedState` values included. */\n records: RecordCache;\n /** Send queued writes and stop hearing other writers. */\n close(): Promise<void>;\n}\n\n/**\n * Read a stored lab and return a store bound to it: every change to the store\n * is written as the records it touched, and another writer's records are\n * applied back into the store. A lab stored before records existed is folded\n * forward on the way in.\n */\nexport async function openLabStore(options: OpenLabStoreOptions): Promise<OpenedLabStore> {\n const records = await openRecords({\n storage: options.storage,\n prefix: labPrefix(options.storageKey),\n });\n const { doc, orders } = await readLab(options, records);\n const store = createLabStore({ ...options, initial: doc });\n const unbind = bindLabStore(store, records, { orders });\n return {\n store,\n records,\n close: async () => {\n unbind();\n await records.close();\n },\n };\n}\n\ninterface ReadLab {\n doc: LabDocument;\n orders: Map<string, number>;\n}\n\nfunction emptyLab(mode: LabMode): ReadLab {\n return { doc: emptyDocument(mode), orders: new Map() };\n}\n\nfunction ordersOf(doc: LabDocument): Map<string, number> {\n return new Map(doc.trials.map((t, i) => [t.id, i]));\n}\n\nasync function readLab(options: OpenLabStoreOptions, records: OwnedRecordCache): Promise<ReadLab> {\n const mode = options.initialMode ?? 'auto';\n if (!records.writable) return emptyLab(mode);\n\n const joined = documentOfRecords(records.entries());\n if (!joined) return foldOldStorage(options, records);\n\n const outcome = runMigrations(joined.doc, MIGRATIONS, CURRENT_DOCUMENT_VERSION);\n if (outcome.ok) {\n return { doc: normalizeDocument(outcome.doc, mode), orders: joined.orders };\n }\n const why =\n outcome.reason === 'future' ? 'are from a newer version of labkit' : 'failed to migrate';\n console.warn(`[labkit] lab records ${why}; starting empty and leaving them alone`, outcome.error);\n records.stopWriting();\n return emptyLab(mode);\n}\n\n/** Fold a version-3 document, or the four keys before it, into records. The\n * old keys are deleted only once every record has been read back. */\nasync function foldOldStorage(\n options: OpenLabStoreOptions,\n records: OwnedRecordCache,\n): Promise<ReadLab> {\n const { storage, storageKey } = options;\n const mode = options.initialMode ?? 'auto';\n\n let raw: unknown;\n let oldKeys: string[];\n try {\n const stored = await storage.get(labDocumentKey(storageKey));\n if (stored !== undefined) {\n raw = stored;\n oldKeys = [labDocumentKey(storageKey)];\n } else {\n raw = await readLegacyDocument((key) => storage.get(key), storageKey, mode);\n oldKeys = legacyKeys(storageKey);\n if (raw === null) return emptyLab(mode);\n }\n } catch (error) {\n console.warn(\n `[labkit] could not read the old storage for \"${storageKey}\"; not persisting`,\n error,\n );\n records.stopWriting();\n return emptyLab(mode);\n }\n\n const setAside = async (value: unknown, why: string, error?: unknown): Promise<ReadLab> => {\n if (await quarantineDocument(storage, storageKey, value)) {\n console.warn(`[labkit] ${why}; quarantined it and starting empty`, error);\n await deleteConfirmed(storage, oldKeys);\n records.set(META_RECORD, { version: CURRENT_DOCUMENT_VERSION, mode } satisfies LabMeta);\n } else {\n console.warn(\n `[labkit] ${why} and could not be quarantined; leaving it in place and not persisting`,\n error,\n );\n records.stopWriting();\n }\n return emptyLab(mode);\n };\n\n const parsed = parseStored(raw);\n if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {\n return setAside(raw, 'lab document is unparseable');\n }\n const outcome = runMigrations(\n parsed as Record<string, unknown>,\n MIGRATIONS,\n CURRENT_DOCUMENT_VERSION,\n );\n if (!outcome.ok) {\n if (outcome.reason === 'future') {\n console.warn(\n '[labkit] lab document is from a newer version of labkit; starting empty and leaving it alone',\n );\n records.stopWriting();\n return emptyLab(mode);\n }\n return setAside(parsed, 'lab document failed to migrate', outcome.error);\n }\n\n const doc = normalizeDocument(outcome.doc, mode);\n const entries = recordsOfDocument(doc);\n records.seed(entries);\n if (await writeConfirmed(storage, records.prefix, entries)) {\n await deleteConfirmed(storage, oldKeys);\n } else {\n console.warn(\n `[labkit] keeping the old storage for \"${storageKey}\": could not confirm its records were written`,\n );\n }\n return { doc, orders: ordersOf(doc) };\n}\n\n/** Write `entries` in order and read every one back. */\nasync function writeConfirmed(\n storage: StorageAdapter,\n prefix: string,\n entries: [string, unknown][],\n): Promise<boolean> {\n try {\n for (const [name, value] of entries) await storage.set(prefix + name, value);\n const back = new Map(await storage.list(prefix));\n return entries.every(([name, value]) => sameStored(back.get(prefix + name), value));\n } catch {\n return false;\n }\n}\n\ninterface BindOptions {\n orders: Map<string, number>;\n}\n\nfunction persistedFieldsDiffer(a: TrialRecord, b: TrialRecord): boolean {\n if (a === b) return false;\n const left = a as unknown as Record<string, unknown>;\n const right = b as unknown as Record<string, unknown>;\n for (const key of new Set([...Object.keys(left), ...Object.keys(right)])) {\n if (key !== 'undoStack' && !Object.is(left[key], right[key])) return true;\n }\n return false;\n}\n\n/** Keep `store` and `records` in step, both ways. Returns the unbinding. */\nfunction bindLabStore(store: LabStore, records: RecordCache, options: BindOptions): () => void {\n const orderOf = new Map(options.orders);\n let applyingRemote = false;\n\n const nextOrder = (): number => {\n let max = -1;\n for (const order of orderOf.values()) max = Math.max(max, order);\n return max + 1;\n };\n\n const writeTrial = (trial: TrialRecord): void => {\n const [serialized] = serializeTrials([trial], store.getState().instrumentHooks().serializers);\n records.set(trialRecord(trial.id), { ...serialized, order: orderOf.get(trial.id) });\n };\n\n const inStoredOrder = (trials: TrialRecord[]): boolean => {\n for (let i = 1; i < trials.length; i++) {\n const a = trials[i - 1] as TrialRecord;\n const b = trials[i] as TrialRecord;\n const at = { trial: a, order: orderOf.get(a.id) ?? 0 };\n const bt = { trial: b, order: orderOf.get(b.id) ?? 0 };\n if (compareTrialOrder(at, bt) >= 0) return false;\n }\n return true;\n };\n\n const offStore = store.subscribe((state, prev) => {\n if (applyingRemote) return;\n let wrote = false;\n\n if (state.trials !== prev.trials) {\n const before = new Map(prev.trials.map((t) => [t.id, t]));\n const now = new Set(state.trials.map((t) => t.id));\n for (const t of prev.trials) {\n if (now.has(t.id)) continue;\n records.delete(trialRecord(t.id));\n for (const [name] of records.entries(trialValuesPrefix(t.id))) records.delete(name);\n orderOf.delete(t.id);\n wrote = true;\n }\n const added = state.trials.filter((t) => !before.has(t.id));\n for (const t of added) orderOf.set(t.id, nextOrder());\n if (inStoredOrder(state.trials)) {\n for (const t of state.trials) {\n const old = before.get(t.id);\n if (old && !persistedFieldsDiffer(old, t)) continue;\n writeTrial(t);\n wrote = true;\n }\n } else {\n // A reorder, or a trial inserted mid-list: renumber them all.\n for (const [i, t] of state.trials.entries()) orderOf.set(t.id, i);\n for (const t of state.trials) writeTrial(t);\n wrote = true;\n }\n }\n\n if (state.savedSnapshots !== prev.savedSnapshots) {\n const before = new Map(prev.savedSnapshots.map((s) => [s.id, s]));\n const now = new Set(state.savedSnapshots.map((s) => s.id));\n for (const s of state.savedSnapshots) {\n if (before.get(s.id) === s) continue;\n records.set(saveRecord(s.id), s);\n wrote = true;\n }\n for (const s of prev.savedSnapshots) {\n if (now.has(s.id)) continue;\n records.delete(saveRecord(s.id));\n wrote = true;\n }\n }\n\n if (state.layout !== prev.layout) {\n records.set(LAYOUT_RECORD, state.layout);\n wrote = true;\n }\n if (state.undockedPanels !== prev.undockedPanels) {\n records.set(UNDOCK_RECORD, state.undockedPanels);\n wrote = true;\n }\n if (state.mode !== prev.mode || (wrote && !records.has(META_RECORD))) {\n records.set(META_RECORD, {\n version: CURRENT_DOCUMENT_VERSION,\n mode: state.mode,\n } satisfies LabMeta);\n }\n });\n\n const offRecords = records.subscribe((changes) => {\n const remote = changes.filter((c) => c.origin === 'remote');\n if (remote.length === 0) return;\n\n const current = store.getState();\n let trials = current.trials;\n let saves = current.savedSnapshots;\n const patch: Partial<Pick<typeof current, 'layout' | 'undockedPanels' | 'mode'>> = {};\n\n for (const { name, value } of remote) {\n const parsed = parseRecordName(name);\n if (!parsed) continue;\n switch (parsed.kind) {\n case 'trial': {\n const id = parsed.id;\n if (value === undefined || !value || typeof value !== 'object') {\n trials = trials.filter((t) => t.id !== id);\n orderOf.delete(id);\n break;\n }\n const { order, ...rest } = value as SerializedTrial & { order?: unknown };\n orderOf.set(\n id,\n typeof order === 'number' && Number.isFinite(order) ? order : nextOrder(),\n );\n // Hydrating starts an empty undo history: the old one describes a\n // state that no longer exists.\n const hooks = store.getState().instrumentHooks();\n const [replacement] = hydrateTrials([{ ...rest, id }], hooks.serializers, hooks);\n if (!replacement) break;\n trials = trials.some((t) => t.id === id)\n ? trials.map((t) => (t.id === id ? replacement : t))\n : [...trials, replacement];\n break;\n }\n case 'save': {\n const id = parsed.id;\n if (value === undefined || !value || typeof value !== 'object') {\n saves = saves.filter((s) => s.id !== id);\n break;\n }\n const [snapshot] = hydrateSnapshots(\n [{ ...(value as SavedSnapshot), id }],\n store.getState().instrumentHooks(),\n );\n if (!snapshot) break;\n saves = saves.some((s) => s.id === id)\n ? saves.map((s) => (s.id === id ? snapshot : s))\n : [...saves, snapshot];\n break;\n }\n case 'layout':\n patch.layout = (value as Record<string, unknown> | undefined) ?? {};\n break;\n case 'undock':\n patch.undockedPanels = (value as typeof current.undockedPanels | undefined) ?? {};\n break;\n case 'meta': {\n const meta = value as Partial<LabMeta> | undefined;\n if (typeof meta?.version === 'number' && meta.version > CURRENT_DOCUMENT_VERSION) {\n console.warn(\n '[labkit] another tab wrote this lab with a newer labkit; no longer persisting',\n );\n if ('stopWriting' in records) (records as OwnedRecordCache).stopWriting();\n break;\n }\n if (meta?.mode === 'light' || meta?.mode === 'dark' || meta?.mode === 'auto') {\n patch.mode = meta.mode;\n }\n break;\n }\n }\n }\n\n if (trials !== current.trials) {\n trials = [...trials].sort((a, b) =>\n compareTrialOrder(\n { trial: a, order: orderOf.get(a.id) ?? 0 },\n { trial: b, order: orderOf.get(b.id) ?? 0 },\n ),\n );\n }\n applyingRemote = true;\n try {\n store.setState({ ...patch, trials, savedSnapshots: saves });\n } finally {\n applyingRemote = false;\n }\n });\n\n return () => {\n offStore();\n offRecords();\n };\n}\n","import { useContext } from 'react';\nimport { useStore } from 'zustand/react';\nimport { LabStoreContext, TrialIdContext } from './context';\nimport type { TrialStateHandle } from './types';\n\n/** An instrument's own state and config, plus setters. Reads the surrounding\n * trial id, so an instrument never has to know which trial it is\n * running in. Throws outside a lab store and trial. */\nexport function useTrialState<TS = unknown, TC = unknown>(): TrialStateHandle<TS, TC> {\n const ctx = useContext(LabStoreContext);\n if (!ctx) throw new Error('[labkit] useTrialState must be used inside <LabStoreProvider>');\n\n const trialId = useContext(TrialIdContext);\n if (!trialId) throw new Error('[labkit] useTrialState must be used inside <TrialIdProvider>');\n\n const record = useStore(ctx.store, (s) => s.trials.find((w) => w.id === trialId));\n\n if (!record) throw new Error(`[labkit] No trial found with id \"${trialId}\"`);\n\n const updateTrialState = useStore(ctx.store, (s) => s.updateTrialState);\n const updateTrialConfig = useStore(ctx.store, (s) => s.updateTrialConfig);\n\n return {\n state: record.state as TS,\n config: record.config as TC,\n setState: (next) => updateTrialState(trialId, next as Parameters<typeof updateTrialState>[1]),\n setConfig: (path, value) => updateTrialConfig(trialId, path, value),\n };\n}\n"]}
|
|
@@ -0,0 +1,310 @@
|
|
|
1
|
+
import { LabContext } from './chunk-NDRYLVOW.js';
|
|
2
|
+
import { fromConfigFields } from './chunk-UU3NJO6N.js';
|
|
3
|
+
import { useContext, useMemo } from 'react';
|
|
4
|
+
|
|
5
|
+
// src/config/builder.ts
|
|
6
|
+
function isConfigBranch(entry) {
|
|
7
|
+
return "children" in entry;
|
|
8
|
+
}
|
|
9
|
+
var BaseNode = class {
|
|
10
|
+
constructor(defaultValue, annotations = {}, options = {}) {
|
|
11
|
+
this.annotations = annotations;
|
|
12
|
+
this.options = options;
|
|
13
|
+
this.default = defaultValue;
|
|
14
|
+
}
|
|
15
|
+
annotations;
|
|
16
|
+
options;
|
|
17
|
+
// `default` is a reserved word in a parameter position, so the field is
|
|
18
|
+
// declared rather than taken as a parameter property.
|
|
19
|
+
default;
|
|
20
|
+
/** Clone into the same subclass. Subclasses with extra constructor
|
|
21
|
+
* arguments override this. */
|
|
22
|
+
with(annotations, options) {
|
|
23
|
+
const Ctor = this.constructor;
|
|
24
|
+
return new Ctor(this.default, annotations, options);
|
|
25
|
+
}
|
|
26
|
+
ann(patch) {
|
|
27
|
+
return this.with({ ...this.annotations, ...patch }, this.options);
|
|
28
|
+
}
|
|
29
|
+
opt(patch) {
|
|
30
|
+
return this.with(this.annotations, { ...this.options, ...patch });
|
|
31
|
+
}
|
|
32
|
+
/** Human-readable label. Defaults to the key, title-cased. */
|
|
33
|
+
label(name) {
|
|
34
|
+
return this.ann({ name });
|
|
35
|
+
}
|
|
36
|
+
/** Longer help text, surfaced as a tooltip on the row label. */
|
|
37
|
+
describe(description) {
|
|
38
|
+
return this.ann({ description });
|
|
39
|
+
}
|
|
40
|
+
/** Omit from the panel unless it is asked to show hidden leaves. */
|
|
41
|
+
hidden() {
|
|
42
|
+
return this.ann({ hidden: true });
|
|
43
|
+
}
|
|
44
|
+
/** Pair with a sibling sharing this id, side-by-side on one row. */
|
|
45
|
+
pair(pair) {
|
|
46
|
+
return this.ann({ pair });
|
|
47
|
+
}
|
|
48
|
+
/** Render under a named section heading. `collapsed` opens the section
|
|
49
|
+
* folded — say it on any one of the section's leaves. */
|
|
50
|
+
section(label, opts = {}) {
|
|
51
|
+
return this.opt({ section: { label, ...opts } });
|
|
52
|
+
}
|
|
53
|
+
/** Show this row only while the predicate holds. Presentational — the value
|
|
54
|
+
* stays in config and the instrument still reads it. */
|
|
55
|
+
showIf(predicate) {
|
|
56
|
+
return this.opt({ showIf: predicate });
|
|
57
|
+
}
|
|
58
|
+
/** Draw this one row yourself, keeping the kind, default and validation. */
|
|
59
|
+
render(renderer) {
|
|
60
|
+
return this.opt({ render: renderer });
|
|
61
|
+
}
|
|
62
|
+
};
|
|
63
|
+
var NumberNode = class extends BaseNode {
|
|
64
|
+
kind = "number";
|
|
65
|
+
/** Bound the value. A number with both bounds renders as a slider. */
|
|
66
|
+
range(min, max) {
|
|
67
|
+
return this.ann({ min, max });
|
|
68
|
+
}
|
|
69
|
+
step(step) {
|
|
70
|
+
return this.ann({ step });
|
|
71
|
+
}
|
|
72
|
+
/** A display suffix for the value — `'px'`, `'ms'`, `'°'`. Presentation only:
|
|
73
|
+
* it is never parsed, and the stored value stays a plain number. */
|
|
74
|
+
suffix(suffix) {
|
|
75
|
+
return this.ann({ suffix });
|
|
76
|
+
}
|
|
77
|
+
/** Show the value in a named format — `'compact'` reads `2.0M`. Presentation
|
|
78
|
+
* only: the stored value stays a plain number, and a typed `2.5m` reads back. */
|
|
79
|
+
format(format) {
|
|
80
|
+
return this.ann({ format });
|
|
81
|
+
}
|
|
82
|
+
/** Force a slider even without both bounds. */
|
|
83
|
+
slider() {
|
|
84
|
+
return this.ann({ control: "slider" });
|
|
85
|
+
}
|
|
86
|
+
/** Force a typed input even when both bounds are set. */
|
|
87
|
+
input() {
|
|
88
|
+
return this.ann({ control: "input" });
|
|
89
|
+
}
|
|
90
|
+
};
|
|
91
|
+
var BooleanNode = class extends BaseNode {
|
|
92
|
+
kind = "boolean";
|
|
93
|
+
/** Ask for a switch. `ControlPanel` still draws a checkbox; weasel-ui's
|
|
94
|
+
* `PrefsForm` honors the distinction. */
|
|
95
|
+
toggle() {
|
|
96
|
+
return this.ann({ control: "switch" });
|
|
97
|
+
}
|
|
98
|
+
};
|
|
99
|
+
var StringNode = class extends BaseNode {
|
|
100
|
+
kind = "string";
|
|
101
|
+
placeholder(placeholder) {
|
|
102
|
+
return this.ann({ placeholder });
|
|
103
|
+
}
|
|
104
|
+
maxLength(maxLength) {
|
|
105
|
+
return this.ann({ maxLength });
|
|
106
|
+
}
|
|
107
|
+
/** Milliseconds to debounce live writes. 0 commits every keystroke. */
|
|
108
|
+
debounce(debounceMs) {
|
|
109
|
+
return this.ann({ debounceMs });
|
|
110
|
+
}
|
|
111
|
+
};
|
|
112
|
+
var ColorNode = class extends BaseNode {
|
|
113
|
+
kind = "color";
|
|
114
|
+
};
|
|
115
|
+
var EnumNode = class extends BaseNode {
|
|
116
|
+
kind = "enum";
|
|
117
|
+
/** Render as a segmented control rather than a select. */
|
|
118
|
+
radio() {
|
|
119
|
+
return this.ann({ control: "radio" });
|
|
120
|
+
}
|
|
121
|
+
/** Relabel the choices. Named `labels` rather than `options` because
|
|
122
|
+
* `options` is the node's own extras bag. */
|
|
123
|
+
labels(options) {
|
|
124
|
+
return this.ann({ options });
|
|
125
|
+
}
|
|
126
|
+
};
|
|
127
|
+
var ValueNode = class extends BaseNode {
|
|
128
|
+
kind = null;
|
|
129
|
+
};
|
|
130
|
+
var CustomNode = class _CustomNode extends BaseNode {
|
|
131
|
+
constructor(kind, defaultValue, annotations = {}, options = {}) {
|
|
132
|
+
super(defaultValue, annotations, options);
|
|
133
|
+
this.kind = kind;
|
|
134
|
+
}
|
|
135
|
+
kind;
|
|
136
|
+
with(annotations, options) {
|
|
137
|
+
return new _CustomNode(this.kind, this.default, annotations, options);
|
|
138
|
+
}
|
|
139
|
+
};
|
|
140
|
+
var GroupNode = class _GroupNode {
|
|
141
|
+
constructor(children, annotations = {}, options = {}) {
|
|
142
|
+
this.children = children;
|
|
143
|
+
this.annotations = annotations;
|
|
144
|
+
this.options = options;
|
|
145
|
+
}
|
|
146
|
+
children;
|
|
147
|
+
annotations;
|
|
148
|
+
options;
|
|
149
|
+
with(annotations, options) {
|
|
150
|
+
return new _GroupNode(this.children, annotations, options);
|
|
151
|
+
}
|
|
152
|
+
/** Heading for the group's rows. Defaults to the key, title-cased. */
|
|
153
|
+
label(name) {
|
|
154
|
+
return this.with({ ...this.annotations, name }, this.options);
|
|
155
|
+
}
|
|
156
|
+
/** Longer help text. Carried on the resolved `PrefGroup`, where weasel-ui's
|
|
157
|
+
* `PrefsForm` draws it under the heading; `ControlPanel` has no place for
|
|
158
|
+
* it yet and shows the heading alone. */
|
|
159
|
+
describe(description) {
|
|
160
|
+
return this.with({ ...this.annotations, description }, this.options);
|
|
161
|
+
}
|
|
162
|
+
/** Render this whole group under a named section heading. */
|
|
163
|
+
section(label, opts = {}) {
|
|
164
|
+
return this.with(this.annotations, { ...this.options, section: { label, ...opts } });
|
|
165
|
+
}
|
|
166
|
+
/** Show this group only while the predicate holds. Presentational — every
|
|
167
|
+
* value beneath it stays in config and the instrument still reads it. */
|
|
168
|
+
showIf(predicate) {
|
|
169
|
+
return this.with(this.annotations, { ...this.options, showIf: predicate });
|
|
170
|
+
}
|
|
171
|
+
};
|
|
172
|
+
function shapeDefaults(shape) {
|
|
173
|
+
const out = {};
|
|
174
|
+
for (const [key, entry] of Object.entries(shape)) {
|
|
175
|
+
out[key] = isConfigBranch(entry) ? shapeDefaults(entry.children) : entry.default;
|
|
176
|
+
}
|
|
177
|
+
return out;
|
|
178
|
+
}
|
|
179
|
+
var expand = (options) => options.map((o) => typeof o === "string" ? { value: o, label: o } : o);
|
|
180
|
+
var f = {
|
|
181
|
+
number: (def) => new NumberNode(def),
|
|
182
|
+
boolean: (def) => new BooleanNode(def),
|
|
183
|
+
string: (def) => new StringNode(def),
|
|
184
|
+
color: (def) => new ColorNode(def),
|
|
185
|
+
/** A fixed set of choices. The default's literal type flows into the config
|
|
186
|
+
* type, so `f.enum('fast', ['fast', 'accurate'])` gives
|
|
187
|
+
* `'fast' | 'accurate'` with no `as const`. */
|
|
188
|
+
enum: (def, options) => new EnumNode(def, { options: expand(options) }),
|
|
189
|
+
/** A leaf with no declared kind: the rule chain decides, which is how a lab
|
|
190
|
+
* states a convention like "every `*Color` key is a color picker". */
|
|
191
|
+
value: (def) => new ValueNode(def),
|
|
192
|
+
/** A branch: its children nest under this key, so `grid: f.group({ size })`
|
|
193
|
+
* puts the value at `config.grid.size` and addresses it as `'grid.size'`.
|
|
194
|
+
* Groups nest as deeply as the schema wants. */
|
|
195
|
+
group: (children) => new GroupNode(children),
|
|
196
|
+
/** A leaf of a kind a lab supplies the control for, through `controls`. */
|
|
197
|
+
custom: (kind, def, validate) => new CustomNode(kind, def, {}, validate ? { validate } : {}),
|
|
198
|
+
/** Collect leaves and groups into an instrument's config. */
|
|
199
|
+
schema(nodes) {
|
|
200
|
+
return { nodes, defaults: () => shapeDefaults(nodes) };
|
|
201
|
+
}
|
|
202
|
+
};
|
|
203
|
+
|
|
204
|
+
// src/config/rules.ts
|
|
205
|
+
function titleCase(key) {
|
|
206
|
+
const words = key.replace(/[_-]+/g, " ").split(/(?<=[a-z0-9])(?=[A-Z])/).join(" ").trim().toLowerCase();
|
|
207
|
+
return words.length === 0 ? words : words[0].toUpperCase() + words.slice(1);
|
|
208
|
+
}
|
|
209
|
+
var kindFromValue = (ctx) => {
|
|
210
|
+
const t = typeof ctx.default;
|
|
211
|
+
if (t === "boolean" || t === "number" || t === "string") return { kind: t };
|
|
212
|
+
return null;
|
|
213
|
+
};
|
|
214
|
+
var labelFromKey = (ctx) => ({ name: titleCase(ctx.key) });
|
|
215
|
+
var sliderWhenBounded = (ctx) => {
|
|
216
|
+
if (ctx.leaf.kind !== "number") return null;
|
|
217
|
+
const bounded = ctx.leaf.min !== void 0 && ctx.leaf.max !== void 0;
|
|
218
|
+
return bounded ? { control: "slider" } : null;
|
|
219
|
+
};
|
|
220
|
+
var descriptionDefault = () => ({ description: "" });
|
|
221
|
+
var builtinRules = [
|
|
222
|
+
kindFromValue,
|
|
223
|
+
labelFromKey,
|
|
224
|
+
sliderWhenBounded,
|
|
225
|
+
descriptionDefault
|
|
226
|
+
];
|
|
227
|
+
function applyRules(seed, ctx, rules) {
|
|
228
|
+
const acc = { ...seed };
|
|
229
|
+
for (const rule of rules) {
|
|
230
|
+
const patch = rule({ ...ctx, leaf: acc });
|
|
231
|
+
if (patch === null) continue;
|
|
232
|
+
for (const [k, v] of Object.entries(patch)) {
|
|
233
|
+
if (v !== void 0 && acc[k] === void 0) {
|
|
234
|
+
acc[k] = v;
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
return acc;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
// src/config/resolve.ts
|
|
242
|
+
function defined(annotations) {
|
|
243
|
+
const out = {};
|
|
244
|
+
for (const [k, v] of Object.entries(annotations)) if (v !== void 0) out[k] = v;
|
|
245
|
+
return out;
|
|
246
|
+
}
|
|
247
|
+
function resolveConfigSchema(schema, rules = []) {
|
|
248
|
+
const sink = {
|
|
249
|
+
sections: [],
|
|
250
|
+
showIf: /* @__PURE__ */ new Map(),
|
|
251
|
+
renderers: {},
|
|
252
|
+
chain: [...rules, ...builtinRules]
|
|
253
|
+
};
|
|
254
|
+
const group = resolveShape(schema.nodes, "", "", sink);
|
|
255
|
+
return { group, sections: sink.sections, showIf: sink.showIf, renderers: sink.renderers };
|
|
256
|
+
}
|
|
257
|
+
function resolveShape(shape, at, name, sink) {
|
|
258
|
+
const children = {};
|
|
259
|
+
const sections = /* @__PURE__ */ new Map();
|
|
260
|
+
for (const [key, entry] of Object.entries(shape)) {
|
|
261
|
+
const path = at === "" ? key : `${at}.${key}`;
|
|
262
|
+
children[key] = resolveEntry(entry, key, path, sink);
|
|
263
|
+
const { section, showIf: predicate } = entry.options;
|
|
264
|
+
if (section !== void 0) {
|
|
265
|
+
let spec = sections.get(section.label);
|
|
266
|
+
if (!spec) {
|
|
267
|
+
spec = { at, label: section.label, paths: [] };
|
|
268
|
+
sections.set(section.label, spec);
|
|
269
|
+
sink.sections.push(spec);
|
|
270
|
+
}
|
|
271
|
+
spec.paths.push(path);
|
|
272
|
+
if (section.collapsed !== void 0) {
|
|
273
|
+
spec.collapsed = (spec.collapsed ?? false) || section.collapsed;
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
if (predicate) sink.showIf.set(path, predicate);
|
|
277
|
+
if (!isConfigBranch(entry) && entry.options.render) sink.renderers[path] = entry.options.render;
|
|
278
|
+
}
|
|
279
|
+
return { name, children };
|
|
280
|
+
}
|
|
281
|
+
function resolveEntry(entry, key, path, sink) {
|
|
282
|
+
if (isConfigBranch(entry)) {
|
|
283
|
+
const group = resolveShape(
|
|
284
|
+
entry.children,
|
|
285
|
+
path,
|
|
286
|
+
entry.annotations.name ?? titleCase(key),
|
|
287
|
+
sink
|
|
288
|
+
);
|
|
289
|
+
return entry.annotations.description === void 0 ? group : { ...group, description: entry.annotations.description };
|
|
290
|
+
}
|
|
291
|
+
const seed = defined({
|
|
292
|
+
...entry.annotations,
|
|
293
|
+
...entry.kind === null ? {} : { kind: entry.kind }
|
|
294
|
+
});
|
|
295
|
+
const patch = applyRules(seed, { key, path, default: entry.default }, sink.chain);
|
|
296
|
+
return { ...patch, default: entry.default };
|
|
297
|
+
}
|
|
298
|
+
var NO_RULES = [];
|
|
299
|
+
function useConfigSchema(instrument) {
|
|
300
|
+
const lab = useContext(LabContext);
|
|
301
|
+
const rules = lab?.configRules ?? NO_RULES;
|
|
302
|
+
return useMemo(
|
|
303
|
+
() => instrument.config ? resolveConfigSchema(instrument.config, rules) : fromConfigFields(instrument.configSchema?.() ?? []),
|
|
304
|
+
[instrument, rules]
|
|
305
|
+
);
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
export { BaseNode, BooleanNode, ColorNode, CustomNode, EnumNode, GroupNode, NumberNode, StringNode, ValueNode, applyRules, builtinRules, f, isConfigBranch, resolveConfigSchema, titleCase, useConfigSchema };
|
|
309
|
+
//# sourceMappingURL=chunk-6ZDGOZQV.js.map
|
|
310
|
+
//# sourceMappingURL=chunk-6ZDGOZQV.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/config/builder.ts","../src/config/rules.ts","../src/config/resolve.ts","../src/config/useConfigSchema.ts"],"names":[],"mappings":"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","file":"chunk-6ZDGOZQV.js","sourcesContent":["import type { PrefLeaf, PrefNumberFormat } from '@weasel-js/ui';\nimport type {\n Annotations,\n BranchAnnotations,\n BranchOptions,\n ConfigBranch,\n ConfigEntry,\n ConfigNode,\n ConfigOption,\n ConfigSchema,\n ConfigShape,\n ControlRenderer,\n InferConfig,\n NodeOptions,\n} from './types';\n\n/** Whether a schema entry is a branch rather than a leaf. */\nexport function isConfigBranch(entry: ConfigEntry): entry is ConfigBranch {\n return 'children' in entry;\n}\n\n/** Shared chaining surface. Every method clones, so a node can be reused as a\n * base for several leaves without one bleeding into the next. */\nexport abstract class BaseNode<T> implements ConfigNode<T> {\n abstract readonly kind: string | null;\n\n // `default` is a reserved word in a parameter position, so the field is\n // declared rather than taken as a parameter property.\n readonly default: T;\n\n constructor(\n defaultValue: T,\n readonly annotations: Readonly<Annotations> = {},\n readonly options: Readonly<NodeOptions> = {},\n ) {\n this.default = defaultValue;\n }\n\n /** Clone into the same subclass. Subclasses with extra constructor\n * arguments override this. */\n protected with(annotations: Annotations, options: NodeOptions): this {\n const Ctor = this.constructor as new (d: T, a: Annotations, o: NodeOptions) => this;\n return new Ctor(this.default, annotations, options);\n }\n\n protected ann(patch: Annotations): this {\n return this.with({ ...this.annotations, ...patch }, this.options);\n }\n\n protected opt(patch: NodeOptions): this {\n return this.with(this.annotations, { ...this.options, ...patch });\n }\n\n /** Human-readable label. Defaults to the key, title-cased. */\n label(name: string): this {\n return this.ann({ name });\n }\n\n /** Longer help text, surfaced as a tooltip on the row label. */\n describe(description: string): this {\n return this.ann({ description });\n }\n\n /** Omit from the panel unless it is asked to show hidden leaves. */\n hidden(): this {\n return this.ann({ hidden: true });\n }\n\n /** Pair with a sibling sharing this id, side-by-side on one row. */\n pair(pair: string): this {\n return this.ann({ pair });\n }\n\n /** Render under a named section heading. `collapsed` opens the section\n * folded — say it on any one of the section's leaves. */\n section(label: string, opts: { collapsed?: boolean } = {}): this {\n return this.opt({ section: { label, ...opts } });\n }\n\n /** Show this row only while the predicate holds. Presentational — the value\n * stays in config and the instrument still reads it. */\n showIf(predicate: (config: Record<string, unknown>) => boolean): this {\n return this.opt({ showIf: predicate });\n }\n\n /** Draw this one row yourself, keeping the kind, default and validation. */\n render(renderer: ControlRenderer): this {\n return this.opt({ render: renderer });\n }\n}\n\nexport class NumberNode extends BaseNode<number> {\n readonly kind = 'number';\n\n /** Bound the value. A number with both bounds renders as a slider. */\n range(min: number, max: number): this {\n return this.ann({ min, max });\n }\n\n step(step: number): this {\n return this.ann({ step });\n }\n\n /** A display suffix for the value — `'px'`, `'ms'`, `'°'`. Presentation only:\n * it is never parsed, and the stored value stays a plain number. */\n suffix(suffix: string): this {\n return this.ann({ suffix });\n }\n\n /** Show the value in a named format — `'compact'` reads `2.0M`. Presentation\n * only: the stored value stays a plain number, and a typed `2.5m` reads back. */\n format(format: PrefNumberFormat): this {\n return this.ann({ format });\n }\n\n /** Force a slider even without both bounds. */\n slider(): this {\n return this.ann({ control: 'slider' });\n }\n\n /** Force a typed input even when both bounds are set. */\n input(): this {\n return this.ann({ control: 'input' });\n }\n}\n\nexport class BooleanNode extends BaseNode<boolean> {\n readonly kind = 'boolean';\n\n /** Ask for a switch. `ControlPanel` still draws a checkbox; weasel-ui's\n * `PrefsForm` honors the distinction. */\n toggle(): this {\n return this.ann({ control: 'switch' });\n }\n}\n\nexport class StringNode extends BaseNode<string> {\n readonly kind = 'string';\n\n placeholder(placeholder: string): this {\n return this.ann({ placeholder });\n }\n\n maxLength(maxLength: number): this {\n return this.ann({ maxLength });\n }\n\n /** Milliseconds to debounce live writes. 0 commits every keystroke. */\n debounce(debounceMs: number): this {\n return this.ann({ debounceMs });\n }\n}\n\nexport class ColorNode extends BaseNode<string> {\n readonly kind = 'color';\n}\n\nexport class EnumNode<T extends string> extends BaseNode<T> {\n readonly kind = 'enum';\n\n /** Render as a segmented control rather than a select. */\n radio(): this {\n return this.ann({ control: 'radio' });\n }\n\n /** Relabel the choices. Named `labels` rather than `options` because\n * `options` is the node's own extras bag. */\n labels(options: readonly ConfigOption[]): this {\n return this.ann({ options });\n }\n}\n\n/** A leaf whose kind the rule chain decides. */\nexport class ValueNode<T> extends BaseNode<T> {\n readonly kind = null;\n}\n\n/** A leaf of a kind labkit does not ship a row for. */\nexport class CustomNode<T> extends BaseNode<T> {\n constructor(\n readonly kind: string,\n defaultValue: T,\n annotations: Readonly<Annotations> = {},\n options: Readonly<NodeOptions> = {},\n ) {\n super(defaultValue, annotations, options);\n }\n\n protected override with(annotations: Annotations, options: NodeOptions): this {\n return new CustomNode(this.kind, this.default, annotations, options) as this;\n }\n}\n\n/**\n * A branch of the schema: its children nest under its key, so a leaf inside\n * one is read and written at a dotted path. Distinct from `.section()`, which\n * puts a heading over sibling leaves and leaves their paths alone.\n */\nexport class GroupNode<S extends ConfigShape> implements ConfigBranch<S> {\n constructor(\n readonly children: S,\n readonly annotations: Readonly<BranchAnnotations> = {},\n readonly options: Readonly<BranchOptions> = {},\n ) {}\n\n private with(annotations: BranchAnnotations, options: BranchOptions): GroupNode<S> {\n return new GroupNode(this.children, annotations, options);\n }\n\n /** Heading for the group's rows. Defaults to the key, title-cased. */\n label(name: string): GroupNode<S> {\n return this.with({ ...this.annotations, name }, this.options);\n }\n\n /** Longer help text. Carried on the resolved `PrefGroup`, where weasel-ui's\n * `PrefsForm` draws it under the heading; `ControlPanel` has no place for\n * it yet and shows the heading alone. */\n describe(description: string): GroupNode<S> {\n return this.with({ ...this.annotations, description }, this.options);\n }\n\n /** Render this whole group under a named section heading. */\n section(label: string, opts: { collapsed?: boolean } = {}): GroupNode<S> {\n return this.with(this.annotations, { ...this.options, section: { label, ...opts } });\n }\n\n /** Show this group only while the predicate holds. Presentational — every\n * value beneath it stays in config and the instrument still reads it. */\n showIf(predicate: (config: Record<string, unknown>) => boolean): GroupNode<S> {\n return this.with(this.annotations, { ...this.options, showIf: predicate });\n }\n}\n\n/** Every default in a shape, nested the way the shape is. */\nfunction shapeDefaults(shape: ConfigShape): Record<string, unknown> {\n const out: Record<string, unknown> = {};\n for (const [key, entry] of Object.entries(shape)) {\n out[key] = isConfigBranch(entry) ? shapeDefaults(entry.children) : entry.default;\n }\n return out;\n}\n\nconst expand = <T extends string>(\n options: readonly T[] | readonly ConfigOption[],\n): readonly ConfigOption[] =>\n options.map((o) => (typeof o === 'string' ? { value: o, label: o } : o));\n\n/**\n * Builds an instrument's config: the values, their types, and how each is\n * edited, in one declaration.\n *\n * ```ts\n * const config = f.schema({\n * showGrid: f.boolean(true),\n * cellSize: f.number(20).range(5, 80).step(5).label('Grid spacing'),\n * // Nested: read and written at `grid.color`.\n * grid: f.group({ color: f.color('#ffffff') }),\n * })\n * ```\n */\nexport const f = {\n number: (def: number): NumberNode => new NumberNode(def),\n boolean: (def: boolean): BooleanNode => new BooleanNode(def),\n string: (def: string): StringNode => new StringNode(def),\n color: (def: string): ColorNode => new ColorNode(def),\n\n /** A fixed set of choices. The default's literal type flows into the config\n * type, so `f.enum('fast', ['fast', 'accurate'])` gives\n * `'fast' | 'accurate'` with no `as const`. */\n enum: <const T extends string>(\n def: T,\n options: readonly T[] | readonly ConfigOption[],\n ): EnumNode<T> => new EnumNode<T>(def, { options: expand(options) }),\n\n /** A leaf with no declared kind: the rule chain decides, which is how a lab\n * states a convention like \"every `*Color` key is a color picker\". */\n value: <T>(def: T): ValueNode<T> => new ValueNode(def),\n\n /** A branch: its children nest under this key, so `grid: f.group({ size })`\n * puts the value at `config.grid.size` and addresses it as `'grid.size'`.\n * Groups nest as deeply as the schema wants. */\n group: <const S extends ConfigShape>(children: S): GroupNode<S> => new GroupNode(children),\n\n /** A leaf of a kind a lab supplies the control for, through `controls`. */\n custom: <T>(kind: string, def: T, validate?: (leaf: PrefLeaf) => string[]): CustomNode<T> =>\n new CustomNode(kind, def, {}, validate ? { validate } : {}),\n\n /** Collect leaves and groups into an instrument's config. */\n schema<S extends ConfigShape>(nodes: S): ConfigSchema<InferConfig<S>> {\n return { nodes, defaults: () => shapeDefaults(nodes) as InferConfig<S> };\n },\n};\n","import type { ConfigRule, ConfigRuleContext, LeafPatch } from './types';\n\n/** `cellSize` -> `Cell size`. Splits camelCase without shattering acronyms. */\nexport function titleCase(key: string): string {\n const words = key\n .replace(/[_-]+/g, ' ')\n .split(/(?<=[a-z0-9])(?=[A-Z])/)\n .join(' ')\n .trim()\n .toLowerCase();\n return words.length === 0 ? words : words[0].toUpperCase() + words.slice(1);\n}\n\n/** An `f.value` leaf takes its kind from the shape of its default. */\nconst kindFromValue: ConfigRule = (ctx) => {\n const t = typeof ctx.default;\n if (t === 'boolean' || t === 'number' || t === 'string') return { kind: t };\n return null;\n};\n\nconst labelFromKey: ConfigRule = (ctx) => ({ name: titleCase(ctx.key) });\n\n/** A number bounded on both sides is a slider; one bound or none is an input. */\nconst sliderWhenBounded: ConfigRule = (ctx) => {\n if (ctx.leaf.kind !== 'number') return null;\n const bounded = ctx.leaf.min !== undefined && ctx.leaf.max !== undefined;\n return bounded ? { control: 'slider' } : null;\n};\n\n/** `PrefBase.description` is required, so every leaf must end up with one. */\nconst descriptionDefault: ConfigRule = () => ({ description: '' });\n\n/**\n * labkit's own inference, expressed in the same vocabulary a consumer\n * extends. Runs after the consumer's rules, so a lab always wins.\n *\n * If one of these ever needs something a `ConfigRule` cannot express, the\n * seam is too weak — that is the point of writing them this way.\n */\nexport const builtinRules: readonly ConfigRule[] = [\n kindFromValue,\n labelFromKey,\n sliderWhenBounded,\n descriptionDefault,\n];\n\n/**\n * Fold a rule chain into one leaf patch. Gap-filling: a property already\n * settled is never overwritten, so the accumulator seeded with the author's\n * annotations beats every rule, and earlier rules beat later ones.\n */\nexport function applyRules(\n seed: LeafPatch,\n ctx: Omit<ConfigRuleContext, 'leaf'>,\n rules: readonly ConfigRule[],\n): LeafPatch {\n const acc: LeafPatch = { ...seed };\n for (const rule of rules) {\n const patch = rule({ ...ctx, leaf: acc });\n if (patch === null) continue;\n for (const [k, v] of Object.entries(patch)) {\n if (v !== undefined && acc[k as keyof LeafPatch] === undefined) {\n (acc as Record<string, unknown>)[k] = v;\n }\n }\n }\n return acc;\n}\n","import type { PrefGroup, PrefLeaf } from '@weasel-js/ui';\nimport { isConfigBranch } from './builder';\nimport { applyRules, builtinRules, titleCase } from './rules';\nimport type {\n ConfigEntry,\n ConfigRule,\n ConfigSchema,\n ConfigShape,\n ControlRenderer,\n LeafPatch,\n ResolvedConfig,\n SectionSpec,\n} from './types';\n\n/** Drop keys whose value is undefined, so an unset annotation stays a gap the\n * rule chain can fill rather than a settled `undefined`. */\nfunction defined(annotations: Record<string, unknown>): LeafPatch {\n const out: Record<string, unknown> = {};\n for (const [k, v] of Object.entries(annotations)) if (v !== undefined) out[k] = v;\n return out as LeafPatch;\n}\n\n/** What the walk collects on the side of the `PrefGroup` tree. */\ninterface Sink {\n sections: SectionSpec[];\n showIf: Map<string, (config: Record<string, unknown>) => boolean>;\n renderers: Record<string, ControlRenderer>;\n chain: readonly ConfigRule[];\n}\n\n/**\n * Resolve a schema into the vocabulary weasel-ui renders, running each leaf\n * through the consumer's rules and then labkit's own.\n *\n * The group mirrors the schema's own nesting: an `f.group` becomes a nested\n * `PrefGroup`, so a leaf's dotted path within the tree is the path its value\n * is written at, and both `ControlPanel` and `PrefsForm` address it\n * identically.\n */\nexport function resolveConfigSchema<TC>(\n schema: ConfigSchema<TC>,\n rules: readonly ConfigRule[] = [],\n): ResolvedConfig {\n const sink: Sink = {\n sections: [],\n showIf: new Map(),\n renderers: {},\n chain: [...rules, ...builtinRules],\n };\n const group = resolveShape(schema.nodes, '', '', sink);\n return { group, sections: sink.sections, showIf: sink.showIf, renderers: sink.renderers };\n}\n\n/** A section while it is still being filled. */\ntype OpenSection = Omit<SectionSpec, 'paths'> & { paths: string[] };\n\nfunction resolveShape(shape: ConfigShape, at: string, name: string, sink: Sink): PrefGroup {\n const children: Record<string, PrefLeaf | PrefGroup> = {};\n const sections = new Map<string, OpenSection>();\n\n for (const [key, entry] of Object.entries(shape)) {\n const path = at === '' ? key : `${at}.${key}`;\n children[key] = resolveEntry(entry, key, path, sink);\n\n const { section, showIf: predicate } = entry.options;\n if (section !== undefined) {\n let spec = sections.get(section.label);\n if (!spec) {\n spec = { at, label: section.label, paths: [] };\n sections.set(section.label, spec);\n sink.sections.push(spec);\n }\n spec.paths.push(path);\n // One node saying `collapsed` settles the section, so a schema does not\n // have to repeat it on every node under the heading.\n if (section.collapsed !== undefined) {\n spec.collapsed = (spec.collapsed ?? false) || section.collapsed;\n }\n }\n if (predicate) sink.showIf.set(path, predicate);\n if (!isConfigBranch(entry) && entry.options.render) sink.renderers[path] = entry.options.render;\n }\n\n return { name, children };\n}\n\nfunction resolveEntry(\n entry: ConfigEntry,\n key: string,\n path: string,\n sink: Sink,\n): PrefLeaf | PrefGroup {\n if (isConfigBranch(entry)) {\n const group = resolveShape(\n entry.children,\n path,\n entry.annotations.name ?? titleCase(key),\n sink,\n );\n return entry.annotations.description === undefined\n ? group\n : { ...group, description: entry.annotations.description };\n }\n const seed: LeafPatch = defined({\n ...entry.annotations,\n ...(entry.kind === null ? {} : { kind: entry.kind }),\n });\n const patch = applyRules(seed, { key, path, default: entry.default }, sink.chain);\n return { ...patch, default: entry.default } as PrefLeaf;\n}\n","import { useContext, useMemo } from 'react';\nimport type { Instrument } from '../instrument/types';\nimport { LabContext } from '../lab/LabContext';\nimport { fromConfigFields } from './fromConfigField';\nimport { resolveConfigSchema } from './resolve';\nimport type { ResolvedConfig } from './types';\n\nconst NO_RULES: readonly [] = [];\n\n/**\n * An instrument's controls, resolved against the surrounding lab's rules.\n *\n * Resolution cannot happen in `defineInstrument` — rules are lab-scoped and\n * that runs at module load — so it happens here, at render. Both instrument\n * paths land in the same shape, so callers never branch on which was used.\n */\nexport function useConfigSchema(\n // biome-ignore lint/suspicious/noExplicitAny: instruments are stored contravariantly; see InstrumentList\n instrument: Instrument<any, any, any>, // eslint-disable-line @typescript-eslint/no-explicit-any\n): ResolvedConfig {\n const lab = useContext(LabContext);\n const rules = lab?.configRules ?? NO_RULES;\n return useMemo(\n () =>\n instrument.config\n ? resolveConfigSchema(instrument.config, rules)\n : fromConfigFields(instrument.configSchema?.() ?? []),\n [instrument, rules],\n );\n}\n"]}
|