@agent-native/core 0.141.5 → 0.141.6

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 (43) hide show
  1. package/corpus/README.md +1 -1
  2. package/corpus/templates/analytics/.agents/skills/data-querying/SKILL.md +13 -6
  3. package/corpus/templates/analytics/actions/get-first-party-analytics-health.ts +15 -1
  4. package/corpus/templates/analytics/actions/query-agent-native-analytics.ts +2 -2
  5. package/corpus/templates/analytics/app/i18n-data.ts +328 -0
  6. package/corpus/templates/analytics/app/pages/DataSources.tsx +66 -30
  7. package/corpus/templates/analytics/changelog/2026-08-05-historical-analytics-rollups-now-repair-automatically-after-.md +6 -0
  8. package/corpus/templates/analytics/netlify.toml +4 -0
  9. package/corpus/templates/analytics/scripts/emit-netlify-dashboard-report-cron.ts +109 -0
  10. package/corpus/templates/analytics/server/jobs/analytics-rollup-backfill.ts +342 -0
  11. package/corpus/templates/analytics/server/lib/analytics-rollup-lock.ts +8 -0
  12. package/corpus/templates/analytics/server/lib/dashboard-catalog.ts +11 -29
  13. package/corpus/templates/analytics/server/lib/dashboards-store.ts +55 -0
  14. package/corpus/templates/analytics/server/lib/first-party-analytics-health.ts +117 -31
  15. package/corpus/templates/analytics/server/lib/first-party-analytics-rollups.ts +15 -9
  16. package/corpus/templates/analytics/server/lib/first-party-analytics.ts +5 -16
  17. package/corpus/templates/analytics/server/plugins/analytics-rollup-backfill-jobs.ts +61 -0
  18. package/corpus/templates/analytics/server/plugins/db.ts +58 -187
  19. package/corpus/templates/analytics/server/routes/api/analytics-rollups/backfill.post.ts +53 -0
  20. package/corpus/templates/clips/changelog/2026-08-05-clips-overlays-now-use-clean-surfaces-without-clipped-shadow.md +6 -0
  21. package/corpus/templates/clips/desktop/src/overlays/meeting-notification.tsx +2 -2
  22. package/corpus/templates/clips/desktop/src/styles.css +3 -54
  23. package/corpus/templates/clips/desktop/src-tauri/src/notifications.rs +3 -3
  24. package/corpus/templates/design/actions/get-design-system.ts +3 -1
  25. package/corpus/templates/design/changelog/2026-08-05-missing-design-systems-now-return-a-clear-not-found-response.md +6 -0
  26. package/corpus/templates/slides/app/components/deck/DeckCard.tsx +2 -2
  27. package/corpus/templates/slides/app/components/editor/SlideEditor.tsx +1 -1
  28. package/corpus/templates/slides/app/i18n/en-US.ts +5 -5
  29. package/corpus/templates/slides/changelog/2026-08-05-new-presentation-prompts-now-clearly-describe-presentation-g.md +6 -0
  30. package/dist/client/AssistantChat.js +1 -1
  31. package/dist/client/chat/run-recovery.d.ts +2 -1
  32. package/dist/client/chat/run-recovery.js +10 -3
  33. package/dist/client/error-format.d.ts +1 -0
  34. package/dist/client/error-format.js +4 -2
  35. package/dist/collab/struct-routes.d.ts +1 -1
  36. package/dist/observability/routes.d.ts +3 -3
  37. package/dist/progress/routes.d.ts +1 -1
  38. package/dist/resources/handlers.d.ts +1 -1
  39. package/dist/secrets/routes.d.ts +3 -3
  40. package/dist/server/agent-engine-api-key-route.d.ts +1 -1
  41. package/package.json +1 -1
  42. package/corpus/templates/analytics/changelog/2026-08-05-historical-analytics-rollups-stay-complete-and-atomic.md +0 -6
  43. package/corpus/templates/analytics/changelog/2026-08-05-historical-rollups-stay-complete-while-live-events-arrive.md +0 -6
@@ -1,9 +1,7 @@
1
1
  import {
2
2
  deferMigration,
3
3
  ensureAdditiveColumns,
4
- getDialect,
5
4
  getDbExec,
6
- isPostgres,
7
5
  runMigrations,
8
6
  } from "@agent-native/core/db";
9
7
  import { isInBackgroundFunctionRuntime } from "@agent-native/core/server";
@@ -12,7 +10,7 @@ import { isInBackgroundFunctionRuntime } from "@agent-native/core/server";
12
10
  // startup so the dashboard / analysis share actions know where to dispatch.
13
11
  import "../db/index.js";
14
12
  import * as schema from "../db/schema.js";
