@mulmoclaude/core 4.4.2 → 4.6.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.
Files changed (81) hide show
  1. package/assets/helps/bug-report-faq.md +13 -0
  2. package/assets/helps/business.md +4 -0
  3. package/assets/helps/error-recovery.md +71 -0
  4. package/assets/helps/gemini.md +1 -1
  5. package/assets/helps/mulmoscript.md +71 -1
  6. package/assets/helps/presentation-deck.md +4 -1
  7. package/assets/helps/storyteller.md +7 -0
  8. package/dist/collection/core/accentColor.d.ts +23 -0
  9. package/dist/collection/core/iconGlyph.d.ts +26 -0
  10. package/dist/collection/core/schema.d.ts +9 -0
  11. package/dist/collection/core/schemaZ.d.ts +19 -1
  12. package/dist/collection/core/shortcutInfo.d.ts +14 -0
  13. package/dist/collection/core/uiTypes.d.ts +6 -0
  14. package/dist/collection/index.cjs +22 -2
  15. package/dist/collection/index.cjs.map +1 -1
  16. package/dist/collection/index.d.ts +3 -0
  17. package/dist/collection/index.js +17 -3
  18. package/dist/collection/index.js.map +1 -1
  19. package/dist/collection/registry/server/index.cjs +3 -3
  20. package/dist/collection/registry/server/index.js +3 -3
  21. package/dist/collection/server/index.cjs +6 -4
  22. package/dist/collection/server/index.js +5 -5
  23. package/dist/collection/server/paths.d.ts +10 -0
  24. package/dist/collection/server/skillAssets.d.ts +20 -0
  25. package/dist/collection-watchers/index.cjs +5 -5
  26. package/dist/collection-watchers/index.js +5 -5
  27. package/dist/{discovery-Dl9nRa-m.cjs → discovery-2TvulIz8.cjs} +25 -5
  28. package/dist/discovery-2TvulIz8.cjs.map +1 -0
  29. package/dist/{discovery-P64fgtuD.js → discovery-BY-nMCuh.js} +20 -6
  30. package/dist/discovery-BY-nMCuh.js.map +1 -0
  31. package/dist/feeds/index.cjs +2 -2
  32. package/dist/feeds/index.js +2 -2
  33. package/dist/feeds/server/index.cjs +5 -5
  34. package/dist/feeds/server/index.js +5 -5
  35. package/dist/files/atomic.d.ts +4 -0
  36. package/dist/files/index.cjs +1 -1
  37. package/dist/files/index.js +1 -1
  38. package/dist/global-config/index.cjs +1 -1
  39. package/dist/google/index.cjs +4 -4
  40. package/dist/google/index.js +3 -3
  41. package/dist/iconGlyph-D8JeNg7R.js +59 -0
  42. package/dist/iconGlyph-D8JeNg7R.js.map +1 -0
  43. package/dist/iconGlyph-fSBp6k4J.cjs +70 -0
  44. package/dist/iconGlyph-fSBp6k4J.cjs.map +1 -0
  45. package/dist/{ingestTypes-CaX7_s-K.cjs → ingestTypes-DsVH9BYy.cjs} +2 -2
  46. package/dist/{ingestTypes-CaX7_s-K.cjs.map → ingestTypes-DsVH9BYy.cjs.map} +1 -1
  47. package/dist/{ingestTypes-CIy1lHjf.js → ingestTypes-GkG7bJdB.js} +2 -2
  48. package/dist/{ingestTypes-CIy1lHjf.js.map → ingestTypes-GkG7bJdB.js.map} +1 -1
  49. package/dist/{itemId-BY2YPBkT.js → itemId-4P9S_ehz.js} +48 -2
  50. package/dist/itemId-4P9S_ehz.js.map +1 -0
  51. package/dist/{itemId-CEQdK01z.cjs → itemId-CGT2J7YK.cjs} +65 -1
  52. package/dist/itemId-CGT2J7YK.cjs.map +1 -0
  53. package/dist/{paths-ClV946_f.cjs → paths-C8-jxR0s.cjs} +1 -2
  54. package/dist/{paths-ClV946_f.cjs.map → paths-C8-jxR0s.cjs.map} +1 -1
  55. package/dist/plugin-vue/IconGlyph.d.ts +27 -0
  56. package/dist/plugin-vue/index.cjs +64 -9
  57. package/dist/plugin-vue/index.cjs.map +1 -1
  58. package/dist/plugin-vue/index.d.ts +1 -0
  59. package/dist/plugin-vue/index.js +65 -11
  60. package/dist/plugin-vue/index.js.map +1 -1
  61. package/dist/{promptSafety-Bp780ezk.js → promptSafety-Bte62OvU.js} +2 -2
  62. package/dist/{promptSafety-Bp780ezk.js.map → promptSafety-Bte62OvU.js.map} +1 -1
  63. package/dist/{promptSafety-C0BWDsfT.cjs → promptSafety-Cv-SoTd4.cjs} +2 -2
  64. package/dist/{promptSafety-C0BWDsfT.cjs.map → promptSafety-Cv-SoTd4.cjs.map} +1 -1
  65. package/dist/{root-BMroU_mB.js → root-BcS4-Kge.js} +12 -2
  66. package/dist/root-BcS4-Kge.js.map +1 -0
  67. package/dist/{root-rPH6FGDT.cjs → root-BvwBUcol.cjs} +12 -2
  68. package/dist/root-BvwBUcol.cjs.map +1 -0
  69. package/dist/{server-CF1qllGV.cjs → server-DXUt34Rt.cjs} +125 -46
  70. package/dist/server-DXUt34Rt.cjs.map +1 -0
  71. package/dist/{server-BvXO49Vj.js → server-DyYwfNB6.js} +120 -47
  72. package/dist/server-DyYwfNB6.js.map +1 -0
  73. package/package.json +9 -9
  74. package/dist/discovery-Dl9nRa-m.cjs.map +0 -1
  75. package/dist/discovery-P64fgtuD.js.map +0 -1
  76. package/dist/itemId-BY2YPBkT.js.map +0 -1
  77. package/dist/itemId-CEQdK01z.cjs.map +0 -1
  78. package/dist/root-BMroU_mB.js.map +0 -1
  79. package/dist/root-rPH6FGDT.cjs.map +0 -1
  80. package/dist/server-BvXO49Vj.js.map +0 -1
  81. package/dist/server-CF1qllGV.cjs.map +0 -1
