@guuey/chat 0.16.8 → 0.16.10

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/src/theme.ts CHANGED
@@ -34,6 +34,12 @@ export const GuueyChatPalette = z
34
34
  canvas: z.string(),
35
35
  canvasMuted: z.string(),
36
36
  error: z.string(),
37
+ /**
38
+ * Anchor color (guuey#528) — OPTIONAL by the evolution rule (a stored
39
+ * pre-#528 theme must keep passing the strict write gate); the default
40
+ * theme states it, so RESOLVED themes always carry it.
41
+ */
42
+ link: z.string().optional(),
37
43
  })
38
44
  .loose();
39
45
  export type GuueyChatPalette = z.infer<typeof GuueyChatPalette>;
@@ -122,6 +128,14 @@ export const GuueyChatTheme = z
122
128
  density: z.enum(["compact", "comfortable"]),
123
129
  })
124
130
  .loose(),
131
+ /**
132
+ * Per-court override DOCUMENTS (guuey#519), keyed by serving court —
133
+ * declared vocabulary (guuey#536: the manifest grammar mirrors this
134
+ * schema key-for-key, so the member must be stated, not passthrough).
135
+ * Entries are partial theme documents; `resolveCourtTheme` owns the
136
+ * per-token layering and resolved themes are always court-free.
137
+ */
138
+ courts: z.record(z.string(), z.unknown()).optional(),
125
139
  })
126
140
  .loose();
127
141
  export type GuueyChatTheme = z.infer<typeof GuueyChatTheme>;
@@ -130,12 +144,20 @@ export type GuueyChatTheme = z.infer<typeof GuueyChatTheme>;
130
144
  * The brand-neutral-but-polished package default — the theme a builder gets
131
145
  * before configuring anything, and the per-token fallback floor every other
132
146
  * theme resolves against.