15
- import { FIRST_PARTY_ANALYTICS_ROLLUP_LOCK_KEY } from "../lib/first-party-analytics-rollups.js";
13
+ import { isHistoricalAnalyticsRollupBackfillComplete } from "../jobs/analytics-rollup-backfill.js";
16
14
  import { repairPersistedFirstPartyDashboardQueries } from "../lib/first-party-dashboard-repair.js";
17
15
 
18
16
  /**
@@ -33,182 +31,6 @@ function isDrizzleTable(value: unknown): value is object {
33
31
 
34
32
  const schemaTables = Object.values(schema).filter(isDrizzleTable);
35
33
 
36
- const ANALYTICS_ROLLUP_BACKFILL_STATEMENTS = {
37
- postgres: [
38
- `
39
- INSERT INTO analytics_event_daily_rollups (
40
- id, tenant_key, owner_email, org_id, event_date, event_name,
41
- app, template, event_count
42
- )
43
- SELECT
44
- md5(random()::text || clock_timestamp()::text), tenant_key,
45
- MIN(owner_email), MIN(org_id), event_date, event_name, app, template,
46
- COUNT(*)::INTEGER
47
- FROM (
48
- SELECT
49
- CASE
50
- WHEN org_id IS NOT NULL AND org_id <> '' THEN 'org:' || org_id
51
- ELSE 'user:' || owner_email
52
- END AS tenant_key,
53
- owner_email,
54
- org_id,
55
- COALESCE(NULLIF(event_date, ''), substr(timestamp, 1, 10)) AS event_date,
56
- event_name,
57
- COALESCE(app, '') AS app,
58
- COALESCE(template, '') AS template
59
- FROM analytics_events
60
- ) AS historical_events
61
- WHERE event_date <> ''
62
- GROUP BY tenant_key, event_date, event_name, app, template
63
- ON CONFLICT (tenant_key, event_date, event_name, app, template)
64
- DO UPDATE SET event_count = GREATEST(
65
- analytics_event_daily_rollups.event_count,
66
- EXCLUDED.event_count
67
- )
68
- `,
69
- `
70
- INSERT INTO analytics_user_days (
71
- id, tenant_key, owner_email, org_id, event_date, user_key
72
- )
73
- SELECT
74
- md5(random()::text || clock_timestamp()::text), tenant_key,
75
- owner_email, org_id, event_date, user_key
76
- FROM (
77
- SELECT DISTINCT
78
- CASE
79
- WHEN org_id IS NOT NULL AND org_id <> '' THEN 'org:' || org_id
80
- ELSE 'user:' || owner_email
81
- END AS tenant_key,
82
- owner_email,
83
- org_id,
84
- COALESCE(NULLIF(event_date, ''), substr(timestamp, 1, 10)) AS event_date,
85
- user_key
86
- FROM analytics_events
87
- WHERE user_key IS NOT NULL AND TRIM(user_key) <> ''
88
- ) AS historical_user_days
89
- WHERE event_date <> ''
90
- ON CONFLICT (tenant_key, event_date, user_key) DO NOTHING
91
- `,
92
- ],
93
- sqlite: [
94
- `
95
- INSERT INTO analytics_event_daily_rollups (
96
- id, tenant_key, owner_email, org_id, event_date, event_name,
97
- app, template, event_count
98
- )
99
- SELECT
100
- lower(hex(randomblob(16))), tenant_key, MIN(owner_email), MIN(org_id),
101
- event_date, event_name, app, template, COUNT(*)
102
- FROM (
103
- SELECT
104
- CASE
105
- WHEN org_id IS NOT NULL AND org_id <> '' THEN 'org:' || org_id
106
- ELSE 'user:' || owner_email
107
- END AS tenant_key,
108
- owner_email,
109
- org_id,
110
- COALESCE(NULLIF(event_date, ''), substr(timestamp, 1, 10)) AS event_date,
111
- event_name,
112
- COALESCE(app, '') AS app,
113
- COALESCE(template, '') AS template
114
- FROM analytics_events
115
- ) AS historical_events
116
- WHERE event_date <> ''
117
- GROUP BY tenant_key, event_date, event_name, app, template
118
- ON CONFLICT (tenant_key, event_date, event_name, app, template)
119
- DO UPDATE SET event_count = excluded.event_count
120
- `,
121
- `
122
- INSERT INTO analytics_user_days (
123
- id, tenant_key, owner_email, org_id, event_date, user_key
124
- )
125
- SELECT
126
- lower(hex(randomblob(16))), tenant_key, owner_email, org_id,
127
- event_date, user_key
128
- FROM (
129
- SELECT DISTINCT
130
- CASE
131
- WHEN org_id IS NOT NULL AND org_id <> '' THEN 'org:' || org_id
132
- ELSE 'user:' || owner_email
133
- END AS tenant_key,
134
- owner_email,
135
- org_id,
136
- COALESCE(NULLIF(event_date, ''), substr(timestamp, 1, 10)) AS event_date,
137
- user_key
138
- FROM analytics_events
139
- WHERE user_key IS NOT NULL AND TRIM(user_key) <> ''
140
- ) AS historical_user_days
141
- WHERE event_date <> ''
142
- ON CONFLICT (tenant_key, event_date, user_key) DO NOTHING
143
- `,
144
- ],
145
- } as const;
146
-
147
- async function runHistoricalAnalyticsRollupBackfill() {
148
- // Durable agent workers share this plugin bundle with the regular server,
149
- // but they must not become a second migration runner for an expensive
150
- // historical scan. The keep-warm/regular server path owns this work.
151
- if (isInBackgroundFunctionRuntime()) {
152
- console.info(
153
- "[db] Deferring historical Analytics rollup backfill from durable background runtime",
154
- );
155
- return deferMigration();
156
- }
157
-
158
- const db = getDbExec();
159
-
160
- if (isPostgres()) {
161
- if (!db.transaction) {
162
- throw new Error(
163
- "Analytics rollup backfill requires a Postgres transaction",
164
- );
165
- }
166
-
167
- const result = await db.transaction(async (tx) => {
168
- // Only one serverless instance should perform the expensive scan. A
169
- // A try-lock keeps competing cold starts from holding pooled Neon
170
- // connections while waiting for the winner. A lock loser defers without
171
- // recording the migration so a failed winner can be retried safely.
172
- // The monotonic rollup upsert below keeps a live increment from being
173
- // overwritten if ingest commits during the historical snapshot. Live
174
- // ingest takes this same lock before inserting its raw event.
175
- const lockResult = await tx.execute({
176
- sql: "SELECT pg_try_advisory_xact_lock(hashtextextended(?, 0::bigint)) AS acquired",
177
- args: [FIRST_PARTY_ANALYTICS_ROLLUP_LOCK_KEY],
178
- });
179
- const acquired = lockResult.rows[0]?.acquired;
180
- if (acquired !== true && acquired !== "t") {
181
- console.info(
182
- "[db] Deferring historical Analytics rollup backfill; another instance owns the advisory lock",
183
- );
184
- return deferMigration();
185
- }
186
- for (const statement of ANALYTICS_ROLLUP_BACKFILL_STATEMENTS.postgres) {
187
- await tx.execute(statement);
188
- }
189
- });
190
- return result;
191
- }
192
-
193
- const statements = ANALYTICS_ROLLUP_BACKFILL_STATEMENTS.sqlite;
194
- if (getDialect() === "d1") {
195
- if (!db.atomicBatch) {
196
- throw new Error("D1 analytics rollup backfill requires an atomic batch");
197
- }
198
- await db.atomicBatch(statements);
199
- return;
200
- }
201
-
202
- if (!db.transaction) {
203
- throw new Error(
204
- "Analytics rollup backfill requires a database transaction",
205
- );
206
- }
207
- await db.transaction(async (tx) => {
208
- for (const statement of statements) await tx.execute(statement);
209
- });
210
- }
211
-
212
34
  // Convention: every new migration below MUST set a unique `name:` slug (see
213
35
  // packages/core/src/db/migrations.ts for the full rationale). Version numbers
214
36
  // alone are not a safe identity across parallel branches that each extend
@@ -1548,14 +1370,56 @@ const runAnalyticsMigrations = runMigrations(
1548
1370
  name: "analytics-query-pressure-daily-key-idx",
1549
1371
  sql: `CREATE UNIQUE INDEX IF NOT EXISTS analytics_query_pressure_daily_key_idx ON analytics_query_pressure_daily (tenant_key, event_date, query_class)`,
1550
1372
  },
1551
- // Rebuild the compact analytics rollups from raw history once when the
1552
- // tables first ship. The run-only migration takes the same write lock as
1553
- // ingest so a live increment cannot be overwritten by its snapshot.
1373
+ // Keep this historical migration name reserved, but record it without a
1374
+ // boot-time run. Scanning analytics_events here makes every concurrent
1375
+ // serverless migration runner hold a database connection for the full
1376
+ // history; v126-v131 continue to maintain the compact tables incrementally.
1377
+ // Any future one-shot backfill must use a new migration identity or an
1378
+ // explicit out-of-band job because this marker is permanently applied.
1554
1379
  {
1555
1380
  version: 132,
1556
1381
  name: "analytics-rollups-historical-backfill",
1557
1382
  sql: {},
1558
- run: runHistoricalAnalyticsRollupBackfill,
1383
+ },
1384
+ {
1385
+ version: 133,
1386
+ name: "analytics-rollups-historical-backfill-state",
1387
+ sql: {
1388
+ postgres: `CREATE TABLE IF NOT EXISTS analytics_rollup_backfill_state (
1389
+ id TEXT PRIMARY KEY,
1390
+ status TEXT NOT NULL DEFAULT 'pending',
1391
+ completed_at TEXT,
1392
+ lease_token TEXT,
1393
+ lease_expires_at TEXT,
1394
+ updated_at TEXT NOT NULL DEFAULT (now()::text)
1395
+ )`,
1396
+ sqlite: `CREATE TABLE IF NOT EXISTS analytics_rollup_backfill_state (
1397
+ id TEXT PRIMARY KEY,
1398
+ status TEXT NOT NULL DEFAULT 'pending',
1399
+ completed_at TEXT,
1400
+ lease_token TEXT,
1401
+ lease_expires_at TEXT,
1402
+ updated_at TEXT NOT NULL DEFAULT (datetime('now'))
1403
+ )`,
1404
+ },
1405
+ },
1406
+ {
1407
+ version: 134,
1408
+ name: "analytics-rollups-historical-backfill-repair",
1409
+ sql: {},
1410
+ run: async () => {
1411
+ if (!(await isHistoricalAnalyticsRollupBackfillComplete())) {
1412
+ return deferMigration();
1413
+ }
1414
+ },
1415
+ },
1416
+ {
1417
+ version: 135,
1418
+ name: "analytics-rollups-historical-backfill-lease",
1419
+ sql: `
1420
+ ALTER TABLE analytics_rollup_backfill_state ADD COLUMN IF NOT EXISTS lease_token TEXT;
1421
+ ALTER TABLE analytics_rollup_backfill_state ADD COLUMN IF NOT EXISTS lease_expires_at TEXT;
1422
+ `,
1559
1423
  },
1560
1424
  ],
1561
1425
  { table: "analytics_migrations" },
@@ -1572,10 +1436,17 @@ const runAnalyticsMigrations = runMigrations(
1572
1436
  * swallowed so it can never fail boot.
1573
1437
  */