@@ -128,3 +128,16 @@ Desktop and phone views have **incompatible runtime contracts** — a desktop vi
128
128
  records with an injected token, while a `target: "mobile"` view gets them over a postMessage bridge
129
129
  and cannot `fetch` at all. A view authored against the wrong contract renders empty with no error.
130
130
  Check which target it is registered as before treating it as a rendering fault.
131
+
132
+ ## The narration is not the voice I asked for, or a small tweak re-recorded every beat
133
+
134
+ help: mulmoscript.md
135
+ help: error-recovery.md
136
+
137
+ Speech settings resolve through fallbacks that raise no error, so read the script's own
138
+ `speechParams` block against the speaker reference in `mulmoscript.md` (§ speechParams) before
139
+ treating it as a bug. Three mechanisms explain nearly every report: a `lang` entry replaces the
140
+ whole speaker rather than extending it, each delivery option reaches some providers and is dropped
141
+ by others, and the audio cache key of each beat is built from the speaker's own fields — so editing
142
+ one of them re-records every beat that speaker narrates. Which field is missing decides which
143
+ symptom you are looking at.
@@ -18,6 +18,10 @@ Reach for `presentMulmoScript` when the user asks for a presentation, slideshow,
18
18
  - `markdown` — rich text, tables, mixed content
19
19
  - Do NOT use `imagePrompt` or `moviePrompt` in business presentations
20
20
  - Write concise, professional narration text for each beat (becomes the voiceover)
21
+ - Leave the speaker's `model` out unless the user asks for higher-quality narration
22
+ (`"model": "gemini-2.5-pro-preview-tts"`), and set it before the first render — voice / model /
23
+ `speechOptions` form the audio cache key, so a later change re-records every beat
24
+ (full speaker reference: `config/helps/mulmoscript.md` → speechParams)
21
25
  - Put a 1–2 sentence summary of the whole presentation in the top-level `description` field
22
26
 
