@agent-native/core 0.187.0 → 0.187.1-nightly-20260925003003
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/corpus/README.md +1 -1
- package/corpus/templates/analytics/app/components/layout/Sidebar.tsx +4 -3
- package/corpus/templates/analytics/changelog/2026-09-24-analytics-sidebar-footer-aligns-consistently.md +6 -0
- package/corpus/templates/analytics/changelog/2026-09-24-dashboard-emails-match-stacked-area-totals.md +6 -0
- package/corpus/templates/analytics/server/lib/report-chart-svg.ts +47 -20
- package/corpus/templates/calendar/app/components/calendar/AttendeeAutocomplete.tsx +1 -1
- package/corpus/templates/calendar/changelog/2026-09-24-guest-inputs-wrap-beneath-existing-guests-so-the-active-inpu.md +6 -0
- package/corpus/templates/clips/app/components/editor/stitch-manager.tsx +8 -3
- package/corpus/templates/clips/app/lib/recording-link.ts +21 -0
- package/corpus/templates/clips/app/routes/record.tsx +27 -8
- package/corpus/templates/clips/changelog/2026-09-24-the-camera-bubble-stays-visible-while-you-reposition-it-befo.md +6 -0
- package/corpus/templates/clips/desktop/src-tauri/src/clips/mod.rs +7 -0
- package/corpus/templates/clips/desktop/src-tauri/src/lib.rs +63 -55
- package/corpus/templates/design/.generated/bridge/editor-chrome.generated.ts +251 -74
- package/corpus/templates/design/app/components/design/DesignCanvas.tsx +66 -56
- package/corpus/templates/design/app/components/design/EditPanel.tsx +19 -0
- package/corpus/templates/design/app/components/design/MultiScreenCanvas.tsx +59 -9
- package/corpus/templates/design/app/components/design/edit-panel/interaction-state-helpers.ts +4 -0
- package/corpus/templates/design/app/components/design/edit-panel/layout-properties.tsx +119 -0
- package/corpus/templates/design/app/components/design/edit-panel/stroke-properties.tsx +48 -51
- package/corpus/templates/design/app/components/design/edit-panel/style-change-types.ts +1 -0
- package/corpus/templates/design/app/components/design/inspector/AutoLayoutMatrix.tsx +298 -187
- package/corpus/templates/design/app/components/design/inspector/GradientEditor.tsx +2 -2
- package/corpus/templates/design/app/components/design/inspector/index.ts +4 -0
- package/corpus/templates/design/app/components/design/multi-screen/types.ts +4 -2
- package/corpus/templates/design/app/i18n/en-US.ts +6 -0
- package/corpus/templates/design/app/pages/Present.tsx +101 -87
- package/corpus/templates/design/app/pages/design-editor/clone-and-pen-edit.ts +75 -14
- package/corpus/templates/design/app/pages/design-editor/commands/README.md +1 -0
- package/corpus/templates/design/app/pages/design-editor/commands/commit-relative-style-delta-to-selected-layers.ts +69 -56
- package/corpus/templates/design/app/pages/design-editor/commands/mode-change.ts +16 -0
- package/corpus/templates/design/app/pages/design-editor/commands/scale-selection.ts +17 -2
- package/corpus/templates/design/app/pages/design-editor/commands/styles-change.ts +23 -30
- package/corpus/templates/design/app/pages/design-editor/commands/swap-fill-stroke.ts +105 -0
- package/corpus/templates/design/app/pages/design-editor/pending-edits.ts +21 -0
- package/corpus/templates/design/app/pages/present-review-state.ts +7 -1
- package/corpus/templates/design/changelog/2026-09-24-a-vector-s-stroke-row-shows-position-beside-weight-instead-o.md +6 -0
- package/corpus/templates/design/changelog/2026-09-24-css-margins-can-be-adjusted-on-the-canvas-and-in-the-layout-.md +6 -0
- package/corpus/templates/design/changelog/2026-09-24-design-reviews-show-the-real-canvas-beside-its-conversation.md +6 -0
- package/corpus/templates/design/changelog/2026-09-24-double-clicking-text-or-a-vector-on-the-board-starts-editing.md +6 -0
- package/corpus/templates/design/changelog/2026-09-24-interact-opens-a-focused-responsive-preview-without-reloadin.md +6 -0
- package/corpus/templates/design/changelog/2026-09-24-keep-gradient-values-visible-and-limit-canvas-paints-to-supp.md +6 -0
- package/corpus/templates/design/changelog/2026-09-24-open-pen-paths-no-longer-paint-a-filled-triangle-after-shift.md +6 -0
- package/corpus/templates/design/changelog/2026-09-24-re-clicking-the-active-responsive-preview-returns-to-the-can.md +6 -0
- package/corpus/templates/design/changelog/2026-09-24-resized-vectors-can-be-point-edited-and-the-k-scale-tool-sho.md +6 -0
- package/corpus/templates/design/changelog/2026-09-24-while-editing-text-click-to-place-the-caret-and-double-click.md +6 -0
- package/corpus/templates/design/e2e/helpers.ts +19 -2
- package/corpus/templates/mail/app/components/email/ComposeModal.tsx +1 -1
- package/corpus/templates/mail/changelog/2026-09-24-gmail-push-ack.md +6 -0
- package/corpus/templates/mail/changelog/2026-09-24-the-composer-keeps-its-taller-layout-in-a-narrower-window.md +6 -0
- package/corpus/templates/mail/server/db/schema.ts +26 -1
- package/corpus/templates/mail/server/lib/inbox-store.ts +68 -23
- package/corpus/templates/mail/server/lib/inbox-sync.ts +43 -17
- package/corpus/templates/mail/server/plugins/db.ts +19 -0
- package/corpus/templates/mail/server/routes/api/gmail/push.post.ts +4 -15
- package/corpus/templates/slides/.agents/skills/create-deck/SKILL.md +45 -30
- package/corpus/templates/slides/.agents/skills/slide-editing/SKILL.md +18 -2
- package/corpus/templates/slides/actions/export-html.ts +28 -17
- package/corpus/templates/slides/app/hooks/use-deck-design-system.ts +27 -15
- package/corpus/templates/slides/app/pages/DeckEditor.tsx +1 -1
- package/corpus/templates/slides/changelog/2026-09-23-decks-with-no-design-system-now-follow-a-theme-chosen-for-th.md +6 -0
- package/corpus/templates/slides/shared/slide-background.ts +3 -1
- package/dist/agent/engine/builder-engine.js +11 -0
- package/dist/agent/engine/types.d.ts +1 -0
- package/dist/agent/production-agent.d.ts +2 -0
- package/dist/agent/production-agent.js +32 -4
- package/dist/agent/run-loop-with-resume.js +6 -0
- package/dist/cli/index.js +4 -4
- package/dist/cli/process.d.ts +10 -1
- package/dist/cli/process.js +66 -0
- package/dist/cli/workspace-dev.js +2 -0
- package/dist/client/agent-chat.js +13 -14
- package/dist/client/error-format.js +4 -4
- package/dist/client/observability/ObservabilityDashboard.js +95 -65
- package/dist/client/observability/OutputPreview.d.ts +2 -1
- package/dist/client/observability/OutputPreview.js +64 -7
- package/dist/client/observability/useObservability.d.ts +2 -1
- package/dist/client/observability/useObservability.js +5 -1
- package/dist/client/settings/UsageSection.js +105 -17
- package/dist/collab/routes.d.ts +1 -1
- package/dist/collab/struct-routes.d.ts +1 -1
- package/dist/db/client.d.ts +44 -0
- package/dist/db/client.js +62 -3
- package/dist/db/create-get-db.js +23 -1
- package/dist/db/embedded-runtime.d.ts +21 -0
- package/dist/db/embedded-runtime.js +27 -0
- package/dist/db/migrations.js +2 -0
- package/dist/db/server-runtime.d.ts +22 -0
- package/dist/db/server-runtime.js +26 -0
- package/dist/deploy/build.d.ts +29 -0
- package/dist/deploy/build.js +204 -121
- package/dist/feature-flags/index.d.ts +1 -1
- package/dist/feature-flags/index.js +1 -1
- package/dist/feature-flags/registry.d.ts +1 -0
- package/dist/feature-flags/registry.js +5 -0
- package/dist/file-upload/actions/upload-image.d.ts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/integrations/webhook-handler.js +4 -1
- package/dist/jobs/background-automation-runner.js +6 -1
- package/dist/localization/core-messages/ar-SA.js +9 -1
- package/dist/localization/core-messages/de-DE.js +9 -1
- package/dist/localization/core-messages/en-US.d.ts +9 -1
- package/dist/localization/core-messages/en-US.js +9 -1
- package/dist/localization/core-messages/es-ES.js +9 -1
- package/dist/localization/core-messages/fr-FR.js +9 -1
- package/dist/localization/core-messages/hi-IN.js +9 -1
- package/dist/localization/core-messages/ja-JP.js +9 -1
- package/dist/localization/core-messages/ko-KR.js +9 -1
- package/dist/localization/core-messages/pt-BR.js +9 -1
- package/dist/localization/core-messages/supplemental/ar-SA.js +1 -0
- package/dist/localization/core-messages/supplemental/de-DE.js +1 -0
- package/dist/localization/core-messages/supplemental/en-US.js +1 -0
- package/dist/localization/core-messages/supplemental/es-ES.js +1 -0
- package/dist/localization/core-messages/supplemental/fr-FR.js +1 -0
- package/dist/localization/core-messages/supplemental/hi-IN.js +1 -0
- package/dist/localization/core-messages/supplemental/ja-JP.js +1 -0
- package/dist/localization/core-messages/supplemental/ko-KR.js +1 -0
- package/dist/localization/core-messages/supplemental/pt-BR.js +1 -0
- package/dist/localization/core-messages/supplemental/zh-CN.js +1 -0
- package/dist/localization/core-messages/supplemental/zh-TW.js +1 -0
- package/dist/localization/core-messages/zh-CN.js +9 -1
- package/dist/localization/core-messages/zh-TW.js +9 -1
- package/dist/localization/core-messages.d.ts +9 -1
- package/dist/localization/mcp-settings-messages.d.ts +1 -0
- package/dist/mcp/connect-route.js +45 -8
- package/dist/mcp/connect-store.d.ts +7 -5
- package/dist/mcp/connect-store.js +30 -39
- package/dist/notifications/routes.d.ts +3 -3
- package/dist/observability/actions/get-observability-review-detail.d.ts +7 -0
- package/dist/observability/actions/get-observability-review-detail.js +25 -0
- package/dist/observability/reviews.d.ts +9 -1
- package/dist/observability/reviews.js +20 -0
- package/dist/observability/routes.d.ts +3 -3
- package/dist/observability/types.d.ts +8 -0
- package/dist/org/handlers.d.ts +1 -1
- package/dist/provider-api/actions/custom-provider-registration.d.ts +7 -7
- package/dist/provider-api/actions/provider-api.d.ts +9 -9
- package/dist/scripts/db/query.js +6 -1
- package/dist/server/agent-engine-api-key-route.d.ts +1 -1
- package/dist/server/agent-engine-ollama-models-route.d.ts +2 -2
- package/dist/server/agent-teams.js +4 -1
- package/dist/server/auth.js +60 -20
- package/dist/server/better-auth-instance.js +7 -1
- package/dist/server/core-routes-plugin.d.ts +1 -1
- package/dist/server/core-routes-plugin.js +2 -0
- package/dist/server/embedded.js +6 -0
- package/dist/server/framework-request-handler.js +6 -0
- package/dist/server/realtime-token.d.ts +1 -1
- package/dist/usage/actions/get-usage-metrics.js +4 -1
- package/dist/usage/metrics-store.d.ts +19 -0
- package/dist/usage/metrics-store.js +106 -14
- package/dist/usage/store.d.ts +7 -2
- package/dist/usage/store.js +25 -4
- package/dist/vite/action-types-plugin.js +4 -0
- package/dist/vite/client.d.ts +6 -1
- package/dist/vite/client.js +40 -1
- package/package.json +4 -3
|
@@ -27,17 +27,18 @@ import {
|
|
|
27
27
|
} from "./google-auth.js";
|
|
28
28
|
import { classifyAutomated } from "./inbox-classify.js";
|
|
29
29
|
import {
|
|
30
|
-
assertSyncClaimHeld,
|
|
31
30
|
claimSyncAccount,
|
|
32
31
|
deleteInboxThreadRow,
|
|
33
32
|
ensureSyncAccountRow,
|
|
34
33
|
markThreadsOutOfInboxBeforeSync,
|
|
35
34
|
patchSyncAccount,
|
|
35
|
+
readInboxPushGeneration,
|
|
36
36
|
readSyncAccounts,
|
|
37
37
|
releaseSyncAccount,
|
|
38
38
|
resetSyncAccountProgress,
|
|
39
39
|
SyncClaimLostError,
|
|
40
40
|
upsertInboxThreadRows,
|
|
41
|
+
withSyncClaim,
|
|
41
42
|
type CachedGmailLabel,
|
|
42
43
|
type SyncAccountPatch,
|
|
43
44
|
type SyncAccountRow,
|
|
@@ -297,12 +298,17 @@ async function hydrateAndApply(
|
|
|
297
298
|
else deletes.push({ id: part.id, readStartedAt });
|
|
298
299
|
}
|
|
299
300
|
}
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
301
|
+
await withSyncClaim(ownerEmail, accountEmail, claimId, async (tx) => {
|
|
302
|
+
if (upserts.length > 0) await upsertInboxThreadRows(upserts, tx);
|
|
303
|
+
for (const { id, readStartedAt } of deletes)
|
|
304
|
+
await deleteInboxThreadRow(
|
|
305
|
+
ownerEmail,
|
|
306
|
+
accountEmail,
|
|
307
|
+
id,
|
|
308
|
+
readStartedAt,
|
|
309
|
+
tx,
|
|
310
|
+
);
|
|
311
|
+
});
|
|
306
312
|
}
|
|
307
313
|
|
|
308
314
|
async function runFullSyncStep(
|
|
@@ -348,10 +354,9 @@ async function runFullSyncStep(
|
|
|
348
354
|
accountEmail,
|
|
349
355
|
connected,
|
|
350
356
|
);
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
await upsertInboxThreadRows(rows);
|
|
357
|
+
await withSyncClaim(ownerEmail, accountEmail, claimId, (tx) =>
|
|
358
|
+
upsertInboxThreadRows(rows, tx),
|
|
359
|
+
);
|
|
355
360
|
}
|
|
356
361
|
pageToken = page.nextPageToken;
|
|
357
362
|
await patchProgress(ownerEmail, accountEmail, claimId, {
|
|
@@ -359,11 +364,13 @@ async function runFullSyncStep(
|
|
|
359
364
|
});
|
|
360
365
|
|
|
361
366
|
if (!pageToken) {
|
|
362
|
-
await
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
+
await withSyncClaim(ownerEmail, accountEmail, claimId, (tx) =>
|
|
368
|
+
markThreadsOutOfInboxBeforeSync(
|
|
369
|
+
ownerEmail,
|
|
370
|
+
accountEmail,
|
|
371
|
+
fullSyncStartedAt!,
|
|
372
|
+
tx,
|
|
373
|
+
),
|
|
367
374
|
);
|
|
368
375
|
await patchProgress(ownerEmail, accountEmail, claimId, {
|
|
369
376
|
historyId: fullSyncHistoryId,
|
|
@@ -555,6 +562,7 @@ export async function syncInboxAccount(
|
|
|
555
562
|
budgetMs?: number;
|
|
556
563
|
force?: boolean;
|
|
557
564
|
connectedAccountEmails?: readonly string[];
|
|
565
|
+
pushGeneration?: number;
|
|
558
566
|
},
|
|
559
567
|
): Promise<InboxSyncAccountStatus> {
|
|
560
568
|
const budgetMs = opts?.budgetMs ?? DEFAULT_BUDGET_MS;
|
|
@@ -573,6 +581,9 @@ export async function syncInboxAccount(
|
|
|
573
581
|
|
|
574
582
|
let row = claim.row;
|
|
575
583
|
try {
|
|
584
|
+
const pushGeneration =
|
|
585
|
+
opts?.pushGeneration ??
|
|
586
|
+
(await readInboxPushGeneration(ownerEmail, accountEmail));
|
|
576
587
|
let client: { accessToken: string; email: string } | null;
|
|
577
588
|
try {
|
|
578
589
|
client = await getClientForConnectedAccount(ownerEmail, accountEmail);
|
|
@@ -647,6 +658,14 @@ export async function syncInboxAccount(
|
|
|
647
658
|
: "idle";
|
|
648
659
|
if (syncResult.changed) invalidateHistoryCacheForAccount(accountEmail);
|
|
649
660
|
invalidateListCacheForOwner(ownerEmail);
|
|
661
|
+
if (
|
|
662
|
+
accountStatus.state === "ready" &&
|
|
663
|
+
pushGeneration > row.lastPushGeneration
|
|
664
|
+
) {
|
|
665
|
+
await patchProgress(ownerEmail, accountEmail, claim.claimId, {
|
|
666
|
+
lastPushGeneration: pushGeneration,
|
|
667
|
+
});
|
|
668
|
+
}
|
|
650
669
|
await releaseSyncAccount(ownerEmail, accountEmail, claim.claimId, dbStatus);
|
|
651
670
|
return accountStatus;
|
|
652
671
|
} catch (err) {
|
|
@@ -692,8 +711,14 @@ export async function ensureInboxFresh(
|
|
|
692
711
|
const statuses = await Promise.all(
|
|
693
712
|
emails.map(async (accountEmail) => {
|
|
694
713
|
const row = await ensureSyncAccountRow(ownerEmail, accountEmail);
|
|
714
|
+
const pushGeneration = await readInboxPushGeneration(
|
|
715
|
+
ownerEmail,
|
|
716
|
+
accountEmail,
|
|
717
|
+
);
|
|
695
718
|
const fresh =
|
|
696
|
-
row.
|
|
719
|
+
row.lastPushGeneration >= pushGeneration &&
|
|
720
|
+
row.lastSyncedAt != null &&
|
|
721
|
+
now - row.lastSyncedAt < maxAgeMs;
|
|
697
722
|
if (fresh) return statusFromRow(row);
|
|
698
723
|
try {
|
|
699
724
|
// Accounts run concurrently and share nothing — each has its own
|
|
@@ -701,6 +726,7 @@ export async function ensureInboxFresh(
|
|
|
701
726
|
return await syncInboxAccount(ownerEmail, accountEmail, {
|
|
702
727
|
budgetMs,
|
|
703
728
|
connectedAccountEmails: accounts,
|
|
729
|
+
pushGeneration,
|
|
704
730
|
});
|
|
705
731
|
} catch (err) {
|
|
706
732
|
// Belt-and-suspenders: syncInboxAccount already records failures on
|
|
@@ -281,6 +281,25 @@ ADD COLUMN IF NOT EXISTS local_mutation_history_id TEXT;
|
|
|
281
281
|
ALTER TABLE mail_inbox_threads
|
|
282
282
|
ADD COLUMN IF NOT EXISTS local_mutation_fields INTEGER`,
|
|
283
283
|
},
|
|
284
|
+
{
|
|
285
|
+
version: 25,
|
|
286
|
+
name: "mail-inbox-push-invalidations",
|
|
287
|
+
sql: `CREATE TABLE IF NOT EXISTS mail_inbox_push_invalidations (
|
|
288
|
+
id TEXT PRIMARY KEY,
|
|
289
|
+
owner_email TEXT NOT NULL,
|
|
290
|
+
account_email TEXT NOT NULL
|
|
291
|
+
);
|
|
292
|
+
CREATE INDEX IF NOT EXISTS idx_mail_inbox_push_invalidations_owner_account
|
|
293
|
+
ON mail_inbox_push_invalidations(owner_email, account_email);`,
|
|
294
|
+
},
|
|
295
|
+
{
|
|
296
|
+
version: 26,
|
|
297
|
+
name: "mail-inbox-push-generation",
|
|
298
|
+
sql: `ALTER TABLE mail_inbox_push_invalidations
|
|
299
|
+
ADD COLUMN IF NOT EXISTS generation BIGINT NOT NULL DEFAULT 1;
|
|
300
|
+
ALTER TABLE mail_sync_accounts
|
|
301
|
+
ADD COLUMN IF NOT EXISTS last_push_generation BIGINT NOT NULL DEFAULT 0;`,
|
|
302
|
+
},
|
|
284
303
|
],
|
|
285
304
|
{ table: "mail_migrations" },
|
|
286
305
|
);
|
|
@@ -12,7 +12,7 @@ import {
|
|
|
12
12
|
bumpHistoryWatermark,
|
|
13
13
|
invalidateListCacheForOwner,
|
|
14
14
|
} from "../../../lib/google-auth.js";
|
|
15
|
-
import {
|
|
15
|
+
import { recordInboxPushInvalidation } from "../../../lib/inbox-store.js";
|
|
16
16
|
|
|
17
17
|
// Cache Google's public keys for OIDC verification. jose handles TTL + refresh.
|
|
18
18
|
// https://cloud.google.com/pubsub/docs/push#validate_tokens
|
|
@@ -123,23 +123,12 @@ export default defineEventHandler(async (event: H3Event) => {
|
|
|
123
123
|
bumpHistoryWatermark(emailAddress, historyId);
|
|
124
124
|
if (owner) {
|
|
125
125
|
invalidateListCacheForOwner(owner);
|
|
126
|
-
|
|
127
|
-
// of waiting for the next 15s freshness check. A sync failure here
|
|
128
|
-
// must never fail the push ack (Pub/Sub would just redeliver it).
|
|
129
|
-
// Budget stays well under Pub/Sub's default 10s ack deadline — an
|
|
130
|
-
// incremental history sync (the common case here) is ~1-2s, and a full
|
|
131
|
-
// sync just resumes on the next request/poll since its progress is
|
|
132
|
-
// persisted, so there's no need to risk redelivery chasing it to
|
|
133
|
-
// completion synchronously (fire-and-forget after responding is killed
|
|
134
|
-
// on Netlify Lambdas, so this can't run past the response either way).
|
|
135
|
-
await syncInboxAccount(owner, emailAddress, { budgetMs: 4_000 }).catch(
|
|
136
|
-
(syncErr: any) => {
|
|
137
|
-
console.warn(`[gmail-push] inbox resync failed: ${syncErr?.message}`);
|
|
138
|
-
},
|
|
139
|
-
);
|
|
126
|
+
await recordInboxPushInvalidation(owner, emailAddress);
|
|
140
127
|
}
|
|
141
128
|
} catch (err: any) {
|
|
142
129
|
console.warn(`[gmail-push] processing failed: ${err.message}`);
|
|
130
|
+
setResponseStatus(event, 500);
|
|
131
|
+
return { ok: false, error: "processing failed" };
|
|
143
132
|
}
|
|
144
133
|
|
|
145
134
|
return { ok: true };
|
|
@@ -192,20 +192,38 @@ pnpm action add-slide --deckId=<id> --layout content --content "..."
|
|
|
192
192
|
|
|
193
193
|
## Slide Wrapper
|
|
194
194
|
|
|
195
|
-
Every slide's `content` must use
|
|
195
|
+
Every slide's `content` must use the semantic `--deck-*` contract on its outer
|
|
196
|
+
div. Which of the two forms you use depends on whether a design system is
|
|
197
|
+
linked.
|
|
198
|
+
|
|
199
|
+
**A design system is linked.** Inherit the renderer's hydrated variables
|
|
200
|
+
(`--ds-bg`, `--ds-text`, `--ds-text-muted`, `--ds-accent`, `--ds-surface`,
|
|
201
|
+
`--ds-heading-font`, `--ds-body-font`, `--ds-radius`) through the contract
|
|
202
|
+
rather than copying values into individual elements:
|
|
203
|
+
|
|
204
|
+
```html
|
|
205
|
+
<div class="fmd-slide" style="--deck-bg: var(--ds-bg); --deck-ink: var(--ds-text); --deck-muted: var(--ds-text-muted); --deck-accent: var(--ds-accent); --deck-surface: var(--ds-surface); --deck-heading-font: var(--ds-heading-font); --deck-body-font: var(--ds-body-font); --deck-radius: var(--ds-radius); background: var(--deck-bg); color: var(--deck-ink); padding: 64px 80px; display: flex; flex-direction: column; justify-content: flex-start; font-family: var(--deck-body-font);">
|
|
206
|
+
<!-- slide content here -->
|
|
207
|
+
</div>
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
**No design system is linked.** There is no house style to fall back to, and
|
|
211
|
+
the renderer publishes no `--ds-*` tokens beyond the slide's own background.
|
|
212
|
+
Derive a contract from the deck's subject and write it as literal values:
|
|
196
213
|
|
|
197
214
|
```html
|
|
198
|
-
<div class="fmd-slide" style="--deck-bg:
|
|
215
|
+
<div class="fmd-slide" style="--deck-bg: #10261C; --deck-ink: #F2EFE6; --deck-muted: #A8B8AC; --deck-accent: #7FB069; --deck-surface: rgba(255,255,255,0.05); --deck-heading-font: 'Fraunces', Georgia, serif; --deck-body-font: 'Inter', sans-serif; --deck-radius: 4px; background: var(--deck-bg); color: var(--deck-ink); padding: 64px 80px; display: flex; flex-direction: column; justify-content: flex-start; font-family: var(--deck-body-font);">
|
|
199
216
|
<!-- slide content here -->
|
|
200
217
|
</div>
|
|
201
218
|
```
|
|
202
219
|
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
a stock presentation palette, font, or
|
|
220
|
+
That example is a nature-related deck, so colors match the topic of deck. Pick the values once, before the first slide, and repeat the identical contract on every wrapper.
|
|
221
|
+
|
|
222
|
+
Inheriting when nothing is linked is the failure mode to avoid: a
|
|
223
|
+
`var(--ds-accent, currentColor)` reference on an unlinked deck silently
|
|
224
|
+
resolves to a browser default, so the deck reads as unstyled rather than as the
|
|
225
|
+
direction you chose. Never import a stock presentation palette, font, or
|
|
226
|
+
component language as a substitute for choosing.
|
|
209
227
|
|
|
210
228
|
## Fit budget
|
|
211
229
|
|
|
@@ -219,13 +237,9 @@ stack. Keep body text at or above 16px. Never hide overflow with zoom,
|
|
|
219
237
|
may reduce the slide's explicit padding, and that padding must remain intact
|
|
220
238
|
when the saved HTML is rendered.
|
|
221
239
|
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
radius, and accent treatment as semantic `--deck-*` values, then repeat them on
|
|
226
|
-
every wrapper. Build an intentional composition beyond a text dump: use a title
|
|
227
|
-
block, two-column split, metric treatment, rule, callout, visual placeholder,
|
|
228
|
-
or simple diagram where it fits the message. Keep the canvas stable across the
|
|
240
|
+
Build an intentional composition beyond a text dump: use a title block,
|
|
241
|
+
two-column split, metric treatment, rule, callout, visual placeholder, or
|
|
242
|
+
simple diagram where it fits the message. Keep the canvas stable across the
|
|
229
243
|
deck, use accents only for hierarchy or meaning, and do not add decorative
|
|
230
244
|
cards, gradients, fake logos, or shapes without a semantic role.
|
|
231
245
|
|
|
@@ -241,33 +255,34 @@ was rendered and compared.
|
|
|
241
255
|
|
|
242
256
|
## Ready-to-Use Templates
|
|
243
257
|
|
|
244
|
-
Copy and fill in the bracketed values.
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
258
|
+
Copy and fill in the bracketed values. `[WRAPPER STYLE]` stands for the deck
|
|
259
|
+
contract chosen in "Slide Wrapper" - the inherited `var(--ds-*)` form when a
|
|
260
|
+
system is linked, the literal form when none is. It is identical on every
|
|
261
|
+
slide. Inside the slide, read the contract directly (`var(--deck-accent)`), not
|
|
262
|
+
through another layer of fallbacks; the wrapper has already defined every role.
|
|
248
263
|
|
|
249
264
|
### Title Slide
|
|
250
265
|
|
|
251
266
|
```html
|
|
252
|
-
<div class="fmd-slide" style="
|
|
253
|
-
<div style="font-size: 14px; font-weight: 700; letter-spacing: 0.12em; text-transform: uppercase; color: var(--deck-accent
|
|
254
|
-
<h1 style="font-size: 56px; font-weight: 750; color: var(--deck-ink
|
|
255
|
-
<p style="font-size: 20px; color: var(--deck-muted
|
|
267
|
+
<div class="fmd-slide" style="[WRAPPER STYLE] justify-content: center; align-items: flex-start; gap: 18px;">
|
|
268
|
+
<div style="font-size: 14px; font-weight: 700; letter-spacing: 0.12em; text-transform: uppercase; color: var(--deck-accent);">[LABEL OR DATE]</div>
|
|
269
|
+
<h1 style="font-size: 56px; font-weight: 750; color: var(--deck-ink); font-family: var(--deck-heading-font); line-height: 1.05; letter-spacing: -0.04em; margin: 0; max-width: 760px;">[TITLE]</h1>
|
|
270
|
+
<p style="font-size: 20px; color: var(--deck-muted); margin: 4px 0 0;">[SUBTITLE OR PRESENTER]</p>
|
|
256
271
|
</div>
|
|
257
272
|
```
|
|
258
273
|
|
|
259
274
|
### Content or Two-Column Slide
|
|
260
275
|
|
|
261
276
|
```html
|
|
262
|
-
<div class="fmd-slide" style="
|
|
263
|
-
<div style="font-size: 13px; font-weight: 700; letter-spacing: 0.1em; text-transform: uppercase; color: var(--deck-accent
|
|
264
|
-
<h2 style="font-size: 34px; font-weight: 750; color: var(--deck-ink
|
|
277
|
+
<div class="fmd-slide" style="[WRAPPER STYLE] justify-content: flex-start; gap: 18px;">
|
|
278
|
+
<div style="font-size: 13px; font-weight: 700; letter-spacing: 0.1em; text-transform: uppercase; color: var(--deck-accent);">[SECTION LABEL]</div>
|
|
279
|
+
<h2 style="font-size: 34px; font-weight: 750; color: var(--deck-ink); font-family: var(--deck-heading-font); line-height: 1.12; letter-spacing: -0.03em; margin: 0 0 18px;">[SLIDE HEADING]</h2>
|
|
265
280
|
<div style="display: grid; grid-template-columns: 1fr 1fr; gap: 24px; align-items: start;">
|
|
266
281
|
<div style="display: flex; flex-direction: column; gap: 14px;">
|
|
267
|
-
<div style="border-left: 3px solid var(--deck-accent
|
|
268
|
-
<div style="border-left: 3px solid var(--deck-accent
|
|
282
|
+
<div style="border-left: 3px solid var(--deck-accent); padding: 12px 16px; background: var(--deck-surface); border-radius: var(--deck-radius); font-size: 18px; line-height: 1.4;">[KEY POINT]</div>
|
|
283
|
+
<div style="border-left: 3px solid var(--deck-accent); padding: 12px 16px; background: var(--deck-surface); border-radius: var(--deck-radius); font-size: 18px; line-height: 1.4;">[KEY POINT]</div>
|
|
269
284
|
</div>
|
|
270
|
-
<div class="fmd-img-placeholder" style="min-height: 220px; border-radius: var(--deck-radius
|
|
285
|
+
<div class="fmd-img-placeholder" style="min-height: 220px; border-radius: var(--deck-radius);">[VISUAL OR IMAGE DESCRIPTION]</div>
|
|
271
286
|
</div>
|
|
272
287
|
</div>
|
|
273
288
|
```
|
|
@@ -282,7 +297,7 @@ When a slide needs a visual, use this div. It renders as a styled placeholder
|
|
|
282
297
|
and can later be replaced with a generated image:
|
|
283
298
|
|
|
284
299
|
```html
|
|
285
|
-
<div class="fmd-img-placeholder" style="width: 100%; min-height: 220px; border-radius: var(--deck-radius
|
|
300
|
+
<div class="fmd-img-placeholder" style="width: 100%; min-height: 220px; border-radius: var(--deck-radius);">[Description of what image should show]</div>
|
|
286
301
|
```
|
|
287
302
|
|
|
288
303
|
## Bulk Replacement Only
|
|
@@ -16,14 +16,30 @@ registry; do not assume a fixed 1920x1080 canvas.
|
|
|
16
16
|
|
|
17
17
|
## Slide HTML Structure
|
|
18
18
|
|
|
19
|
-
Every slide uses
|
|
19
|
+
Every slide uses the same `--deck-*` wrapper contract. What changes is where
|
|
20
|
+
those values come from.
|
|
21
|
+
|
|
22
|
+
**A design system is linked** - inherit its tokens:
|
|
23
|
+
|
|
24
|
+
```html
|
|
25
|
+
<div class="fmd-slide" style="--deck-bg: var(--ds-bg); --deck-ink: var(--ds-text); --deck-muted: var(--ds-text-muted); --deck-accent: var(--ds-accent); --deck-surface: var(--ds-surface); --deck-heading-font: var(--ds-heading-font); --deck-body-font: var(--ds-body-font); --deck-radius: var(--ds-radius); background: var(--deck-bg); color: var(--deck-ink); padding: 64px 80px; display: flex; flex-direction: column; justify-content: flex-start; font-family: var(--deck-body-font);">
|
|
26
|
+
<!-- Slide content here -->
|
|
27
|
+
</div>
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
**No design system is linked** - write the deck's chosen values as literals:
|
|
20
31
|
|
|
21
32
|
```html
|
|
22
|
-
<div class="fmd-slide" style="--deck-bg:
|
|
33
|
+
<div class="fmd-slide" style="--deck-bg: #10261C; --deck-ink: #F2EFE6; --deck-muted: #A8B8AC; --deck-accent: #7FB069; --deck-surface: rgba(255,255,255,0.05); --deck-heading-font: 'Fraunces', Georgia, serif; --deck-body-font: 'Inter', sans-serif; --deck-radius: 4px; background: var(--deck-bg); color: var(--deck-ink); padding: 64px 80px; display: flex; flex-direction: column; justify-content: flex-start; font-family: var(--deck-body-font);">
|
|
23
34
|
<!-- Slide content here -->
|
|
24
35
|
</div>
|
|
25
36
|
```
|
|
26
37
|
|
|
38
|
+
The renderer publishes `--ds-bg` from the slide's own background, and nothing
|
|
39
|
+
else, when no system is linked. Every other `var(--ds-*, ...)` reference
|
|
40
|
+
resolves to its fallback, so an unlinked deck that inherits instead of baking
|
|
41
|
+
renders as unstyled browser defaults. Bake the values.
|
|
42
|
+
|
|
27
43
|
## Styling Rules
|
|
28
44
|
|
|
29
45
|
These are fallback defaults only. When a design system is linked, its hydrated
|
|
@@ -244,25 +244,36 @@ function standaloneDesignSystemVars(
|
|
|
244
244
|
builderTokenValues,
|
|
245
245
|
);
|
|
246
246
|
const darkBackground = isDarkStandaloneBackground(safeBackground);
|
|
247
|
+
// Must match SlideRenderer: publishing a token an unlinked deck never chose
|
|
248
|
+
// overrides the theme baked into its slide HTML.
|
|
249
|
+
const token = (name: string, value: unknown): string | null => {
|
|
250
|
+
if (typeof value !== "string" || !value.trim()) return null;
|
|
251
|
+
const resolved = safeCssToken(value, "", builderTokenValues);
|
|
252
|
+
return resolved ? `${name}: ${resolved}` : null;
|
|
253
|
+
};
|
|
254
|
+
const designSystemTokens = designSystem
|
|
255
|
+
? [
|
|
256
|
+
token("--ds-text", colors?.text),
|
|
257
|
+
token("--ds-text-muted", colors?.textMuted),
|
|
258
|
+
token("--ds-accent", colors?.accent),
|
|
259
|
+
token("--ds-primary", colors?.primary),
|
|
260
|
+
token("--ds-secondary", colors?.secondary),
|
|
261
|
+
token("--ds-surface", colors?.surface),
|
|
262
|
+
token("--ds-heading-font", typography?.headingFont),
|
|
263
|
+
token("--ds-body-font", typography?.bodyFont),
|
|
264
|
+
token("--ds-radius", borders?.radius),
|
|
265
|
+
].filter((entry): entry is string => entry !== null)
|
|
266
|
+
: [];
|
|
267
|
+
const needsDarkTextOverride =
|
|
268
|
+
darkBackground &&
|
|
269
|
+
(!colors?.text || isDarkStandaloneBackground(colors.text));
|
|
247
270
|
return [
|
|
248
271
|
`--ds-bg: ${safeBackground}`,
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
`--ds-text-muted: ${safeCssToken(colors?.textMuted, darkBackground ? "rgba(255, 255, 255, 0.72)" : "#667085", builderTokenValues)}`,
|
|
255
|
-
// guard:allow-raw-color - standalone export fallback palette
|
|
256
|
-
`--ds-accent: ${safeCssToken(colors?.accent, "#2457D6", builderTokenValues)}`,
|
|
257
|
-
// guard:allow-raw-color - standalone export fallback palette
|
|
258
|
-
`--ds-primary: ${safeCssToken(colors?.primary, "#2457D6", builderTokenValues)}`,
|
|
259
|
-
// guard:allow-raw-color - standalone export fallback palette
|
|
260
|
-
`--ds-secondary: ${safeCssToken(colors?.secondary, "#C85C3A", builderTokenValues)}`,
|
|
261
|
-
// guard:allow-raw-color - standalone export fallback palette
|
|
262
|
-
`--ds-surface: ${safeCssToken(colors?.surface, "#FFFFFF", builderTokenValues)}`,
|
|
263
|
-
`--ds-heading-font: ${safeCssToken(typography?.headingFont, "Inter, sans-serif", builderTokenValues)}`,
|
|
264
|
-
`--ds-body-font: ${safeCssToken(typography?.bodyFont, "Inter, sans-serif", builderTokenValues)}`,
|
|
265
|
-
`--ds-radius: ${safeCssToken(borders?.radius, "14px", builderTokenValues)}`,
|
|
272
|
+
...designSystemTokens,
|
|
273
|
+
...(needsDarkTextOverride
|
|
274
|
+
? // guard:allow-raw-color - readable defaults on explicit dark backgrounds
|
|
275
|
+
[`--ds-text: #FFFFFF`, `--ds-text-muted: rgba(255, 255, 255, 0.72)`]
|
|
276
|
+
: []),
|
|
266
277
|
...Object.entries(builderTokenValues ?? {})
|
|
267
278
|
.filter(
|
|
268
279
|
([name, value]) =>
|
|
@@ -79,27 +79,24 @@ export function mergeDesignSystemData(value: unknown): DesignSystemData {
|
|
|
79
79
|
}
|
|
80
80
|
|
|
81
81
|
export interface DeckDesignSystemResult {
|
|
82
|
-
designSystem: DesignSystemData;
|
|
82
|
+
designSystem: DesignSystemData | undefined;
|
|
83
83
|
designSystemTitle: string | null;
|
|
84
84
|
imageStyleReferenceUrls: string[];
|
|
85
85
|
isLoading: boolean;
|
|
86
86
|
}
|
|
87
87
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
});
|
|
96
|
-
|
|
88
|
+
// Returning a stock palette for an unlinked deck publishes `--ds-*` values the
|
|
89
|
+
// slide's own `var(--ds-*, ...)` fallbacks can never override, so "no design
|
|
90
|
+
// system" renders as one nobody picked.
|
|
91
|
+
export function resolveDeckDesignSystem(
|
|
92
|
+
designSystemId: string | null | undefined,
|
|
93
|
+
data: { title?: string | null; data?: string } | undefined,
|
|
94
|
+
): Omit<DeckDesignSystemResult, "isLoading"> {
|
|
97
95
|
if (!designSystemId || !data?.data) {
|
|
98
96
|
return {
|
|
99
|
-
designSystem:
|
|
97
|
+
designSystem: undefined,
|
|
100
98
|
designSystemTitle: null,
|
|
101
99
|
imageStyleReferenceUrls: [],
|
|
102
|
-
isLoading: false,
|
|
103
100
|
};
|
|
104
101
|
}
|
|
105
102
|
|
|
@@ -109,16 +106,31 @@ export function useDeckDesignSystem(designSystemId?: string | null) {
|
|
|
109
106
|
designSystem: parsed,
|
|
110
107
|
designSystemTitle: data.title ?? null,
|
|
111
108
|
imageStyleReferenceUrls: getDesignSystemImageStyleReferenceUrls(parsed),
|
|
112
|
-
isLoading,
|
|
113
109
|
};
|
|
114
110
|
} catch {
|
|
115
111
|
return {
|
|
116
|
-
designSystem:
|
|
112
|
+
designSystem: undefined,
|
|
117
113
|
designSystemTitle: data.title ?? null,
|
|
118
114
|
imageStyleReferenceUrls: [],
|
|
119
|
-
isLoading,
|
|
120
115
|
};
|
|
121
116
|
}
|
|
122
117
|
}
|
|
123
118
|
|
|
119
|
+
export function useDeckDesignSystem(
|
|
120
|
+
designSystemId?: string | null,
|
|
121
|
+
): DeckDesignSystemResult {
|
|
122
|
+
const { data, isLoading } = useActionQuery<{
|
|
123
|
+
id: string;
|
|
124
|
+
title: string;
|
|
125
|
+
data: string;
|
|
126
|
+
}>("get-design-system", designSystemId ? { id: designSystemId } : undefined, {
|
|
127
|
+
enabled: Boolean(designSystemId),
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
return {
|
|
131
|
+
...resolveDeckDesignSystem(designSystemId, data),
|
|
132
|
+
isLoading: designSystemId ? isLoading : false,
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
|
|
124
136
|
export { DEFAULT_DESIGN_SYSTEM };
|
|
@@ -2817,7 +2817,7 @@ export default function DeckEditor() {
|
|
|
2817
2817
|
questions={questionFlowQuestions ?? []}
|
|
2818
2818
|
onSubmit={handleQuestionSubmit}
|
|
2819
2819
|
onSkip={handleQuestionSkip}
|
|
2820
|
-
designSystem={
|
|
2820
|
+
designSystem={designSystem}
|
|
2821
2821
|
title={questionFlowTitle}
|
|
2822
2822
|
description={questionFlowDescription}
|
|
2823
2823
|
skipLabel={questionFlowSkipLabel}
|
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
|
|
1
|
+
// Neutral on purpose: a tinted canvas default reads as a theme the user never
|
|
2
|
+
// chose.
|
|
3
|
+
export const DEFAULT_SLIDE_BACKGROUND = "#FFFFFF"; // guard:allow-raw-color - default slide canvas fallback
|
|
2
4
|
|
|
3
5
|
// `slide.background` holds either a raw CSS value or a Tailwind arbitrary
|
|
4
6
|
// class (`bg-[...]`), which SlideRenderer applies as a class rather than
|
|
@@ -744,6 +744,14 @@ async function* parseJsonlStream(reader, model, captureContext = {}) {
|
|
|
744
744
|
}
|
|
745
745
|
case "usage": {
|
|
746
746
|
const cacheWrite = (event.cacheCreatedTokens ?? 0) + (event.cacheCreated1hTokens ?? 0);
|
|
747
|
+
if (event.creditsUsed !== undefined &&
|
|
748
|
+
(!Number.isFinite(event.creditsUsed) || event.creditsUsed < 0)) {
|
|
749
|
+
yield gatewayErrorStop({
|
|
750
|
+
error: "Builder gateway returned invalid credit usage",
|
|
751
|
+
errorCode: "builder_gateway_error",
|
|
752
|
+
}, captureContext.creditsLane, captureContext.requestShape);
|
|
753
|
+
return;
|
|
754
|
+
}
|
|
747
755
|
yield {
|
|
748
756
|
type: "usage",
|
|
749
757
|
inputTokens: event.inputTokens ?? 0,
|
|
@@ -752,6 +760,9 @@ async function* parseJsonlStream(reader, model, captureContext = {}) {
|
|
|
752
760
|
? { cacheReadTokens: event.cacheInputTokens }
|
|
753
761
|
: {}),
|
|
754
762
|
...(cacheWrite > 0 ? { cacheWriteTokens: cacheWrite } : {}),
|
|
763
|
+
...(event.creditsUsed !== undefined
|
|
764
|
+
? { builderCreditsUsed: event.creditsUsed }
|
|
765
|
+
: {}),
|
|
755
766
|
};
|
|
756
767
|
break;
|
|
757
768
|
}
|
|
@@ -196,6 +196,7 @@ export type EngineEvent = {
|
|
|
196
196
|
cacheWriteTokens?: number;
|
|
197
197
|
totalTokens?: number;
|
|
198
198
|
reasoningTokens?: number;
|
|
199
|
+
builderCreditsUsed?: number;
|
|
199
200
|
} | {
|
|
200
201
|
/** Final assistant content for the turn. Engines MUST emit this
|
|
201
202
|
* exactly once, immediately before the terminal `stop` event. */
|
|
@@ -571,6 +571,8 @@ export interface AgentLoopUsage {
|
|
|
571
571
|
outputTokens: number;
|
|
572
572
|
cacheReadTokens: number;
|
|
573
573
|
cacheWriteTokens: number;
|
|
574
|
+
builderCreditsUsed?: number;
|
|
575
|
+
engineName?: string;
|
|
574
576
|
model: string;
|
|
575
577
|
/** Number of provider model-stream attempts, including retries. */
|
|
576
578
|
llmCalls?: number;
|