1574
1438
  export default async (nitroApp: any): Promise<void> => {
1575
- if (isInBackgroundFunctionRuntime()) {
1576
- // Durable workers execute signed internal routes against a schema owned by
1577
- // the regular server. A second migration runner only adds a Neon pool
1578
- // probe to every worker cold start.
1439
+ const isScheduledRollupRuntime =
1440
+ (
1441
+ globalThis as typeof globalThis & {
1442
+ __AGENT_NATIVE_ANALYTICS_ROLLUP_BACKFILL_SCHEDULED_RUNTIME__?: boolean;
1443
+ }
1444
+ ).__AGENT_NATIVE_ANALYTICS_ROLLUP_BACKFILL_SCHEDULED_RUNTIME__ === true;
1445
+ if (isInBackgroundFunctionRuntime() && !isScheduledRollupRuntime) {
1446
+ // Most durable workers execute signed internal routes against a schema
1447
+ // owned by the regular server. A second migration runner only adds a Neon
1448
+ // pool probe to every worker cold start. The scheduled rollup worker is
1449
+ // the exception because it can be the first post-deploy invocation.
1579
1450
  console.info(
1580
1451
  "[db] Skipping Analytics migrations in durable background runtime",
1581
1452
  );
@@ -0,0 +1,53 @@
1
+ import { timingSafeEqual } from "node:crypto";
2
+
3
+ import { createError, defineEventHandler, getHeader } from "h3";
4
+
5
+ // guard:allow-action-twin — external cron endpoint authenticates scheduled callers before invoking the rollup job.
6
+ import { runAnalyticsRollupBackfillOnce } from "../../../jobs/analytics-rollup-backfill";
7
+
8
+ declare global {
9
+ var __AGENT_NATIVE_ANALYTICS_ROLLUP_BACKFILL_SCHEDULED_RUNTIME__:
10
+ | boolean
11
+ | undefined;
12
+ }
13
+
14
+ function scheduledFunctionRuntime(): boolean {
15
+ return (
16
+ globalThis.__AGENT_NATIVE_ANALYTICS_ROLLUP_BACKFILL_SCHEDULED_RUNTIME__ ===
17
+ true
18
+ );
19
+ }
20
+
21
+ function cronSecret(): string | null {
22
+ const secret = process.env.ANALYTICS_ROLLUP_BACKFILL_CRON_SECRET?.trim();
23
+ return secret ? secret : null;
24
+ }
25
+
26
+ function headerMatchesSecret(
27
+ header: string | undefined,
28
+ secret: string,
29
+ ): boolean {
30
+ const expected = `Bearer ${secret}`;
31
+ const value = header?.trim() ?? "";
32
+ if (value.length !== expected.length) return false;
33
+ return timingSafeEqual(Buffer.from(value), Buffer.from(expected));
34
+ }
35
+
36
+ export default defineEventHandler(async (event) => {
37
+ const secret = cronSecret();
38
+ const scheduledRuntime = scheduledFunctionRuntime();
39
+ const authenticatedHeader =
40
+ secret !== null &&
41
+ headerMatchesSecret(getHeader(event, "authorization"), secret);
42
+ if (!scheduledRuntime && !authenticatedHeader) {
43
+ throw createError({
44
+ statusCode: secret ? 401 : 503,
45
+ statusMessage: secret
46
+ ? "Unauthorized"
47
+ : "ANALYTICS_ROLLUP_BACKFILL_CRON_SECRET is required",
48
+ });
49
+ }
50
+
51
+ const result = await runAnalyticsRollupBackfillOnce();
52
+ return { ok: true, ...result };
53
+ });
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: improved
3
+ date: 2026-08-05
4
+ ---
5
+
6
+ Clips overlays now use clean surfaces without clipped shadows
@@ -40,8 +40,8 @@ interface TranscriptionStatusPayload {
40
40
  const SNOOZE_MS = 5 * 60_000;
41
41
  const FALLBACK_AUTO_HIDE_MS = 6 * 60_000;
42
42
  const DISMISSAL_TOMBSTONE_MS = 30 * 60_000;
43
- // Card is up to 440px wide; the extra width leaves room for the drop shadow
44
- // (~32px each side) so it isn't clipped by the transparent window edges.
43
+ // Card is up to 440px wide; the extra width keeps its close control and menu
44
+ // inside the transparent window edges.
45
45
  const NOTIFICATION_WINDOW_WIDTH = 504;
46
46
  const NOTIFICATION_COLLAPSED_HEIGHT = 120;
47
47
  const NOTIFICATION_MENU_HEIGHT = 224;
@@ -856,7 +856,6 @@ body[data-clips-route="recording-pill"] #root {
856
856
  width: 3px;
857
857
  border-radius: 999px;
858
858
  background: rgba(132, 204, 22, 0.98);
859
- box-shadow: 0 0 5px rgba(132, 204, 22, 0.35);
860
859
  transition: height 90ms ease-out;
861
860
  }
862
861
 
@@ -3474,7 +3473,6 @@ body[data-clips-route="recording-pill"] #root {
3474
3473
  border: 1px solid rgba(255, 255, 255, 0.22);
3475
3474
  border-radius: 50%;
3476
3475
  background: rgba(10, 12, 16, 0.42);
3477
- box-shadow: 0 10px 28px rgba(0, 0, 0, 0.28);
3478
3476
  color: #ffffff;
3479
3477
  cursor: pointer;
3480
3478
  pointer-events: auto;
@@ -3530,21 +3528,6 @@ body[data-clips-route="recording-pill"] #root {
3530
3528
  font-weight: 800;
3531
3529
  font-variant-numeric: tabular-nums;
3532
3530
  color: #ffffff;
3533
- /*
3534
- * Do NOT use -webkit-text-stroke here: it joins with a hard miter and no way
3535
- * to set stroke-linejoin, so at this size/weight the sharp concave corner of
3536
- * the "2" (where the curved top meets the flat bottom bar) sprouts star-shaped
3537
- * miter spikes. We fake a soft dark edge with a 4-direction 1px shadow halo
3538
- * instead, which has no join geometry and cannot spike.
3539
- */
3540
- text-shadow:
3541
- 1px 0 1px rgba(17, 24, 39, 0.22),
3542
- -1px 0 1px rgba(17, 24, 39, 0.22),
3543
- 0 1px 1px rgba(17, 24, 39, 0.22),
3544
- 0 -1px 1px rgba(17, 24, 39, 0.22),
3545
- 0 2px 4px rgba(0, 0, 0, 0.48),
3546
- 0 12px 34px rgba(0, 0, 0, 0.36),
3547
- 0 0 2px rgba(0, 0, 0, 0.72);
3548
3531
  animation: cd-pop 1s ease-out forwards;
3549
3532
  line-height: 1;
3550
3533
  }
@@ -3560,7 +3543,6 @@ body[data-clips-route="recording-pill"] #root {
3560
3543
  border: 1px solid rgba(255, 255, 255, 0.1);
3561
3544
  border-radius: var(--radius-pill);
3562
3545
  background: rgba(10, 12, 16, 0.28);
3563
- box-shadow: 0 10px 28px rgba(0, 0, 0, 0.24);
3564
3546
  color: rgba(255, 255, 255, 0.58);
3565
3547
  font-size: 11px;
3566
3548
  font-weight: 500;
@@ -3676,9 +3658,6 @@ body[data-clips-route="recording-pill"] #root {
3676
3658
  backdrop-filter: blur(20px);
3677
3659
  -webkit-backdrop-filter: blur(20px);
3678
3660
  border: 1px solid rgba(255, 255, 255, 0.08);
3679
- box-shadow:
3680
- 0 14px 40px rgba(0, 0, 0, 0.45),
3681
- 0 2px 6px rgba(0, 0, 0, 0.3);
3682
3661
  color: white;
3683
3662
  }
3684
3663
 
@@ -4404,9 +4383,7 @@ body[data-clips-route="recording-pill"] #root {
4404
4383
  justify-content: center;
4405
4384
  flex-shrink: 0;
4406
4385
  border-radius: 50%;
4407
- transition:
4408
- background 120ms,
4409
- box-shadow 120ms;
4386
+ transition: background 120ms;
4410
4387
  }