23
27
  ## Template
@@ -225,6 +225,41 @@ shipped registry repo's README. Common rejections:
225
225
  - `name` reuses the reserved value `official`.
226
226
  - `name` doesn't match `[A-Za-z0-9][A-Za-z0-9_-]{0,31}`.
227
227
 
228
+ ## A collection's icon shows up as letters, or smears over its neighbours
229
+
230
+ ### Symptoms
231
+
232
+ - A collection / feed / pinned shortcut draws its icon NAME as text
233
+ (`podcasts`, or a fragment like `_A__C`) instead of a glyph.
234
+ - The text spills out of its square and overlaps the buttons next to it
235
+ in the launcher bar.
236
+
237
+ ### Cause + fix
238
+
239
+ `schema.icon` was set to something the Material Symbols font has no
240
+ ligature for. The font matches icons by LIGATURE, so an unknown value
241
+ is simply laid out as ordinary text.
242
+
243
+ Valid names are lowercase letters, digits and underscores only —
244
+ `podcasts`, `rss_feed`, `menu_book`, `3d_rotation`. Capitals, spaces
245
+ and hyphens never match (`Podcasts` and `menu-book` are the usual
246
+ typos). Look the name up in the Material Symbols set before writing it;
247
+ do not invent one.
248
+
249
+ An **emoji is a valid alternative** and is often the better choice: it
250
+ is in colour and stands out among the monochrome glyphs, which is the
251
+ point when several collections would otherwise share a look-alike icon
252
+ (`podcasts` / `rss_feed` / `menu_book`). Set `"icon": "🎙️"` the same way you
253
+ would set a name. Only the first GRAPHEME is drawn — one emoji, whatever
254
+ number of code points it is built from (a variation selector, a skin tone, a
255
+ ZWJ family) — so write exactly one glyph and nothing after it.
256
+
257
+ If the smearing persists after the value is corrected, the surface
258
+ drawing it is bypassing `IconGlyph`
259
+ (`@mulmoclaude/core/plugin-vue`) — every schema-authored icon must go
260
+ through it rather than into a bare
261
+ `<span class="material-symbols-outlined">`.
262
+
228
263
  ## A hand-placed custom role never appears in the list
229
264
 
230
265
  ### Symptoms
@@ -365,6 +400,42 @@ missing key to `.env` (restart the server) or rewrite the script's
365
400
  keys configured. Don't retry the render unchanged — the same provider
366
401
  will fail the same way.
367
402
 
403
+ ## MulmoScript narration — wrong voice, ignored direction, or every beat re-recorded
404
+
405
+ ### Symptoms
406
+
407
+ - A non-default language (`speechParams.speakers.<name>.lang.<code>`) renders
408
+ in a different voice, or fails on a missing `OPENAI_API_KEY` even though the
409
+ script names `gemini` everywhere.
410
+ - `speechOptions.instruction` or `speed` has no audible effect on the narration.
411
+ - A small edit to a speaker re-generates audio for **every** beat, including the
412
+ ones whose text never changed.
413
+
414
+ ### Cause
415
+
416
+ Speech settings resolve through silent fallbacks — the schema does not reject a
417
+ missing field, it fills one in:
418
+
419
+ - A `lang` entry **replaces** the whole speaker; it does not extend it. Whatever
420
+ the entry omits falls back to the schema and provider defaults, so one holding
421
+ only `voiceId` loses `provider` (which then resolves to `openai`), `model`, and
422
+ `speechOptions`.
423
+ - `instruction` is dropped by the `google` provider unless the speaker also sets
424
+ a `model`; `speed` is ignored by `gemini` altogether.
425
+ - Each beat's audio file is cached under a hash of that beat's `text` plus the
426
+ speaker's `voiceId` + `provider` + `model` + `speechOptions`. Editing the
427
+ speaker is a new cache key for every beat that speaks through it — the whole
428
+ script when there is one speaker — so it is re-recorded and re-billed.
429
+
430
+ ### Fix
431
+
432
+ Repeat every field the parent speaker sets — `provider` above all — inside each
433
+ `lang` entry. Express pacing for Gemini through `instruction` rather than
434
+ `speed`. Settle voice, model, and delivery **before** the first render — and when
435
+ a change is genuinely wanted, say up front that every beat of that speaker
436
+ re-records, rather than letting the bill surprise the user. Field-by-field
437
+ reference: `config/helps/mulmoscript.md` → speechParams.
438
+
368
439
  ## Build / yarn workspace ordering
