@agent-native/core 0.70.0 → 0.70.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +15 -15
- package/corpus/README.md +2 -2
- package/corpus/core/CHANGELOG.md +61 -0
- package/corpus/core/docs/design/durable-agent-runs.md +217 -0
- package/corpus/core/package.json +1 -1
- package/corpus/core/src/action.ts +89 -0
- package/corpus/core/src/agent/model-config.ts +0 -2
- package/corpus/core/src/agent/run-loop-with-resume.ts +48 -0
- package/corpus/core/src/cli/skills.ts +2 -2
- package/corpus/core/src/client/AgentPanel.tsx +5 -3
- package/corpus/core/src/client/AssistantChat.tsx +8 -0
- package/corpus/core/src/client/client-surface.ts +41 -0
- package/corpus/core/src/client/composer/TiptapComposer.tsx +123 -12
- package/corpus/core/src/client/feedback-context.ts +4 -0
- package/corpus/core/src/client/index.ts +1 -0
- package/corpus/core/src/client/sse-event-processor.ts +7 -1
- package/corpus/core/src/mcp/build-server.ts +57 -1
- package/corpus/core/src/mcp/embed-app.ts +86 -2
- package/corpus/core/src/mcp/oauth-route.ts +109 -13
- package/corpus/core/src/server/core-routes-plugin.ts +78 -0
- package/corpus/core/src/server/onboarding-html.ts +35 -0
- package/corpus/core/src/templates/workspace-core/.agents/skills/reliable-mutations/SKILL.md +72 -0
- package/corpus/templates/analytics/.agents/skills/dashboard-management/SKILL.md +22 -0
- package/corpus/templates/analytics/AGENTS.md +8 -1
- package/corpus/templates/analytics/actions/install-dashboard-template.ts +97 -2
- package/corpus/templates/analytics/actions/update-dashboard.ts +36 -7
- package/corpus/templates/analytics/app/components/dashboard/SqlChart.tsx +30 -3
- package/corpus/templates/analytics/app/components/layout/Header.tsx +0 -1
- package/corpus/templates/analytics/app/components/layout/Sidebar.tsx +2 -4
- package/corpus/templates/analytics/app/hooks/use-navigation-state.ts +0 -3
- package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/SqlChartCard.tsx +32 -2
- package/corpus/templates/analytics/changelog/2026-06-23-long-dashboard-and-analysis-names-in-the-sidebar-now-truncat.md +6 -0
- package/corpus/templates/analytics/changelog/2026-06-23-open-any-dashboard-chart-full-screen-in-a-modal-from-the-pan.md +6 -0
- package/corpus/templates/assets/actions/generate-image.ts +24 -0
- package/corpus/templates/assets/app/components/layout/Sidebar.tsx +2 -2
- package/corpus/templates/assets/app/global.css +17 -1
- package/corpus/templates/assets/app/routes/_index.tsx +94 -14
- package/corpus/templates/assets/changelog/2026-06-23-chat-dates-in-the-sidebar-no-longer-wrap-onto-two-lines.md +6 -0
- package/corpus/templates/assets/changelog/2026-06-23-clicking-image-video-or-refine-on-the-start-screen-now-drops.md +6 -0
- package/corpus/templates/assets/changelog/2026-06-23-pick-your-image-generation-model-right-from-the-chat-model-m.md +6 -0
- package/corpus/templates/clips/app/components/library/library-grid.tsx +1 -20
- package/corpus/templates/clips/app/components/recorder/countdown-overlay.tsx +72 -48
- package/corpus/templates/clips/app/lib/countdown-audio-cue.ts +35 -17
- package/corpus/templates/clips/changelog/2026-06-23-removed-select-button-from-library-hover-a-clip-to-select-it.md +6 -0
- package/corpus/templates/clips/changelog/2026-06-23-shared-video-links-now-play-for-anyone-without-signing-in.md +6 -0
- package/corpus/templates/clips/changelog/2026-06-23-skip-or-cancel-the-recording-countdown-with-buttons-beside-t.md +6 -0
- package/corpus/templates/clips/changelog/2026-06-23-the-desktop-camera-bubble-now-looks-sharp-the-instant-it-ope.md +6 -0
- package/corpus/templates/clips/changelog/2026-06-23-the-desktop-recording-widget-now-grows-downward-only-when-yo.md +6 -0
- package/corpus/templates/clips/changelog/2026-06-23-the-recording-start-sound-is-now-a-softer-more-modern-chime.md +6 -0
- package/corpus/templates/clips/chrome-extension/public/manifest.json +17 -8
- package/corpus/templates/clips/chrome-extension/src/background.ts +769 -180
- package/corpus/templates/clips/chrome-extension/src/content-script.ts +177 -0
- package/corpus/templates/clips/chrome-extension/src/offscreen.ts +466 -329
- package/corpus/templates/clips/chrome-extension/src/overlay.css +377 -0
- package/corpus/templates/clips/chrome-extension/src/overlay.html +12 -0
- package/corpus/templates/clips/chrome-extension/src/overlay.ts +330 -0
- package/corpus/templates/clips/chrome-extension/src/permission.html +227 -0
- package/corpus/templates/clips/chrome-extension/src/permission.ts +105 -0
- package/corpus/templates/clips/chrome-extension/src/popup.html +3 -0
- package/corpus/templates/clips/chrome-extension/src/popup.ts +50 -2
- package/corpus/templates/clips/chrome-extension/src/styles.css +50 -0
- package/corpus/templates/clips/chrome-extension/vite.config.ts +3 -0
- package/corpus/templates/clips/desktop/src/lib/audio-cue.ts +41 -24
- package/corpus/templates/clips/desktop/src/lib/bubble-webrtc.ts +119 -2
- package/corpus/templates/clips/desktop/src/overlays/countdown.tsx +21 -2
- package/corpus/templates/clips/desktop/src/styles.css +70 -2
- package/corpus/templates/clips/desktop/src-tauri/src/clips/mod.rs +73 -0
- package/corpus/templates/clips/server/routes/api/video/[recordingId].get.ts +34 -5
- package/corpus/templates/content/AGENTS.md +20 -3
- package/corpus/templates/content/actions/_database-utils.ts +15 -0
- package/corpus/templates/content/actions/_property-utils.ts +371 -4
- package/corpus/templates/content/actions/configure-document-property.ts +35 -1
- package/corpus/templates/content/actions/create-content-database.ts +5 -1
- package/corpus/templates/content/actions/delete-document-property.ts +52 -2
- package/corpus/templates/content/actions/duplicate-document-property.ts +34 -17
- package/corpus/templates/content/actions/reorder-document-property.ts +79 -0
- package/corpus/templates/content/actions/set-document-property.ts +39 -0
- package/corpus/templates/content/app/components/editor/DocumentBlockFields.tsx +806 -0
- package/corpus/templates/content/app/components/editor/DocumentDatabase.tsx +226 -68
- package/corpus/templates/content/app/components/editor/DocumentEditor.tsx +59 -30
- package/corpus/templates/content/app/components/editor/DocumentProperties.tsx +25 -1
- package/corpus/templates/content/app/components/editor/blockFieldSaveController.ts +180 -0
- package/corpus/templates/content/app/components/editor/blockFieldSaveRegistry.ts +179 -0
- package/corpus/templates/content/app/components/editor/previewDocumentSaveController.ts +244 -0
- package/corpus/templates/content/app/components/editor/previewDocumentSaveRegistry.ts +132 -0
- package/corpus/templates/content/app/global.css +9 -0
- package/corpus/templates/content/app/hooks/use-document-properties.ts +21 -0
- package/corpus/templates/content/server/db/schema.ts +30 -0
- package/corpus/templates/content/server/plugins/db.ts +87 -0
- package/corpus/templates/content/shared/api.ts +7 -0
- package/corpus/templates/content/shared/properties.ts +109 -0
- package/corpus/templates/forms/.agents/skills/form-responses/SKILL.md +10 -0
- package/corpus/templates/forms/actions/export-responses.ts +6 -0
- package/corpus/templates/forms/actions/list-responses.ts +2 -0
- package/corpus/templates/forms/actions/response-insights.ts +2 -0
- package/corpus/templates/forms/app/pages/ResponsesPage.tsx +138 -1
- package/corpus/templates/forms/changelog/2026-06-23-response-tables-now-show-the-page-each-submission-came-from-.md +6 -0
- package/corpus/templates/forms/changelog/2026-06-23-response-tables-now-show-whether-feedback-came-from-the-web-.md +6 -0
- package/corpus/templates/forms/server/db/schema.ts +7 -0
- package/corpus/templates/forms/server/handlers/submissions.ts +19 -2
- package/corpus/templates/forms/server/lib/integrations.ts +76 -2
- package/corpus/templates/forms/server/plugins/db.ts +20 -0
- package/corpus/templates/forms/shared/types.ts +12 -0
- package/corpus/templates/plan/app/components/plan/wireframe/html-artboard.css +3 -1
- package/corpus/templates/plan/changelog/2026-06-23-large-diagrams-in-a-plan-now-scroll-within-their-block-inste.md +6 -0
- package/dist/action.js +87 -0
- package/dist/action.js.map +1 -1
- package/dist/agent/engine/builder-engine.d.ts +1 -1
- package/dist/agent/engine/builder-engine.d.ts.map +1 -1
- package/dist/agent/model-config.d.ts +2 -2
- package/dist/agent/model-config.d.ts.map +1 -1
- package/dist/agent/model-config.js +0 -2
- package/dist/agent/model-config.js.map +1 -1
- package/dist/agent/run-loop-with-resume.d.ts +13 -0
- package/dist/agent/run-loop-with-resume.d.ts.map +1 -1
- package/dist/agent/run-loop-with-resume.js +44 -0
- package/dist/agent/run-loop-with-resume.js.map +1 -1
- package/dist/cli/skills.js +2 -2
- package/dist/cli/skills.js.map +1 -1
- package/dist/client/AgentPanel.d.ts.map +1 -1
- package/dist/client/AgentPanel.js +5 -3
- package/dist/client/AgentPanel.js.map +1 -1
- package/dist/client/AssistantChat.d.ts +6 -1
- package/dist/client/AssistantChat.d.ts.map +1 -1
- package/dist/client/AssistantChat.js +2 -2
- package/dist/client/AssistantChat.js.map +1 -1
- package/dist/client/client-surface.d.ts +17 -0
- package/dist/client/client-surface.d.ts.map +1 -0
- package/dist/client/client-surface.js +24 -0
- package/dist/client/client-surface.js.map +1 -0
- package/dist/client/composer/TiptapComposer.d.ts +26 -1
- package/dist/client/composer/TiptapComposer.d.ts.map +1 -1
- package/dist/client/composer/TiptapComposer.js +27 -5
- package/dist/client/composer/TiptapComposer.js.map +1 -1
- package/dist/client/feedback-context.d.ts +3 -0
- package/dist/client/feedback-context.d.ts.map +1 -1
- package/dist/client/feedback-context.js +2 -0
- package/dist/client/feedback-context.js.map +1 -1
- package/dist/client/index.d.ts +1 -0
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js +1 -0
- package/dist/client/index.js.map +1 -1
- package/dist/client/sse-event-processor.d.ts.map +1 -1
- package/dist/client/sse-event-processor.js +7 -1
- package/dist/client/sse-event-processor.js.map +1 -1
- package/dist/mcp/build-server.d.ts +21 -0
- package/dist/mcp/build-server.d.ts.map +1 -1
- package/dist/mcp/build-server.js +43 -1
- package/dist/mcp/build-server.js.map +1 -1
- package/dist/mcp/embed-app.d.ts.map +1 -1
- package/dist/mcp/embed-app.js +86 -2
- package/dist/mcp/embed-app.js.map +1 -1
- package/dist/mcp/oauth-route.d.ts.map +1 -1
- package/dist/mcp/oauth-route.js +95 -12
- package/dist/mcp/oauth-route.js.map +1 -1
- package/dist/server/core-routes-plugin.d.ts.map +1 -1
- package/dist/server/core-routes-plugin.js +63 -0
- package/dist/server/core-routes-plugin.js.map +1 -1
- package/dist/server/onboarding-html.d.ts.map +1 -1
- package/dist/server/onboarding-html.js +35 -0
- package/dist/server/onboarding-html.js.map +1 -1
- package/dist/templates/workspace-core/.agents/skills/reliable-mutations/SKILL.md +72 -0
- package/docs/design/durable-agent-runs.md +217 -0
- package/package.json +1 -1
- package/src/templates/workspace-core/.agents/skills/reliable-mutations/SKILL.md +72 -0
- package/corpus/templates/analytics/app/pages/About.tsx +0 -108
- package/corpus/templates/analytics/app/routes/about.tsx +0 -9
|
@@ -378,6 +378,11 @@ function resolveScope() {
|
|
|
378
378
|
return { orgId, email };
|
|
379
379
|
}
|
|
380
380
|
|
|
381
|
+
/** Resulting panel count, used for the proof-of-done return summary. */
|
|
382
|
+
function countPanels(config: Record<string, unknown>): number {
|
|
383
|
+
return Array.isArray(config.panels) ? config.panels.length : 0;
|
|
384
|
+
}
|
|
385
|
+
|
|
381
386
|
/**
|
|
382
387
|
* Push a config update through the collab layer so open dashboard editors
|
|
383
388
|
* receive the change in real time. Seeds the collab state if it doesn't
|
|
@@ -407,11 +412,19 @@ async function syncToCollab(
|
|
|
407
412
|
|
|
408
413
|
export default defineAction({
|
|
409
414
|
description:
|
|
410
|
-
"Edit a SQL dashboard config (scope-aware). Prefer this over raw db-patch on the settings table — " +
|
|
415
|
+
"Edit a SQL dashboard config (scope-aware) atomically in ONE call. Prefer this over raw db-patch on the settings table — " +
|
|
411
416
|
"it resolves org vs. user scope correctly so the edit lands on the row the UI actually renders. " +
|
|
412
|
-
"
|
|
413
|
-
"
|
|
414
|
-
"
|
|
417
|
+
"BATCH ALL EDITS INTO A SINGLE CALL. Never call this action repeatedly in a loop: hosted agent runs have a ~40s budget, and many sequential update-dashboard calls time out mid-way and leave the dashboard in a partial state even though earlier calls looked like they succeeded. Put every change you want to make into one `ops` array (or one `config`). " +
|
|
418
|
+
"`ops` is an array of { op, path, from?, value? } applied in order in a single atomic save. " +
|
|
419
|
+
"`op` is one of: set | replace | remove | insert | move | move-before. " +
|
|
420
|
+
"`path` is a JSON Pointer into the config (e.g. `/panels/3` is the 4th panel, `/panels/3/title` is its title, `/name` is the dashboard name). The special index `-` means the end of an array: `/panels/-` appends. " +
|
|
421
|
+
"`value` is the panel or object to set/insert. `from` is the source JSON Pointer for move / move-before. " +
|
|
422
|
+
"To ADD N panels in one call, pass N entries of { op: 'insert', path: '/panels/-', value: <panel> }. " +
|
|
423
|
+
"To reorder: [{ op: 'move', from: '/panels/2', path: '/panels/0' }]. " +
|
|
424
|
+
"To edit a field: [{ op: 'replace', path: '/panels/0/title', value: 'Events by Day' }]. " +
|
|
425
|
+
"Use `config` to replace the entire dashboard config in one call. Provide `ops` OR `config`, not both. " +
|
|
426
|
+
"To add a shipped catalog template's panels to an existing dashboard, prefer `install-dashboard-template` with `mergePanels: true` — it appends the template's panels in one call without you having to author each panel. " +
|
|
427
|
+
"The result includes `panelCount` (resulting number of panels), `appliedOps` (count), and a `summary` string — use them as proof-of-done and report the new panel count back to the user instead of assuming success. " +
|
|
415
428
|
"The UI auto-refreshes after this action — do NOT call `refresh-screen`.",
|
|
416
429
|
schema: z.object({
|
|
417
430
|
dashboardId: z
|
|
@@ -440,8 +453,11 @@ export default defineAction({
|
|
|
440
453
|
)
|
|
441
454
|
.optional()
|
|
442
455
|
.describe(
|
|
443
|
-
"Array of JSON-patch-style ops applied in order (or a JSON string). " +
|
|
444
|
-
"
|
|
456
|
+
"Array of JSON-patch-style ops applied in order in ONE atomic save (or a JSON string). " +
|
|
457
|
+
"Each op is { op, path, from?, value? }. op ∈ set|replace|remove|insert|move|move-before. " +
|
|
458
|
+
"path is a JSON Pointer (e.g. '/panels/3', and '/panels/-' appends to the end). " +
|
|
459
|
+
"Add N panels at once with N {op:'insert', path:'/panels/-', value:<panel>} entries. " +
|
|
460
|
+
"Example reorder: [{op:'move', from:'/panels/2', path:'/panels/0'}].",
|
|
445
461
|
),
|
|
446
462
|
config: z
|
|
447
463
|
.preprocess(
|
|
@@ -482,6 +498,7 @@ export default defineAction({
|
|
|
482
498
|
if (sqlError) throw new Error(sqlError);
|
|
483
499
|
await upsertDashboard(args.dashboardId, "sql", args.config, ctx);
|
|
484
500
|
await syncToCollab(args.dashboardId, args.config);
|
|
501
|
+
const panelCount = countPanels(args.config);
|
|
485
502
|
return {
|
|
486
503
|
id: args.dashboardId,
|
|
487
504
|
dashboardId: args.dashboardId,
|
|
@@ -489,6 +506,11 @@ export default defineAction({
|
|
|
489
506
|
typeof args.config.name === "string"
|
|
490
507
|
? args.config.name
|
|
491
508
|
: args.dashboardId,
|
|
509
|
+
// Proof-of-done: lead with verifiable state so the agent/UI can
|
|
510
|
+
// confirm the new shape cheaply instead of assuming success.
|
|
511
|
+
panelCount,
|
|
512
|
+
appliedOps: 0,
|
|
513
|
+
summary: `Replaced dashboard "${args.dashboardId}"; it now has ${panelCount} panel(s).`,
|
|
492
514
|
config: args.config,
|
|
493
515
|
urlPath: `/dashboards/${args.dashboardId}`,
|
|
494
516
|
deepLink: buildDeepLink({
|
|
@@ -528,10 +550,16 @@ export default defineAction({
|
|
|
528
550
|
);
|
|
529
551
|
await syncToCollab(args.dashboardId, root as Record<string, unknown>);
|
|
530
552
|
|
|
553
|
+
const panelCount = countPanels(root as Record<string, unknown>);
|
|
531
554
|
return {
|
|
532
555
|
id: args.dashboardId,
|
|
533
556
|
dashboardId: args.dashboardId,
|
|
534
557
|
name: typeof root.name === "string" ? root.name : args.dashboardId,
|
|
558
|
+
// Proof-of-done: lead with verifiable state (op count + resulting panel
|
|
559
|
+
// count) so the agent/UI can confirm success without re-fetching.
|
|
560
|
+
panelCount,
|
|
561
|
+
appliedOps: details.length,
|
|
562
|
+
summary: `Applied ${details.length} op(s); dashboard "${args.dashboardId}" now has ${panelCount} panel(s).`,
|
|
535
563
|
config: root,
|
|
536
564
|
urlPath: `/dashboards/${args.dashboardId}`,
|
|
537
565
|
deepLink: buildDeepLink({
|
|
@@ -541,7 +569,8 @@ export default defineAction({
|
|
|
541
569
|
}),
|
|
542
570
|
message:
|
|
543
571
|
`Dashboard "${args.dashboardId}" updated. ` +
|
|
544
|
-
`Applied ${details.length} op(s): ${details.join("; ")}
|
|
572
|
+
`Applied ${details.length} op(s): ${details.join("; ")}. ` +
|
|
573
|
+
`Now ${panelCount} panel(s).`,
|
|
545
574
|
};
|
|
546
575
|
},
|
|
547
576
|
link: ({ result }) => {
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
|
+
createContext,
|
|
2
3
|
useCallback,
|
|
4
|
+
useContext,
|
|
3
5
|
useEffect,
|
|
4
6
|
useMemo,
|
|
5
7
|
useState,
|
|
@@ -248,6 +250,20 @@ function SeriesLegend({
|
|
|
248
250
|
);
|
|
249
251
|
}
|
|
250
252
|
|
|
253
|
+
// When a chart renders inside the full-screen modal it should grow to fill the
|
|
254
|
+
// available space rather than the fixed 250px card height. ChartFrame reads
|
|
255
|
+
// this via context so we avoid threading a prop through every renderer
|
|
256
|
+
// (line/area/bar/pie all share ChartFrame).
|
|
257
|
+
const ChartFillHeightContext = createContext(false);
|
|
258
|
+
|
|
259
|
+
export function ChartFillHeight({ children }: { children: ReactNode }) {
|
|
260
|
+
return (
|
|
261
|
+
<ChartFillHeightContext.Provider value={true}>
|
|
262
|
+
{children}
|
|
263
|
+
</ChartFillHeightContext.Provider>
|
|
264
|
+
);
|
|
265
|
+
}
|
|
266
|
+
|
|
251
267
|
function ChartFrame({
|
|
252
268
|
panel,
|
|
253
269
|
legendKeys,
|
|
@@ -259,13 +275,24 @@ function ChartFrame({
|
|
|
259
275
|
colors: string[];
|
|
260
276
|
children: ReactNode;
|
|
261
277
|
}) {
|
|
278
|
+
const fill = useContext(ChartFillHeightContext);
|
|
279
|
+
const chartHeight = fill ? "h-full min-h-[250px]" : "h-[250px]";
|
|
280
|
+
|
|
262
281
|
if (!usesPrometheusPresentation(panel)) {
|
|
263
|
-
return
|
|
282
|
+
return (
|
|
283
|
+
<div className={`${chartHeight} w-full overflow-visible`}>{children}</div>
|
|
284
|
+
);
|
|
264
285
|
}
|
|
265
286
|
|
|
266
287
|
return (
|
|
267
|
-
<div
|
|
268
|
-
|
|
288
|
+
<div
|
|
289
|
+
className={`flex w-full flex-col overflow-hidden ${fill ? "h-full" : ""}`}
|
|
290
|
+
>
|
|
291
|
+
<div
|
|
292
|
+
className={`${chartHeight} w-full overflow-visible ${fill ? "flex-1" : ""}`}
|
|
293
|
+
>
|
|
294
|
+
{children}
|
|
295
|
+
</div>
|
|
269
296
|
<SeriesLegend keys={legendKeys} colors={colors} panel={panel} />
|
|
270
297
|
</div>
|
|
271
298
|
);
|
|
@@ -16,7 +16,6 @@ import {
|
|
|
16
16
|
IconChevronDown,
|
|
17
17
|
IconSun,
|
|
18
18
|
IconMoon,
|
|
19
|
-
IconInfoCircle,
|
|
20
19
|
IconTrash,
|
|
21
20
|
IconDots,
|
|
22
21
|
IconLoader2,
|
|
@@ -146,7 +145,6 @@ import { CSS } from "@dnd-kit/utilities";
|
|
|
146
145
|
const bottomItems = [
|
|
147
146
|
{ icon: IconUsers, label: "Team", href: "/team" },
|
|
148
147
|
{ icon: IconSettings, label: "Settings", href: "/settings" },
|
|
149
|
-
{ icon: IconInfoCircle, label: "About", href: "/about" },
|
|
150
148
|
];
|
|
151
149
|
|
|
152
150
|
function getStoredBoolean(key: string, fallback: boolean): boolean {
|
|
@@ -1947,7 +1945,7 @@ export function Sidebar({ mobile }: { mobile?: boolean } = {}) {
|
|
|
1947
1945
|
</Link>
|
|
1948
1946
|
|
|
1949
1947
|
{/* Dashboards section */}
|
|
1950
|
-
<div className="group/section space-y-1">
|
|
1948
|
+
<div className="group/section min-w-0 space-y-1">
|
|
1951
1949
|
<div
|
|
1952
1950
|
className={cn(
|
|
1953
1951
|
"flex w-full min-w-0 items-center rounded-lg transition-all hover:text-primary",
|
|
@@ -2050,7 +2048,7 @@ export function Sidebar({ mobile }: { mobile?: boolean } = {}) {
|
|
|
2050
2048
|
</div>
|
|
2051
2049
|
|
|
2052
2050
|
{/* Analyses section */}
|
|
2053
|
-
<div className="group/section space-y-1">
|
|
2051
|
+
<div className="group/section min-w-0 space-y-1">
|
|
2054
2052
|
<div
|
|
2055
2053
|
className={cn(
|
|
2056
2054
|
"flex w-full min-w-0 items-center rounded-lg transition-all hover:text-primary",
|
|
@@ -63,8 +63,6 @@ export function useNavigationState() {
|
|
|
63
63
|
state.view = "catalog";
|
|
64
64
|
} else if (pathname === "/settings") {
|
|
65
65
|
state.view = "settings";
|
|
66
|
-
} else if (pathname === "/about") {
|
|
67
|
-
state.view = "about";
|
|
68
66
|
}
|
|
69
67
|
|
|
70
68
|
return state;
|
|
@@ -84,7 +82,6 @@ export function useNavigationState() {
|
|
|
84
82
|
if (cmd.view === "ask") return "/ask";
|
|
85
83
|
if (cmd.view === "settings") return "/settings";
|
|
86
84
|
if (cmd.view === "overview") return "/overview";
|
|
87
|
-
if (cmd.view === "about") return "/about";
|
|
88
85
|
return "/";
|
|
89
86
|
},
|
|
90
87
|
onNavigate: (_command, path) => {
|
|
@@ -5,6 +5,7 @@ import {
|
|
|
5
5
|
IconArrowsMaximize,
|
|
6
6
|
IconArrowsMinimize,
|
|
7
7
|
IconDotsVertical,
|
|
8
|
+
IconMaximize,
|
|
8
9
|
IconPencil,
|
|
9
10
|
IconTrash,
|
|
10
11
|
IconCode,
|
|
@@ -32,9 +33,15 @@ import {
|
|
|
32
33
|
AlertDialogHeader,
|
|
33
34
|
AlertDialogTitle,
|
|
34
35
|
} from "@/components/ui/alert-dialog";
|
|
36
|
+
import {
|
|
37
|
+
Dialog,
|
|
38
|
+
DialogContent,
|
|
39
|
+
DialogHeader,
|
|
40
|
+
DialogTitle,
|
|
41
|
+
} from "@/components/ui/dialog";
|
|
35
42
|
import { useSortable } from "@dnd-kit/sortable";
|
|
36
43
|
import { CSS } from "@dnd-kit/utilities";
|
|
37
|
-
import { SqlChart } from "@/components/dashboard/SqlChart";
|
|
44
|
+
import { ChartFillHeight, SqlChart } from "@/components/dashboard/SqlChart";
|
|
38
45
|
import { ViewSqlPopover } from "./ViewSqlPopover";
|
|
39
46
|
import type { SqlPanel } from "./types";
|
|
40
47
|
|
|
@@ -76,6 +83,7 @@ export function SqlChartCard({
|
|
|
76
83
|
} = useSortable({ id: panel.id, disabled: !editable });
|
|
77
84
|
|
|
78
85
|
const [confirmOpen, setConfirmOpen] = useState(false);
|
|
86
|
+
const [expanded, setExpanded] = useState(false);
|
|
79
87
|
const [exportCsv, setExportCsv] = useState<(() => void) | null>(null);
|
|
80
88
|
const [shouldLoadData, setShouldLoadData] = useState(
|
|
81
89
|
panel.chartType === "section",
|
|
@@ -229,7 +237,9 @@ export function SqlChartCard({
|
|
|
229
237
|
);
|
|
230
238
|
}
|
|
231
239
|
|
|
232
|
-
|
|
240
|
+
// Every non-section panel exposes at least the Full screen view action, so the
|
|
241
|
+
// options menu always renders — including on read-only / shared dashboards.
|
|
242
|
+
const showPanelMenu = true;
|
|
233
243
|
|
|
234
244
|
return (
|
|
235
245
|
<div
|
|
@@ -259,6 +269,13 @@ export function SqlChartCard({
|
|
|
259
269
|
<TooltipContent>Panel options</TooltipContent>
|
|
260
270
|
</Tooltip>
|
|
261
271
|
<DropdownMenuContent align="end" className="w-44">
|
|
272
|
+
<DropdownMenuItem onSelect={() => setExpanded(true)}>
|
|
273
|
+
<IconMaximize className="h-4 w-4 mr-2" />
|
|
274
|
+
Full screen
|
|
275
|
+
</DropdownMenuItem>
|
|
276
|
+
{editable || panel.chartType === "table" ? (
|
|
277
|
+
<DropdownMenuSeparator />
|
|
278
|
+
) : null}
|
|
262
279
|
{panel.chartType === "table" && (
|
|
263
280
|
<DropdownMenuItem
|
|
264
281
|
disabled={!exportCsv}
|
|
@@ -346,6 +363,19 @@ export function SqlChartCard({
|
|
|
346
363
|
</CardContent>
|
|
347
364
|
</Card>
|
|
348
365
|
|
|
366
|
+
<Dialog open={expanded} onOpenChange={setExpanded}>
|
|
367
|
+
<DialogContent className="flex h-[90vh] w-[95vw] max-w-[1400px] flex-col gap-4">
|
|
368
|
+
<DialogHeader className="shrink-0 pr-8 text-left">
|
|
369
|
+
<DialogTitle className="truncate">{panel.title}</DialogTitle>
|
|
370
|
+
</DialogHeader>
|
|
371
|
+
<div className="flex min-h-0 flex-1 flex-col overflow-auto">
|
|
372
|
+
<ChartFillHeight>
|
|
373
|
+
<SqlChart panel={panel} resolvedSql={resolvedSql} loadData />
|
|
374
|
+
</ChartFillHeight>
|
|
375
|
+
</div>
|
|
376
|
+
</DialogContent>
|
|
377
|
+
</Dialog>
|
|
378
|
+
|
|
349
379
|
{editable ? (
|
|
350
380
|
<AlertDialog open={confirmOpen} onOpenChange={setConfirmOpen}>
|
|
351
381
|
<AlertDialogContent>
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { defineAction } from "@agent-native/core";
|
|
2
2
|
import {
|
|
3
|
+
readAppState,
|
|
3
4
|
writeAppState,
|
|
4
5
|
deleteAppState,
|
|
5
6
|
} from "@agent-native/core/application-state";
|
|
@@ -55,6 +56,28 @@ function resolveModelForTier(
|
|
|
55
56
|
: "gemini-3.1-flash-image";
|
|
56
57
|
}
|
|
57
58
|
|
|
59
|
+
/**
|
|
60
|
+
* The user's default image model, chosen from the composer's model picker and
|
|
61
|
+
* persisted in per-user application state. Used as a fallback when no explicit
|
|
62
|
+
* model, tier, or preset model is supplied. Returns undefined when unset or
|
|
63
|
+
* invalid so the hardcoded default still applies.
|
|
64
|
+
*/
|
|
65
|
+
async function readUserDefaultImageModel(): Promise<ImageModel | undefined> {
|
|
66
|
+
try {
|
|
67
|
+
const stored = await readAppState("imageGenerationModel");
|
|
68
|
+
const model = stored?.model;
|
|
69
|
+
if (
|
|
70
|
+
typeof model === "string" &&
|
|
71
|
+
(IMAGE_MODELS as readonly string[]).includes(model)
|
|
72
|
+
) {
|
|
73
|
+
return model as ImageModel;
|
|
74
|
+
}
|
|
75
|
+
} catch {
|
|
76
|
+
// No request context or read failure — fall back to defaults below.
|
|
77
|
+
}
|
|
78
|
+
return undefined;
|
|
79
|
+
}
|
|
80
|
+
|
|
58
81
|
export default defineAction({
|
|
59
82
|
description:
|
|
60
83
|
"Generate one brand-consistent image from a library. This is synchronous for images and returns the final asset with preview/download/embed URLs. Use generate-image-batch for multiple independent slots; do not poll image runs after this action returns.",
|
|
@@ -226,6 +249,7 @@ export default defineAction({
|
|
|
226
249
|
const resolvedModel = (args.model ??
|
|
227
250
|
resolveModelForTier(resolvedTier, category) ??
|
|
228
251
|
preset?.model ??
|
|
252
|
+
(await readUserDefaultImageModel()) ??
|
|
229
253
|
"gemini-3.1-flash-image") as (typeof IMAGE_MODELS)[number];
|
|
230
254
|
const resolvedCategories =
|
|
231
255
|
args.categories ??
|
|
@@ -305,8 +305,8 @@ function AssetsChatsSection() {
|
|
|
305
305
|
{threadTitle(thread)}
|
|
306
306
|
</span>
|
|
307
307
|
</button>
|
|
308
|
-
<div className="relative flex
|
|
309
|
-
<span className="text-[11px] text-muted-foreground/75 transition-opacity group-hover:opacity-0 group-focus-within:opacity-0">
|
|
308
|
+
<div className="relative flex w-auto min-w-7 shrink-0 items-center justify-end pr-1">
|
|
309
|
+
<span className="whitespace-nowrap text-[11px] tabular-nums text-muted-foreground/75 transition-opacity group-hover:opacity-0 group-focus-within:opacity-0">
|
|
310
310
|
{isActive ? "" : formatThreadAge(threadUpdatedAt(thread))}
|
|
311
311
|
</span>
|
|
312
312
|
<DropdownMenu>
|
|
@@ -135,7 +135,7 @@
|
|
|
135
135
|
gap: 0.5rem;
|
|
136
136
|
}
|
|
137
137
|
|
|
138
|
-
.assets-create-chat-pill-row
|
|
138
|
+
.assets-create-chat-pill-row button {
|
|
139
139
|
display: inline-flex;
|
|
140
140
|
align-items: center;
|
|
141
141
|
gap: 0.375rem;
|
|
@@ -147,6 +147,22 @@
|
|
|
147
147
|
color: hsl(var(--muted-foreground));
|
|
148
148
|
font-size: 0.75rem;
|
|
149
149
|
font-weight: 500;
|
|
150
|
+
cursor: pointer;
|
|
151
|
+
transition:
|
|
152
|
+
background-color 0.15s ease,
|
|
153
|
+
color 0.15s ease,
|
|
154
|
+
border-color 0.15s ease;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
.assets-create-chat-pill-row button:hover {
|
|
158
|
+
border-color: hsl(var(--foreground) / 0.25);
|
|
159
|
+
background: hsl(var(--accent));
|
|
160
|
+
color: hsl(var(--foreground));
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
.assets-create-chat-pill-row button:focus-visible {
|
|
164
|
+
outline: 2px solid hsl(var(--ring));
|
|
165
|
+
outline-offset: 2px;
|
|
150
166
|
}
|
|
151
167
|
|
|
152
168
|
.assets-create-chat-panel
|
|
@@ -1,12 +1,49 @@
|
|
|
1
|
-
import { useEffect } from "react";
|
|
1
|
+
import { useCallback, useEffect, useState } from "react";
|
|
2
2
|
import {
|
|
3
3
|
AgentChatSurface,
|
|
4
4
|
getBrowserTabId,
|
|
5
5
|
markAgentChatHomeHandoff,
|
|
6
|
+
readClientAppState,
|
|
7
|
+
sendToAgentChat,
|
|
8
|
+
writeClientAppState,
|
|
6
9
|
} from "@agent-native/core/client";
|
|
7
10
|
import { IconPhoto, IconSparkles, IconVideo } from "@tabler/icons-react";
|
|
8
11
|
import { ASSETS_CHAT_STORAGE_KEY } from "@/lib/chat";
|
|
9
12
|
|
|
13
|
+
// The composer's model picker shows the chat LLM (Claude/OpenAI/Gemini). The
|
|
14
|
+
// Assets app also drives a separate *image* model, so we surface it in the same
|
|
15
|
+
// menu — otherwise "Claude" reads as the image generator, which it isn't. The
|
|
16
|
+
// choice persists in per-user application state so the generate-image action
|
|
17
|
+
// (server-side) can read it as the default model. Values must be valid
|
|
18
|
+
// IMAGE_MODELS ids from shared/api.
|
|
19
|
+
const IMAGE_MODEL_STATE_KEY = "imageGenerationModel";
|
|
20
|
+
const DEFAULT_IMAGE_MODEL = "gemini-3.1-flash-image";
|
|
21
|
+
const IMAGE_MODEL_OPTIONS = [
|
|
22
|
+
{ value: "gemini-3-pro-image", label: "Gemini 3 Pro · best quality" },
|
|
23
|
+
{ value: "gemini-3.1-flash-image", label: "Gemini 3.1 Flash · fast" },
|
|
24
|
+
{ value: "gemini-2.5-flash-image", label: "Gemini 2.5 Flash" },
|
|
25
|
+
] as const;
|
|
26
|
+
|
|
27
|
+
// Empty-state starters. Clicking one prefills the composer (without sending) so
|
|
28
|
+
// the user can finish the thought instead of staring at a chip that does
|
|
29
|
+
// nothing. `submit: false` = prefill only; `openSidebar: false` keeps focus on
|
|
30
|
+
// the page-level Create surface.
|
|
31
|
+
const CHAT_STARTERS = [
|
|
32
|
+
{
|
|
33
|
+
key: "image",
|
|
34
|
+
Icon: IconPhoto,
|
|
35
|
+
label: "image",
|
|
36
|
+
prompt: "Create an image of ",
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
key: "video",
|
|
40
|
+
Icon: IconVideo,
|
|
41
|
+
label: "video",
|
|
42
|
+
prompt: "Create a video of ",
|
|
43
|
+
},
|
|
44
|
+
{ key: "refine", Icon: IconSparkles, label: "refine", prompt: "Refine " },
|
|
45
|
+
] as const;
|
|
46
|
+
|
|
10
47
|
const SEO_TITLE =
|
|
11
48
|
"Agent-Native Assets - Open Source AI asset library for brand-safe images and video";
|
|
12
49
|
const SEO_DESCRIPTION =
|
|
@@ -25,6 +62,8 @@ export function meta() {
|
|
|
25
62
|
}
|
|
26
63
|
|
|
27
64
|
export default function CreatePage() {
|
|
65
|
+
const [imageModel, setImageModel] = useState<string>(DEFAULT_IMAGE_MODEL);
|
|
66
|
+
|
|
28
67
|
useEffect(() => {
|
|
29
68
|
function handleChatRunning(event: Event) {
|
|
30
69
|
const detail = (event as CustomEvent).detail;
|
|
@@ -38,6 +77,34 @@ export default function CreatePage() {
|
|
|
38
77
|
window.removeEventListener("agentNative.chatRunning", handleChatRunning);
|
|
39
78
|
}, []);
|
|
40
79
|
|
|
80
|
+
// Hydrate the saved image-model default so the picker reflects the user's
|
|
81
|
+
// last choice across sessions.
|
|
82
|
+
useEffect(() => {
|
|
83
|
+
let cancelled = false;
|
|
84
|
+
void readClientAppState<{ model?: string }>(IMAGE_MODEL_STATE_KEY)
|
|
85
|
+
.then((state) => {
|
|
86
|
+
const stored = state?.model;
|
|
87
|
+
if (
|
|
88
|
+
!cancelled &&
|
|
89
|
+
stored &&
|
|
90
|
+
IMAGE_MODEL_OPTIONS.some((option) => option.value === stored)
|
|
91
|
+
) {
|
|
92
|
+
setImageModel(stored);
|
|
93
|
+
}
|
|
94
|
+
})
|
|
95
|
+
.catch(() => {});
|
|
96
|
+
return () => {
|
|
97
|
+
cancelled = true;
|
|
98
|
+
};
|
|
99
|
+
}, []);
|
|
100
|
+
|
|
101
|
+
const handleImageModelChange = useCallback((value: string) => {
|
|
102
|
+
setImageModel(value);
|
|
103
|
+
void writeClientAppState(IMAGE_MODEL_STATE_KEY, { model: value }).catch(
|
|
104
|
+
() => {},
|
|
105
|
+
);
|
|
106
|
+
}, []);
|
|
107
|
+
|
|
41
108
|
return (
|
|
42
109
|
<div className="flex h-full min-h-0 flex-col bg-background">
|
|
43
110
|
<AgentChatSurface
|
|
@@ -47,6 +114,15 @@ export default function CreatePage() {
|
|
|
47
114
|
defaultMode="chat"
|
|
48
115
|
storageKey={ASSETS_CHAT_STORAGE_KEY}
|
|
49
116
|
browserTabId={getBrowserTabId()}
|
|
117
|
+
imageModelMenu={{
|
|
118
|
+
value: imageModel,
|
|
119
|
+
options: IMAGE_MODEL_OPTIONS.map((option) => ({
|
|
120
|
+
value: option.value,
|
|
121
|
+
label: option.label,
|
|
122
|
+
})),
|
|
123
|
+
onChange: handleImageModelChange,
|
|
124
|
+
label: "Image model",
|
|
125
|
+
}}
|
|
50
126
|
showHeader={false}
|
|
51
127
|
showTabBar={false}
|
|
52
128
|
dynamicSuggestions={false}
|
|
@@ -63,19 +139,23 @@ export default function CreatePage() {
|
|
|
63
139
|
Start with a hero image, product reveal, reference edit, or a
|
|
64
140
|
direction you want to explore.
|
|
65
141
|
</p>
|
|
66
|
-
<div className="assets-create-chat-pill-row"
|
|
67
|
-
|
|
68
|
-
<
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
142
|
+
<div className="assets-create-chat-pill-row">
|
|
143
|
+
{CHAT_STARTERS.map(({ key, Icon, label, prompt }) => (
|
|
144
|
+
<button
|
|
145
|
+
key={key}
|
|
146
|
+
type="button"
|
|
147
|
+
onClick={() =>
|
|
148
|
+
sendToAgentChat({
|
|
149
|
+
message: prompt,
|
|
150
|
+
submit: false,
|
|
151
|
+
openSidebar: false,
|
|
152
|
+
})
|
|
153
|
+
}
|
|
154
|
+
>
|
|
155
|
+
<Icon className="size-3.5" />
|
|
156
|
+
{label}
|
|
157
|
+
</button>
|
|
158
|
+
))}
|
|
79
159
|
</div>
|
|
80
160
|
</div>
|
|
81
161
|
}
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { useEffect, useMemo, useRef, useState } from "react";
|
|
2
2
|
import { toast } from "sonner";
|
|
3
|
-
import { cn } from "@/lib/utils";
|
|
4
3
|
import {
|
|
5
4
|
useFolders,
|
|
6
5
|
useRecordings,
|
|
@@ -34,7 +33,6 @@ import {
|
|
|
34
33
|
DialogTitle,
|
|
35
34
|
DialogFooter,
|
|
36
35
|
} from "@/components/ui/dialog";
|
|
37
|
-
import { IconChecks } from "@tabler/icons-react";
|
|
38
36
|
import { ShareRecordingDialog } from "@/components/player/share-dialog";
|
|
39
37
|
|
|
40
38
|
interface LibraryGridProps {
|
|
@@ -107,7 +105,7 @@ export function LibraryGrid({
|
|
|
107
105
|
}: LibraryGridProps) {
|
|
108
106
|
const [sort, setSort] = useState<SortKey>("recent");
|
|
109
107
|
const [selected, setSelected] = useState<Set<string>>(new Set());
|
|
110
|
-
const
|
|
108
|
+
const selectionMode = selected.size > 0;
|
|
111
109
|
const [renamingRec, setRenamingRec] = useState<RecordingSummary | null>(null);
|
|
112
110
|
const [renameValue, setRenameValue] = useState("");
|
|
113
111
|
const [sharingRec, setSharingRec] = useState<RecordingSummary | null>(null);
|
|
@@ -202,7 +200,6 @@ export function LibraryGrid({
|
|
|
202
200
|
|
|
203
201
|
const clearSelection = () => {
|
|
204
202
|
setSelected(new Set());
|
|
205
|
-
setSelectionMode(false);
|
|
206
203
|
};
|
|
207
204
|
|
|
208
205
|
const moveSelected = async (targetFolderId: string | null) => {
|
|
@@ -337,22 +334,6 @@ export function LibraryGrid({
|
|
|
337
334
|
</div>
|
|
338
335
|
<div className="ml-auto flex shrink-0 items-center gap-2">
|
|
339
336
|
{extraActions}
|
|
340
|
-
<Button
|
|
341
|
-
variant={selectionMode ? "default" : "outline"}
|
|
342
|
-
size="sm"
|
|
343
|
-
className={cn(
|
|
344
|
-
"h-8 gap-1.5",
|
|
345
|
-
selectionMode &&
|
|
346
|
-
"bg-primary text-primary-foreground hover:bg-primary/90",
|
|
347
|
-
)}
|
|
348
|
-
onClick={() => {
|
|
349
|
-
setSelectionMode((v) => !v);
|
|
350
|
-
if (selectionMode) setSelected(new Set());
|
|
351
|
-
}}
|
|
352
|
-
>
|
|
353
|
-
<IconChecks className="h-3.5 w-3.5" />
|
|
354
|
-
Select
|
|
355
|
-
</Button>
|
|
356
337
|
<SortMenu value={sort} onChange={setSort} />
|
|
357
338
|
</div>
|
|
358
339
|
</PageHeader>
|