4411
4388
 
4412
4389
  .bubble-control-button {
@@ -4438,7 +4415,6 @@ body[data-clips-route="recording-pill"] #root {
4438
4415
 
4439
4416
  .bubble-dot.is-active {
4440
4417
  background: white;
4441
- box-shadow: 0 0 0 2px rgba(255, 255, 255, 0.25);
4442
4418
  }
4443
4419
 
4444
4420
  .bubble-dot-small {
@@ -4832,8 +4808,7 @@ body[data-clips-route="recording-pill"] #root {
4832
4808
  display: flex;
4833
4809
  align-items: flex-start;
4834
4810
  justify-content: flex-end;
4835
- /* Extra horizontal room so the card's drop shadow isn't clipped by the
4836
- transparent window edges (shadow blur is ~30px). */
4811
+ /* Keep the card and its edge controls inside the transparent window. */
4837
4812
  padding: 12px 32px;
4838
4813
  background: transparent;
4839
4814
  pointer-events: none;
@@ -4850,9 +4825,6 @@ body[data-clips-route="recording-pill"] #root {
4850
4825
  backdrop-filter: blur(20px);
4851
4826
  -webkit-backdrop-filter: blur(20px);
4852
4827
  border: 1px solid rgba(255, 255, 255, 0.08);
4853
- box-shadow:
4854
- 0 10px 30px rgba(0, 0, 0, 0.35),
4855
- 0 2px 6px rgba(0, 0, 0, 0.3);
4856
4828
  color: white;
4857
4829
  pointer-events: auto;
4858
4830
  min-width: 320px;
@@ -5008,7 +4980,6 @@ body[data-clips-route="recording-pill"] #root {
5008
4980
  border-radius: 10px;
5009
4981
  background: rgba(24, 26, 32, 0.98);
5010
4982
  border: 1px solid rgba(255, 255, 255, 0.1);
5011
- box-shadow: 0 12px 28px rgba(0, 0, 0, 0.4);
5012
4983
  z-index: 5;
5013
4984
  display: flex;
5014
4985
  flex-direction: column;
@@ -5061,7 +5032,6 @@ body[data-clips-route="recording-pill"] #root {
5061
5032
  align-items: center;
5062
5033
  justify-content: center;
5063
5034
  padding: 0;
5064
- box-shadow: 0 2px 6px rgba(0, 0, 0, 0.35);
5065
5035
  z-index: 6;
5066
5036
  }
5067
5037
 
@@ -5097,7 +5067,6 @@ body[data-clips-route="recording-pill"] #root {
5097
5067
  backdrop-filter: blur(20px);
5098
5068
  -webkit-backdrop-filter: blur(20px);
5099
5069
  border: 1px solid rgba(255, 255, 255, 0.06);
5100
- box-shadow: 0 10px 30px rgba(0, 0, 0, 0.35);
5101
5070
  cursor: pointer;
5102
5071
  color: white;
5103
5072
  }