369
440
 
370
441
  ### Symptoms
@@ -15,7 +15,7 @@ A single key unlocks all three capabilities (images, TTS audio, video) — you d
15
15
 
16
16
  ### Audio
17
17
 
18
- - **MulmoScript speech** — `presentMulmoScript` synthesizes speaker voices via Gemini TTS (`gemini-2.5-flash-preview-tts`). This is what turns a storyboard into spoken narration, so the **Storyteller** and **Storyteller Plus** roles become near-complete multimedia pieces.
18
+ - **MulmoScript speech** — `presentMulmoScript` synthesizes speaker voices via Gemini TTS. This is what turns a storyboard into spoken narration, so the **Storyteller** and **Storyteller Plus** roles become near-complete multimedia pieces. Speakers record with whatever the provider's current default is (today `gemini-2.5-flash-preview-tts`) unless the script names a model itself — a speaker can set `"model": "gemini-2.5-pro-preview-tts"` for higher-quality narration at about twice the token price (`config/helps/mulmoscript.md` → speechParams).
19
19
 
20
20
  ### Video
21
21
 
@@ -197,7 +197,77 @@ Configure TTS voices per speaker.
197
197
  }
198
198
  ```
199
199
 
200
- Each speaker supports per-language voice overrides via a `lang` map.
200
+ | Field | Required | Description |
201
+ |---|---|---|
202
+ | `voiceId` | Yes | Provider voice name. Gemini voices are names like `"Kore"`, `"Schedar"`, `"Aoede"` — `config/helps/storyteller.md` lists them by tone |
203
+ | `provider` | No | **Falls back to `openai`, not Gemini** — always write `"provider": "gemini"` |
204
+ | `model` | No | TTS model. Omitted → the provider's default (Gemini: `gemini-2.5-flash-preview-tts`) |
205
+ | `isDefault` | No | Beats with no `speaker` field use this speaker |
206
+ | `displayName` | No | Per-language label for the speaker |
207
+ | `speechOptions` | No | How the line is delivered — see below |
208
+ | `lang` | No | Per-language speaker **replacement** — see below |
209
+
210
+ Nothing here is validated against the provider: `model` is a free-form string,
211
+ and a missing `provider` / `model` is not rejected when the script is parsed — it
212
+ resolves to a default, which may then be wrong at render time or merely audible.
213
+ Set them deliberately rather than relying on the fallback.
214
+
215
+ ### Choosing a model
216
+
217
+ Gemini TTS accepts two models:
218
+
219
+ | Model | Cost | Use when |
220
+ |---|---|---|
221
+ | `gemini-2.5-flash-preview-tts` (default) | 1× | Everything, by default — leave `model` out |
222
+ | `gemini-2.5-pro-preview-tts` | ~2× the token price | The user asks for higher-quality or more expressive narration |
223
+
224
+ **Decide before the first render.** Each beat's audio file is cached under a hash
225
+ of that beat's `text` plus the speaker's `voiceId` + `provider` + `model` +
226
+ `speechOptions`. Rewriting one beat's text therefore re-records that beat alone,
227
+ but touching the speaker changes the key of **every beat that speaks through it**
228
+ — in a single-speaker script, the whole thing, billed again. Leave `model` out to
229
+ track the provider default; add it only when you mean to pin a different model.
230
+
231
+ ### `speechOptions`
232
+
233
+ `beats[].speechOptions` merges over the speaker's for that one beat, so a single
234
+ line can be delivered differently without touching the speaker.
235
+
236
+ | Option | Honored by | Notes |
237
+ |---|---|---|
238
+ | `instruction` | `gemini`, `openai` | Direction for delivery, sent as DIRECTOR'S NOTES ahead of the text. Under `google` it is **dropped unless `model` is set** |
239
+ | `speed` | `openai`, `google`, `elevenlabs` | **Silently ignored by `gemini`** — ask for the pace in `instruction` instead |
240
+
241
+ (`stability` / `similarity_boost` belong to `elevenlabs` and `decoration` to
242
+ `kotodama`; neither provider is configured in this app.)
243
+
244
+ ### `lang` — a replacement, not an override
245
+
246
+ A `lang` entry **replaces the whole speaker** for that language: whatever it
247
+ omits falls back to the schema and provider defaults, never to the parent's
248
+ value. Repeat every field the parent sets — `provider` above all, since an entry
249
+ holding only `voiceId` resolves to the `openai` provider and then fails on a
250
+ missing `OPENAI_API_KEY` (or rejects the Gemini voice name) even though the
251
+ script names Gemini everywhere.
252
+
253
+ ```json
254
+ {
255
+ "speakers": {
256
+ "Presenter": {
257
+ "provider": "gemini",
258
+ "voiceId": "Kore",
259
+ "speechOptions": { "instruction": "Warm and unhurried." },
260
+ "lang": {
261
+ "ja": {
262
+ "provider": "gemini",
263
+ "voiceId": "Kore",
264
+ "speechOptions": { "instruction": "Warm and unhurried." }
265
+ }
266
+ }
267
+ }
268
+ }
269
+ }
270
+ ```
201
271
 
202
272
  ## audioParams
203
273
 
@@ -26,7 +26,10 @@ override the generic guidance above:
26
26
  - **Tool**: hand the finished JSON to the `presentMulmoScript` tool — that is how a deck is rendered here.
27
27
  - **Narration / TTS**: declare a speaker once in `speechParams.speakers` with `isDefault: true` and
28
28
  the **Google** provider (`"provider": "gemini", "voiceId": "Kore"`). Beats then inherit it without
29
- repeating `speaker`. Both samples below include this block — keep it.
29
+ repeating `speaker`. Both samples below include this block — keep it. Leave `model` out unless the
30
+ user asks for higher-quality narration (`"model": "gemini-2.5-pro-preview-tts"`), and set it before
31
+ the first render — voice / model / `speechOptions` are part of the audio cache key, so a later change
32
+ re-records every beat. Full speaker reference: `config/helps/mulmoscript.md` → speechParams.
30
33
  - **Providers are Google-only.** This app configures only Google providers; never emit `openai`,
31
34
  `elevenlabs`, etc. Neither approach generates AI images or video, so `imageParams` / `movieParams`
32
35
  are not needed (the `slide` type renders static layouts; `html_tailwind` + `animation` renders from
@@ -31,6 +31,13 @@ Set `speechOptions.instruction` on the Narrator speaker to match the story's ton
31
31
  | Deep / authoritative | Alnilam, Iapetus, Algieba |
32
32
  | Soft / gentle | Aoede, Umbriel, Laomedeia, Achernar, Rasalgethi, Pulcherrima, Vindemiatrix, Sadachbia, Sadaltager, Zubenelgenubi |
33
33
 
34
+ Leave `model` out — the speaker then records with Gemini's default TTS model. Add
35
+ `"model": "gemini-2.5-pro-preview-tts"` only when the user asks for higher-quality
36
+ narration, and add it **before the first render**: voice, model, and
37
+ `speechOptions` are part of the audio cache key, so changing one later re-records
38
+ every beat. Full speaker reference (including the `lang` map, which *replaces* the
39
+ speaker rather than extending it): `config/helps/mulmoscript.md` → speechParams.
40
+
34
41
  ## Other Rules
35
42
 
36
43
  - Always use Google providers (`gemini` for TTS, `google` for image generation).
@@ -0,0 +1,23 @@
1
+ /** The colours a schema may name, ordered around the hue circle.
2
+ *
3
+ * Deliberately EXCLUDES the warm band (red / orange / amber): those are the
4
+ * notification severities (`ENUM_ALERT` red, `ENUM_NUDGE` amber in
5
+ * `enumColors.ts`) and the launcher's own session badges, so an accent can
6
+ * never make a shortcut read as "something needs attention".
7
+ *
8
+ * Seven rather than eight: rendering all eight candidates on one identical
9
+ * glyph put `cyan` squarely between `sky` and `teal`, so a pill could hold
10
+ * three shortcuts in the same blue-green band. A colour that cannot be told
11
+ * from its neighbour identifies nothing, which is the entire job here — the
12
+ * set is sized for separation, not for count. */
13
+ export declare const ACCENT_COLORS: readonly ["violet", "indigo", "sky", "teal", "emerald", "lime", "fuchsia"];
14
+ export type AccentColor = (typeof ACCENT_COLORS)[number];
15
+ /** Narrowing guard: is `value` one of the colours a schema may name? */
16
+ export declare function isAccentColor(value: unknown): value is AccentColor;
17
+ /** The chip classes for `color`, or `null` when there is no accent to draw —
18
+ * absent, blank, or a name this palette does not carry.
19
+ *
20
+ * Unknown names fail soft rather than throwing, matching how `icon` is
21
+ * handled: a schema is user/LLM-authored, and a typo should cost the colour,
22
+ * not the surface. The caller falls back to its own unstyled treatment. */
23
+ export declare function accentChipClasses(color: string | undefined): string | null;
@@ -0,0 +1,26 @@
1
+ /** A resolved icon value: either a ligature name for the icon font, or a
2
+ * literal glyph (emoji, a single letter) drawn as plain text. */
3
+ export type IconGlyph = {
4
+ kind: "symbol";
5
+ name: string;
6
+ } | {
7
+ kind: "glyph";
8
+ text: string;
9
+ };
10
+ /** Drawn when neither the value nor the surface's own fallback says anything.
11
+ * Without it a blank `fallback` resolves to an EMPTY glyph and the surface
12
+ * renders a hole — this function promises a drawable result for every input,
13
+ * so the promise needs a floor. */
14
+ export declare const DEFAULT_ICON = "dataset";
15
+ /** Classify a schema-authored icon value.
16
+ *
17
+ * `raw` empty / blank / absent falls back to `fallback` (the per-surface
18
+ * default such as `"dataset"`), and a blank `fallback` in turn to
19
+ * {@link DEFAULT_ICON}. Both are classified by the same rule — so a caller
20
+ * can never smuggle an unrenderable default past the guard, and the result
21
+ * is always something drawable.
22
+ *
23
+ * A non-name value is cut to ONE grapheme. That bounds the character count,
24
+ * not the rendered width — see the containment note in
25
+ * `plugin-vue/IconGlyph.ts` for the other half. */
26
+ export declare function resolveIconGlyph(raw: string | undefined, fallback: string): IconGlyph;
@@ -186,6 +186,15 @@ export interface CollectionSummary {
186
186
  slug: string;
187
187
  title: string;
188
188
  icon: string;
189
+ /** Optional accent colour name (see `ACCENT_COLORS`), drawn as a pale chip
190
+ * behind the launcher glyph. Absent when the schema names none.
191
+ *
192
+ * Declared NARROW (no `| undefined`), like its `appId` / `readonly`
193
+ * siblings: the remote-host handlers pass these shapes through `Jsonify`,
194
+ * which drops `undefined` from a member union, so a widened optional stops
195
+ * being assignable there. Producers therefore ADD the key conditionally
196
+ * rather than assigning `undefined` to it. */
197
+ color?: string;
189
198
  source: CollectionSource;
190
199
  /** Present (true) when the collection is backed by an external
191
200
  * `dataSource` and therefore read-only in every UI/tool write path.
@@ -693,6 +693,15 @@ export declare const StorageZ: z.ZodDiscriminatedUnion<[z.ZodObject<{
693
693
  * this parses to, and nothing narrower. */
694
694
  declare const CollectionObjectZ: z.ZodObject<{
695
695
  title: z.ZodString;
696
+ color: z.ZodCatch<z.ZodOptional<z.ZodEnum<{
697
+ violet: "violet";
698
+ indigo: "indigo";
699
+ sky: "sky";
700
+ teal: "teal";
701
+ emerald: "emerald";
702
+ lime: "lime";
703
+ fuchsia: "fuchsia";
704
+ }>>>;
696
705
  icon: z.ZodString;
697
706
  dataPath: z.ZodOptional<z.ZodString>;
698
707
  dataSource: z.ZodOptional<z.ZodObject<{
@@ -1175,6 +1184,15 @@ declare const CollectionObjectZ: z.ZodObject<{
1175
1184
  export type CollectionSchemaInput = z.infer<typeof CollectionObjectZ>;
1176
1185
  export declare const CollectionSchemaZ: z.ZodPreprocess<z.ZodObject<{
1177
1186
  title: z.ZodString;
1187
+ color: z.ZodCatch<z.ZodOptional<z.ZodEnum<{
1188
+ violet: "violet";
1189
+ indigo: "indigo";
1190
+ sky: "sky";
1191
+ teal: "teal";
1192
+ emerald: "emerald";
1193
+ lime: "lime";
1194
+ fuchsia: "fuchsia";
1195
+ }>>>;
1178
1196
  icon: z.ZodString;
1179
1197
  dataPath: z.ZodOptional<z.ZodString>;
1180
1198
  dataSource: z.ZodOptional<z.ZodObject<{
@@ -1653,5 +1671,5 @@ export declare const CollectionSchemaZ: z.ZodPreprocess<z.ZodObject<{
1653
1671
  }, z.core.$strip>>;
1654
1672
  fallback: z.ZodOptional<z.ZodString>;
1655
1673
  }, z.core.$strip>>;
1656
- }, z.core.$strip>>;
1674
+ }, z.core.$strip>, unknown>;
1657
1675
  export {};
@@ -0,0 +1,14 @@
1
+ import { CollectionShortcutInfo } from './uiTypes';
2
+ /** The fields an index row supplies. Both `CollectionSummary` and
3
+ * `FeedSummary` satisfy it; `icon` is optional because a feed row may carry
4
+ * an empty one and rely on the surface's default. */
5
+ export interface ShortcutInfoSource {
6
+ slug: string;
7
+ title: string;
8
+ icon?: string | undefined;
9
+ color?: string | undefined;
10
+ }
11
+ /** An index row as shortcut-reconcile input. `fallbackIcon` stands in when the
12
+ * row names no icon (feeds do this); `color` is omitted entirely rather than
13
+ * set to `undefined` when the row has none. */
14
+ export declare function toShortcutInfo(row: ShortcutInfoSource, fallbackIcon: string): CollectionShortcutInfo;
@@ -122,6 +122,9 @@ export interface FeedSummary {
122
122
  slug: string;
123
123
  title: string;
124
124
  icon: string;
125
+ /** Optional accent colour name (see `ACCENT_COLORS`), drawn as a pale chip
126
+ * behind the launcher glyph. Absent when the schema names none. */
127
+ color?: string;
125
128
  kind: string;
126
129
  schedule: string;
127
130
  lastFetchedAt: string | null;
@@ -136,4 +139,7 @@ export interface CollectionShortcutInfo {
136
139
  slug: string;
137
140
  title: string;
138
141
  icon: string;
142
+ /** Optional accent colour name (see `ACCENT_COLORS`), drawn as a pale chip
143
+ * behind the launcher glyph. Absent when the schema names none. */
144
+ color?: string;
139
145
  }
@@ -1,8 +1,9 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
2
  const require_dist = require("../dist-CsgSfWwR.cjs");
3
- const require_itemId = require("../itemId-CEQdK01z.cjs");
4
- const require_promptSafety = require("../promptSafety-C0BWDsfT.cjs");
3
+ const require_itemId = require("../itemId-CGT2J7YK.cjs");
4
+ const require_promptSafety = require("../promptSafety-Cv-SoTd4.cjs");
5
5
  const require_project = require("../project-C1ep9pvo.cjs");
6
+ const require_iconGlyph = require("../iconGlyph-fSBp6k4J.cjs");
6
7
  //#region src/collection/core/presentCollection.ts
7
8
  var TOOL_NAME = "presentCollection";
8
9
  /** Stamp a host's opaque project scope onto a card payload. The ONLY way a
@@ -365,6 +366,19 @@ function skillCommandSeed(slug, message, itemId) {
365
366
  return itemId ? `/${slug} id=${itemId} ${message}` : `/${slug} ${message}`;
366
367
  }
367
368
  //#endregion
369
+ //#region src/collection/core/shortcutInfo.ts
370
+ /** An index row as shortcut-reconcile input. `fallbackIcon` stands in when the
371
+ * row names no icon (feeds do this); `color` is omitted entirely rather than
372
+ * set to `undefined` when the row has none. */
373
+ function toShortcutInfo(row, fallbackIcon) {
374
+ return {
375
+ slug: row.slug,
376
+ title: row.title,
377
+ icon: row.icon && row.icon.length > 0 ? row.icon : fallbackIcon,
378
+ ...row.color === void 0 ? {} : { color: row.color }
379
+ };
380
+ }
381
+ //#endregion
368
382
  //#region src/collection/core/sortItems.ts
369
383
  var EMPTY = { empty: true };
370
384
  /** Field types that render no value text in the table, so offer no sort. */
@@ -635,8 +649,10 @@ var buildOntologyGraph = (entries) => {
635
649
  };
636
650
  };
637
651
  //#endregion
652
+ exports.ACCENT_COLORS = require_itemId.ACCENT_COLORS;
638
653
  exports.AGENT_INGEST_KIND = require_itemId.AGENT_INGEST_KIND;
639
654
  exports.COMPUTED_TYPES = require_itemId.COMPUTED_TYPES;
655
+ exports.DEFAULT_ICON = require_iconGlyph.DEFAULT_ICON;
640
656
  exports.ENUM_ALERT = ENUM_ALERT;
641
657
  exports.ENUM_NEUTRAL = ENUM_NEUTRAL;
642
658
  exports.ENUM_NUDGE = ENUM_NUDGE;
@@ -647,6 +663,7 @@ exports.SAFE_RECORD_ID_PATTERN = require_itemId.SAFE_RECORD_ID_PATTERN;
647
663
  exports.SAFE_SLUG_PATTERN = require_itemId.SAFE_SLUG_PATTERN;
648
664
  exports.TOOL_DEFINITION = TOOL_DEFINITION;
649
665
  exports.TOOL_NAME = TOOL_NAME;
666
+ exports.accentChipClasses = require_itemId.accentChipClasses;
650
667
  exports.actionVisible = require_promptSafety.actionVisible;
651
668
  exports.agentActionRunKey = require_promptSafety.agentActionRunKey;
652
669
  exports.assignLanes = require_itemId.assignLanes;
@@ -694,6 +711,7 @@ exports.firstMissingRequiredField = firstMissingRequiredField;
694
711
  exports.firstUnknownDefault = require_promptSafety.firstUnknownDefault;
695
712
  exports.flagFieldValue = require_promptSafety.flagFieldValue;
696
713
  exports.generateUniqueId = require_itemId.generateUniqueId;
714
+ exports.isAccentColor = require_itemId.isAccentColor;
697
715
  exports.isCanonicalServerTime = require_itemId.isCanonicalServerTime;
698
716
  exports.isFieldDrivenEvery = require_itemId.isFieldDrivenEvery;
699
717
  exports.isLocalCollectionKey = require_itemId.isLocalCollectionKey;
@@ -725,6 +743,7 @@ exports.projectRecordFields = require_project.projectRecordFields;
725
743
  exports.recordSpan = require_itemId.recordSpan;
726
744
  exports.resolveEnumColor = resolveEnumColor;
727
745
  exports.resolveIcon = require_promptSafety.resolveIcon;
746
+ exports.resolveIconGlyph = require_iconGlyph.resolveIconGlyph;
728
747
  exports.resolveRowRefs = require_promptSafety.resolveRowRefs;
729
748
  exports.rollupValue = require_promptSafety.rollupValue;
730
749
  exports.rowFromItem = rowFromItem;
@@ -746,6 +765,7 @@ exports.spanCoversDay = require_itemId.spanCoversDay;
746
765
  exports.storageKindFor = require_itemId.storageKindFor;
747
766
  exports.stringSortValue = stringSortValue;
748
767
  exports.toPresentCollectionData = toPresentCollectionData;
768
+ exports.toShortcutInfo = toShortcutInfo;
749
769
  exports.toggleChecked = require_promptSafety.toggleChecked;
750
770
  exports.uniqueBacklinkSources = require_promptSafety.uniqueBacklinkSources;
751
771
  exports.uniqueEmbedTargets = require_promptSafety.uniqueEmbedTargets;