147
+ *
148
+ * The accent is MONOCHROME (= ink) by founder ruling (guuey#521): an
149
+ * unthemed embed must never carry a foreign accent into a host's product —
150
+ * the old `#2f6bff` blue made every zero-config embed read "off-the-shelf
151
+ * chat vendor" inside someone else's brand (#414's lesson, mirrored). Ink
152
+ * as accent means the send button and user pill render as neutral
153
+ * ink-on-canvas and disappear into any host; a brand accent is a CHOICE
154
+ * (theme prop or one `--guuey-chat-accent` CSS variable), never a default.
133
155
  */
134
156
  export const DEFAULT_CHAT_THEME: GuueyChatTheme = {
135
157
  name: "default",
136
158
  colors: {
137
159
  light: {
138
- accent: "#2f6bff",
160
+ accent: "#111318",
139
161
  onAccent: "#ffffff",
140
162
  ink: "#111318",
141
163
  inkMuted: "#5b6270",
@@ -143,9 +165,10 @@ export const DEFAULT_CHAT_THEME: GuueyChatTheme = {
143
165
  canvas: "#f7f7f5",
144
166
  canvasMuted: "#eceded",
145
167
  error: "#d64545",
168
+ link: "#111318",
146
169
  },
147
170
  dark: {
148
- accent: "#5c8dff",
171
+ accent: "#e8e9ee",
149
172
  onAccent: "#0b0d12",
150
173
  ink: "#e8e9ee",
151
174
  inkMuted: "#9aa0ac",
@@ -153,6 +176,7 @@ export const DEFAULT_CHAT_THEME: GuueyChatTheme = {
153
176
  canvas: "#0f1116",
154
177
  canvasMuted: "#1b1e26",
155
178
  error: "#ff6b6b",
179
+ link: "#e8e9ee",
156
180
  },
157
181
  },
158
182
  typography: {},
@@ -178,6 +202,9 @@ export const GUUEY_CHAT_THEME: GuueyChatTheme = {
178
202
  canvas: "#f6f5ee",
179
203
  canvasMuted: "#ecebe0",
180
204
  error: "#ff5b5b",
205
+ // slime-4, the founder-minted brand link green (guuey#528, 08-30) —
206
+ // the ladder had no readable green at link weight on light.
207
+ link: "#4e7a0e",
181
208
  },
182
209
  dark: {
183
210
  accent: "#b8ff3a",
@@ -188,6 +215,9 @@ export const GUUEY_CHAT_THEME: GuueyChatTheme = {
188
215
  canvas: "#0e1014",
189
216
  canvasMuted: "#1a1d24",
190
217
  error: "#ff5b5b",
218
+ // On dark the ladder's own slime IS readable at link weight — the
219
+ // #528 ruling minted slime-4 for LIGHT, where #b8ff3a is not.
220
+ link: "#b8ff3a",
191
221
  },
192
222
  },
193
223
  typography: {},
@@ -221,6 +251,7 @@ const PALETTE_TOKENS = [
221
251
  "canvas",
222
252
  "canvasMuted",
223
253
  "error",
254
+ "link",
224
255
  ] as const;
225
256
 
226
257
  function mergePalette(
@@ -269,6 +300,44 @@ export function resolveTheme(
269
300
  };
270
301
  }
271
302
 
303
+ /**
304
+ * The court-override member a theme DOCUMENT may carry (guuey#519).
305
+ * Values are theme documents themselves (validated by `resolveTheme`'s own
306
+ * lenient parse at resolution time, so a partial or future-schema court
307
+ * entry degrades per-token like any stored theme).
308
+ */
309
+ const CourtOverrides = z
310
+ .object({ courts: z.record(z.string(), z.unknown()).optional() })
311
+ .loose();
312
+
313
+ /**
314
+ * Per-court theme resolution (guuey#519 — the #414 rule generalized).
315
+ *
316
+ * A theme document may carry `courts`: explicit per-court override
317
+ * documents keyed by serving court (`"guuey"`, `"ggui"`, …). A surface
318
+ * resolves ITS court: the court's override resolved per-token OVER the
319
+ * resolved base document when declared, else the base alone. **Brand is
320
+ * never a default** — an undeclared or unknown court gets the neutral
321
+ * base by construction, and readers that never heard of `courts`
322
+ * (lenient parsers projecting only known tokens) keep reading the base
323
+ * untouched, so no court can inherit another court's brand.
324
+ *
325
+ * Resolved themes are court-free: `courts` never survives resolution.
326
+ * NEVER throws; unparseable input degrades exactly as `resolveTheme`.
327
+ */
328
+ export function resolveCourtTheme(
329
+ candidate: unknown,
330
+ court: string,
331
+ base: GuueyChatTheme = DEFAULT_CHAT_THEME,
332
+ ): GuueyChatTheme {
333
+ const resolvedBase = resolveTheme(candidate, base);
334
+ const parsed = CourtOverrides.safeParse(candidate);
335
+ if (!parsed.success) return resolvedBase;
336
+ const override = parsed.data.courts?.[court];
337
+ if (override === undefined) return resolvedBase;
338
+ return resolveTheme(override, resolvedBase);
339
+ }
340
+
272
341
  /** Slot-level merge of two ramp statements (candidate slots win per slot). */
273
342
  function mergeRampSet(
274
343
  base: GuueyChatRampSet | undefined,
package/src/types.ts CHANGED
@@ -540,7 +540,20 @@ export type ChatDebugEvent =
540
540
  * is by construction, not a failure). Debug-only like every event here
541
541
  * — calm end-user UX keeps deny == miss un-oracled.
542
542
  */
543
- | { type: "locator-read-miss"; key: ItemKey; resourceUri: string; origin: "live" | "history" };
543
+ | { type: "locator-read-miss"; key: ItemKey; resourceUri: string; origin: "live" | "history" }
544
+ /**
545
+ * `clearConversation()`'s server-erasure outcome (guuey#526). The clear
546
+ * itself is ALWAYS local-first and never blocks on this — the contract's
547
+ * unlinkability fallback says a denied/failed delete still clears the
548
+ * device and surfaces "nothing louder than a debug line"; this event IS
549
+ * that line. `"skipped"` = no platform door to call (no `apiBaseUrl`,
550
+ * or no thread ever minted) — the local-only clear, by construction.
551
+ */
552
+ | {
553
+ type: "thread-delete";
554
+ threadId: string | null;
555
+ outcome: "deleted" | "denied" | "failed" | "skipped";
556
+ };
544
557
 
545
558
  /**
546
559
  * One renderable view the plan saw, BEFORE any chips/promotion pass —