@@ -5321,10 +5290,6 @@ body[data-clips-route="recording-pill"] #root {
5321
5290
  border: 1.5px solid rgba(255, 255, 255, 0.62);
5322
5291
  border-radius: 7px;
5323
5292
  background: rgba(255, 255, 255, 0.018);
5324
- box-shadow:
5325
- 0 0 120px 42px rgba(0, 0, 0, 0.075),
5326
- inset 0 0 34px rgba(0, 0, 0, 0.06),
5327
- 0 0 0 1px rgba(0, 0, 0, 0.12);
5328
5293
  }
5329
5294
 
5330
5295
  .region-guide-rect-editable {
@@ -5336,19 +5301,11 @@ body[data-clips-route="recording-pill"] #root {
5336
5301
  .region-guide-rect-editable:hover,
5337
5302
  .region-guide-rect-editable.selected {
5338
5303
  border-color: rgba(255, 255, 255, 0.92);
5339
- box-shadow:
5340
- 0 0 150px 50px rgba(0, 0, 0, 0.105),
5341
- inset 0 0 36px rgba(0, 0, 0, 0.08),
5342
- 0 0 0 1px rgba(0, 0, 0, 0.2);
5343
5304
  }
5344
5305
 
5345
5306
  .region-guide-rect-editable:focus-visible {
5346
5307
  outline: none;
5347
5308
  border-color: rgba(255, 255, 255, 0.98);
5348
- box-shadow:
5349
- 0 0 0 2px rgba(23, 23, 23, 0.5),
5350
- 0 0 150px 50px rgba(0, 0, 0, 0.12),
5351
- inset 0 0 36px rgba(0, 0, 0, 0.08);
5352
5309
  }
5353
5310
 
5354
5311
  .region-guide-rect-draft {
@@ -5365,9 +5322,6 @@ body[data-clips-route="recording-pill"] #root {
5365
5322
  border: 1px solid rgba(23, 23, 23, 0.38);
5366
5323
  border-radius: 4px;
5367
5324
  background: rgba(255, 255, 255, 0.96);
5368
- box-shadow:
5369
- 0 3px 12px rgba(0, 0, 0, 0.18),
5370
- 0 0 0 1px rgba(255, 255, 255, 0.35);
5371
5325
  }
5372
5326
 
5373
5327
  .region-guide-handle-nw {
@@ -5412,7 +5366,6 @@ body[data-clips-route="recording-pill"] #root {
5412
5366
  border-radius: 12px;
5413
5367
  background: rgba(15, 17, 21, 0.74);
5414
5368
  color: #f8fafc;
5415
- box-shadow: 0 16px 48px rgba(0, 0, 0, 0.22);
5416
5369
  transform: translateX(-50%);
5417
5370
  backdrop-filter: blur(18px);
5418
5371
  }
@@ -5616,7 +5569,6 @@ body[data-clips-route="recording-pill"] #root {
5616
5569
  border-radius: 16px;
5617
5570
  background: rgba(26, 26, 26, 0.95);
5618
5571
  color: white;
5619
- box-shadow: 0 0 0 1px rgba(255, 255, 255, 0.1);
5620
5572
  cursor: grab;
5621
5573
  backdrop-filter: blur(12px);
5622
5574
  -webkit-backdrop-filter: blur(12px);
@@ -5651,9 +5603,7 @@ body[data-clips-route="recording-pill"] #root {
5651
5603
  rectangle. */
5652
5604
  border-radius: 999px;
5653
5605
  cursor: pointer;
5654
- transition:
5655
- background 120ms ease,
5656
- box-shadow 120ms ease;
5606
+ transition: background 120ms ease;
5657
5607
  }
5658
5608
 
5659
5609
  /* CSS :hover covers the focused case; .pill-hovered is the Rust-polled
@@ -5661,7 +5611,6 @@ body[data-clips-route="recording-pill"] #root {
5661
5611
  .pill-inner-compact:hover,
5662
5612
  .pill-inner-compact.pill-hovered {
5663
5613
  background: rgba(42, 42, 42, 0.97);
5664
- box-shadow: 0 0 0 1px rgba(255, 255, 255, 0.2);
5665
5614
  }
5666
5615
 
5667
5616
  .pill-controls {
@@ -22,9 +22,9 @@ use crate::util::{
22
22
  };
23
23
 
24
24
  const MEETING_NOTIFICATION_LABEL: &str = "meeting-notif";
25
- // Window is wider than the 440px card so the card's drop shadow (~32px each
26
- // side) renders inside the transparent window instead of being clipped. The
27
- // right margin is pulled in to keep the card near the top-right corner.
25
+ // Window is wider than the 440px card so its close control and menu stay inside
26
+ // the transparent window. The right margin is pulled in to keep the card near
27
+ // the top-right corner.
28
28
  const NOTIFICATION_W_LOGICAL: u32 = 504;
29
29
  const NOTIFICATION_H_LOGICAL: u32 = 120;
30
30
  const NOTIFICATION_TOP_MARGIN_LOGICAL: u32 = 44;
@@ -156,7 +156,9 @@ export default defineAction({
156
156
  run: async ({ id }) => {
157
157
  const access = await resolveAccess("design-system", id);
158
158
  if (!access) {
159
- throw new Error("Design system not found");
159
+ throw Object.assign(new Error("Design system not found"), {
160
+ statusCode: 404,
161
+ });
160
162
  }
161
163
 
162
164
  const row = access.resource;
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: fixed
3
+ date: 2026-08-05
4
+ ---
5
+
6
+ Missing design systems now return a clear not-found response.
@@ -190,7 +190,7 @@ export default function DeckCard({
190
190
  {deck.starred ? (
191
191
  <IconStarFilled className="h-3.5 w-3.5 text-[#F5C451]" />
192
192
  ) : (
193
- <IconStar className="h-3.5 w-3.5 text-foreground/70" />
193
+ <IconStar className="h-3.5 w-3.5 text-white/70" />
194
194
  )}
195
195
  </button>
196
196
  )}
@@ -208,7 +208,7 @@ export default function DeckCard({
208
208
  }`}
209
209
  aria-label={t("raw.deckOptions")}
210
210
  >
211
- <IconDots className="w-3.5 h-3.5 text-foreground/70" />
211
+ <IconDots className="w-3.5 h-3.5 text-white/70" />
212
212
  </button>
213
213
  </DropdownMenuTrigger>
214
214
  <DropdownMenuContent
@@ -4112,7 +4112,7 @@ export default function SlideEditor({
4112
4112
  // to stay mounted or that background becomes uneditable.
4113
4113
  const contextToolbar = !readOnly ? (
4114
4114
  <div
4115
- className="hidden shrink-0 lg:block"
4115
+ className="shrink-0"
4116
4116
  // Snapshotting the range is only half the job: without this marker the
4117
4117
  // click-outside handler exits the edit and clears the snapshot before
4118
4118
  // the button's onClick runs, so partial-text formatting would silently
@@ -654,17 +654,17 @@ const messages = {
654
654
  mine: "Mine",
655
655
  deckCount_one: "deck",
656
656
  deckCount_other: "decks",
657
- createDeckOrVisual: "Create a deck or visual",
657
+ createDeckOrVisual: "Create a presentation",
658
658
  noMineDecks: "No decks created by you yet.",
659
659
  deleteDeckTitle: "Delete Deck?",
660
660
  deleteDeckDescription:
661
661
  "This will permanently delete this deck and all its slides. This action cannot be undone.",
662
662
  cancel: "Cancel",
663
663
  delete: "Delete",
664
- newDeckPromptTitle: "New deck",
665
- newDeckPlaceholder: "Describe your deck, visual, or diagram...",
664
+ newDeckPromptTitle: "New presentation",
665
+ newDeckPlaceholder: "Describe the presentation you want to generate...",
666
666
  skipPrompt: "Skip prompt",
667
- firstDeckPromptTitle: "What do you want to make?",
667
+ firstDeckPromptTitle: "What kind of presentation should we generate?",
668
668
  firstDeckSkip: "Skip",
669
669
  chooseReferences: "Choose references",
670
670
  addDesignSystem: "+ Design system",
@@ -716,7 +716,7 @@ const messages = {
716
716
  emptyTitle: "No decks yet",
717
717
  createFirstDeck: "Create your first deck",
718
718
  emptyDescription:
719
- "Build beautiful presentations, standalone visuals, diagrams, and image-rich stories with AI-powered generation.",
719
+ "Build beautiful presentations with AI-powered generation.",
720
720
  },
721
721
  };
722
722
 
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: improved
3
+ date: 2026-08-05
4
+ ---
5
+
6
+ New presentation prompts now clearly describe presentation generation
@@ -4007,7 +4007,7 @@ const AssistantChatInner = forwardRef(function AssistantChatInner({ emptyStateTe
4007
4007
  } }) })), shouldShowRunError && visibleRunError && (_jsx(MessageScrollerItem, { children: _jsx(RunErrorRecoveryCard, { info: visibleRunError, onContinue: () => {
4008
4008
  setRunErrorInfo(null);
4009
4009
  addToQueue(RECONNECT_NO_PROGRESS_CONTINUE_MESSAGE, undefined, undefined, undefined, undefined, "queued", "continue");
4010
- }, onRetry: retryAfterRunError, onFork: onForkChat, onDismiss: () => {
4010
+ }, onRetry: retryAfterRunError, onFork: onForkChat, onProviderConnected: handleBuilderConnected, onDismiss: () => {
4011
4011
  if (visibleRunErrorKey) {
4012
4012
  setDismissedRunErrorKey(visibleRunErrorKey);
4013
4013
  }
@@ -35,12 +35,13 @@ export declare function BuilderSetupCard({ onConnected, bouncePulse, fullWidth,
35
35
  fullWidth?: boolean;
36
36
  layout?: BuilderSetupCardLayout;
37
37
  }): import("react").JSX.Element;
38
- export declare function RunErrorRecoveryCard({ info, onContinue, onRetry, onFork, onDismiss, }: {
38
+ export declare function RunErrorRecoveryCard({ info, onContinue, onRetry, onFork, onDismiss, onProviderConnected, }: {
39
39
  info: RunErrorInfo;
40
40
  onContinue: () => void;
41
41
  onRetry: () => void;
42
42
  onFork?: () => void | boolean | Promise<void | boolean>;
43
43
  onDismiss: () => void;
44
+ onProviderConnected?: () => void;
44
45
  }): import("react").JSX.Element;
45
46
  export declare function LoopLimitContinueCard({ info, onContinue, }: {
46
47
  info: LoopLimitInfo;