@agent-native/core 0.149.1 → 0.149.2
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/.agents/skills/dashboard-management/SKILL.md +2 -0
- package/corpus/templates/analytics/app/components/dashboard/SqlChart.tsx +150 -46
- package/corpus/templates/analytics/app/global.css +1 -1
- package/corpus/templates/analytics/app/i18n-data.ts +37 -0
- package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/PanelEditorDialog.tsx +1 -1
- package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/SqlChartCard.tsx +32 -0
- package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/types.ts +2 -0
- package/corpus/templates/analytics/changelog/2026-08-10-dashboard-tables-can-be-copied-and-sorted-by-multiple-column.md +6 -0
- package/corpus/templates/analytics/changelog/2026-08-10-full-page-chat-now-uses-the-available-width-up-to-1000px-for.md +6 -0
- package/corpus/templates/assets/app/global.css +1 -1
- package/corpus/templates/assets/changelog/2026-08-10-full-page-chat-now-uses-the-available-width-up-to-1000px-for.md +6 -0
- package/corpus/templates/brain/app/global.css +1 -1
- package/corpus/templates/brain/changelog/2026-08-10-full-page-chat-now-uses-the-available-width-up-to-1000px-for.md +6 -0
- package/corpus/templates/chat/changelog/2026-08-10-full-page-chat-now-uses-the-available-width-up-to-1000px-for.md +6 -0
- package/corpus/templates/clips/changelog/2026-08-10-clips-desktop-sign-in-now-starts-with-magic-links-and-google.md +6 -0
- package/corpus/templates/clips/chrome-extension/src/overlay.css +1 -0
- package/corpus/templates/clips/desktop/src/app.tsx +274 -137
- package/corpus/templates/clips/desktop/src/styles.css +35 -0
- package/corpus/templates/content/app/global.css +1 -0
- package/corpus/templates/crm/app/global.css +1 -1
- package/corpus/templates/crm/changelog/2026-08-10-full-page-chat-now-uses-the-available-width-up-to-1000px-for.md +6 -0
- package/corpus/templates/dispatch/changelog/2026-08-10-full-page-chat-now-uses-the-available-width-up-to-1000px-for.md +6 -0
- package/corpus/templates/factory/changelog/2026-08-10-full-page-chat-now-uses-the-available-width-up-to-1000px-for.md +6 -0
- package/corpus/templates/forms/changelog/2026-08-10-full-page-chat-now-uses-the-available-width-up-to-1000px-for.md +6 -0
- package/corpus/templates/plan/app/global.css +1 -0
- package/corpus/templates/plan/changelog/2026-08-10-full-page-chat-now-uses-the-available-width-up-to-1000px-for.md +6 -0
- package/corpus/templates/plan/server/ui-plan-html.ts +4 -1
- package/corpus/templates/slides/AGENTS.md +11 -10
- package/corpus/templates/slides/actions/add-deck.ts +2 -0
- package/corpus/templates/slides/actions/add-slide.ts +15 -1
- package/corpus/templates/slides/actions/create-deck.ts +18 -6
- package/corpus/templates/slides/actions/get-deck.ts +29 -1
- package/corpus/templates/slides/actions/patch-deck.ts +122 -9
- package/corpus/templates/slides/actions/save-deck.ts +24 -1
- package/corpus/templates/slides/app/components/editor/EditorToolbar.tsx +134 -618
- package/corpus/templates/slides/app/components/editor/ExportMenu.tsx +69 -60
- package/corpus/templates/slides/app/components/editor/GeneratingSlidePreview.tsx +1 -1
- package/corpus/templates/slides/app/components/editor/GoogleDriveConnectionCta.tsx +13 -1
- package/corpus/templates/slides/app/components/editor/NewDeckReferenceStep.tsx +56 -174
- package/corpus/templates/slides/app/components/editor/PromptDialog.tsx +239 -116
- package/corpus/templates/slides/app/components/editor/SlideEditor.tsx +3 -3
- package/corpus/templates/slides/app/components/layout/AgentWorkIndicator.tsx +1 -1
- package/corpus/templates/slides/app/components/layout/Layout.tsx +34 -35
- package/corpus/templates/slides/app/components/layout/layout-route-policy.ts +2 -5
- package/corpus/templates/slides/app/components/onboarding/FirstDeckOnboardingFlow.tsx +45 -40
- package/corpus/templates/slides/app/components/presentation/PresentationView.tsx +63 -16
- package/corpus/templates/slides/app/hooks/use-generating-slide-preview.ts +25 -3
- package/corpus/templates/slides/app/i18n/en-US.ts +1 -0
- package/corpus/templates/slides/app/lib/create-deck-generation.ts +5 -4
- package/corpus/templates/slides/app/lib/recent-references.ts +23 -1
- package/corpus/templates/slides/app/lib/slide-animation-elements.ts +41 -3
- package/corpus/templates/slides/app/pages/DeckEditor.tsx +4 -27
- package/corpus/templates/slides/app/pages/Index.tsx +240 -89
- package/corpus/templates/slides/changelog/2026-08-10-ai-editing-avatar-badge.md +6 -0
- package/corpus/templates/slides/changelog/2026-08-10-generated-decks-keep-a-clear-human-readable-title.md +6 -0
- package/corpus/templates/slides/changelog/2026-08-10-new-deck-creation-now-shows-progress-opens-the-editor-direct.md +6 -0
- package/corpus/templates/slides/changelog/2026-08-10-presentation-reveals-now-follow-valid-targets.md +6 -0
- package/corpus/templates/slides/changelog/2026-08-10-slides-editing-controls-are-consolidated-into-one-focused-me.md +6 -0
- package/corpus/templates/slides/changelog/2026-08-10-slides-now-verify-and-restyle-every-imported-slide-in-one-pa.md +6 -0
- package/corpus/templates/slides/changelog/2026-08-10-slides-only-suggests-a-google-connection-for-relevant-reques.md +6 -0
- package/corpus/templates/slides/changelog/2026-08-10-the-google-slides-import-option-is-now-labeled-slides.md +6 -0
- package/corpus/templates/slides/server/lib/chat-attachments.ts +1 -1
- package/corpus/templates/slides/server/plugins/agent-chat.ts +5 -3
- package/corpus/templates/slides/shared/api.ts +2 -1
- package/corpus/templates/slides/shared/deck-title.ts +135 -0
- package/corpus/templates/tasks/app/components/tasks/TaskListRow.tsx +4 -1
- package/dist/agent/engine/ai-sdk-engine.js +1 -1
- package/dist/agent/production-agent.d.ts +21 -1
- package/dist/agent/production-agent.js +129 -10
- package/dist/agent/tool-approval-store.d.ts +18 -0
- package/dist/agent/tool-approval-store.js +122 -0
- package/dist/agent/types.d.ts +1 -1
- package/dist/cli/code-agent-executor.js +11 -2
- package/dist/client/AgentPanel.js +31 -6
- package/dist/client/AssistantChat.d.ts +3 -2
- package/dist/client/AssistantChat.js +7 -20
- package/dist/client/agent-chat/index.d.ts +1 -1
- package/dist/client/agent-chat-adapter.js +11 -0
- package/dist/client/agent-chat.d.ts +1 -1
- package/dist/client/chat/markdown-renderer.js +14 -2
- package/dist/client/chat/message-components.d.ts +6 -2
- package/dist/client/chat/message-components.js +25 -47
- package/dist/client/chat/tool-call-display.d.ts +3 -5
- package/dist/client/chat/tool-call-display.js +7 -212
- package/dist/client/chat-first/apps-rail.js +3 -3
- package/dist/client/chat-first/browser-pane.js +4 -1
- package/dist/client/chat-first/chat-history.d.ts +3 -1
- package/dist/client/chat-first/chat-history.js +2 -2
- package/dist/client/chat-first/surface-tabs.d.ts +1 -1
- package/dist/client/chat-first/surface-tabs.js +52 -41
- package/dist/client/chat-first/types.d.ts +3 -0
- package/dist/client/chat-first.d.ts +10 -0
- package/dist/client/chat-first.js +37 -2
- package/dist/client/components/ui/message-scroller.js +4 -1
- package/dist/client/i18n.js +3 -1
- package/dist/client/resources/McpConnectionSuggestion.js +9 -1
- package/dist/client/sse-event-processor.js +10 -0
- package/dist/client/use-chat-models.js +156 -80
- package/dist/collab/struct-routes.d.ts +1 -1
- package/dist/integrations/webhook-handler.js +1 -1
- package/dist/observability/routes.d.ts +1 -1
- package/dist/provider-api/actions/custom-provider-registration.d.ts +1 -1
- package/dist/resources/handlers.d.ts +1 -1
- package/dist/secrets/routes.d.ts +9 -9
- package/dist/server/auth.js +262 -31
- package/dist/server/onboarding-html.js +56 -9
- package/dist/shared/reasoning-effort.d.ts +8 -8
- package/dist/shared/reasoning-effort.js +10 -10
- package/dist/shared/streaming-text-smoothing.js +41 -11
- package/dist/styles/agent-native.css +19 -32
- package/dist/templates/chat/changelog/2026-08-10-full-page-chat-now-uses-the-available-width-up-to-1000px-for.md +6 -0
- package/docs/content/template-chat.mdx +1 -1
- package/package.json +2 -2
- package/src/templates/chat/changelog/2026-08-10-full-page-chat-now-uses-the-available-width-up-to-1000px-for.md +6 -0
|
@@ -3,9 +3,12 @@ import { PromptComposer } from "@agent-native/core/client/composer";
|
|
|
3
3
|
import { ensureEmbedAuthFetchInterceptor } from "@agent-native/core/client/host";
|
|
4
4
|
import { useT } from "@agent-native/core/client/i18n";
|
|
5
5
|
import {
|
|
6
|
+
IconArrowLeft,
|
|
6
7
|
IconBrandGoogle,
|
|
7
8
|
IconFileTypePdf,
|
|
9
|
+
IconLoader2,
|
|
8
10
|
IconPresentation,
|
|
11
|
+
IconUpload,
|
|
9
12
|
} from "@tabler/icons-react";
|
|
10
13
|
import { useState, useEffect, useCallback, useRef } from "react";
|
|
11
14
|
import { createPortal } from "react-dom";
|
|
@@ -86,7 +89,7 @@ interface PromptPopoverProps {
|
|
|
86
89
|
placeholder?: string;
|
|
87
90
|
onSkip?: () => void;
|
|
88
91
|
skipLabel?: string;
|
|
89
|
-
onSubmit: (prompt: string, files: UploadedFile[]) => void
|
|
92
|
+
onSubmit: (prompt: string, files: UploadedFile[]) => void | Promise<void>;
|
|
90
93
|
loading?: boolean;
|
|
91
94
|
anchorRef?: React.RefObject<HTMLElement | null>;
|
|
92
95
|
centered?: boolean;
|
|
@@ -125,10 +128,14 @@ export default function PromptPopover({
|
|
|
125
128
|
}: PromptPopoverProps) {
|
|
126
129
|
const t = useT();
|
|
127
130
|
const [uploading, setUploading] = useState(false);
|
|
131
|
+
const [submitting, setSubmitting] = useState(false);
|
|
128
132
|
const [promptText, setPromptText] = useState("");
|
|
129
133
|
const [googleDocContext, setGoogleDocContext] = useState("");
|
|
130
134
|
const [googleSlidesUrl, setGoogleSlidesUrl] = useState("");
|
|
131
|
-
const [
|
|
135
|
+
const [importMode, setImportMode] = useState<PromptImportSource | null>(null);
|
|
136
|
+
const [selectedImportFile, setSelectedImportFile] = useState<File | null>(
|
|
137
|
+
null,
|
|
138
|
+
);
|
|
132
139
|
const [importingSource, setImportingSource] =
|
|
133
140
|
useState<PromptImportSource | null>(null);
|
|
134
141
|
const pdfInputRef = useRef<HTMLInputElement>(null);
|
|
@@ -216,16 +223,20 @@ export default function PromptPopover({
|
|
|
216
223
|
if (files.length > 0 && onBeforeUpload?.(enrichedText, files) === false) {
|
|
217
224
|
return;
|
|
218
225
|
}
|
|
226
|
+
setSubmitting(true);
|
|
219
227
|
try {
|
|
220
228
|
const uploaded = await uploadFiles(files);
|
|
221
|
-
onSubmit(enrichedText, uploaded);
|
|
229
|
+
await onSubmit(enrichedText, uploaded);
|
|
230
|
+
setSubmitting(false);
|
|
222
231
|
} catch (error) {
|
|
232
|
+
setSubmitting(false);
|
|
223
233
|
toast.error(t("raw.uploadFailed"), {
|
|
224
234
|
description:
|
|
225
235
|
error instanceof Error
|
|
226
236
|
? error.message
|
|
227
237
|
: t("raw.uploadAttachedFailed"),
|
|
228
238
|
});
|
|
239
|
+
throw error;
|
|
229
240
|
}
|
|
230
241
|
},
|
|
231
242
|
[googleDocContext, onBeforeUpload, onSubmit, uploadFiles, t],
|
|
@@ -255,11 +266,25 @@ export default function PromptPopover({
|
|
|
255
266
|
const handleFileImport = useCallback(
|
|
256
267
|
(kind: "pdf" | "pptx", file: File | undefined) => {
|
|
257
268
|
if (!file) return;
|
|
269
|
+
setSelectedImportFile(file);
|
|
258
270
|
void runImport({ kind, files: [file] });
|
|
259
271
|
},
|
|
260
272
|
[runImport],
|
|
261
273
|
);
|
|
262
274
|
|
|
275
|
+
const chooseImportMode = useCallback((kind: PromptImportSource) => {
|
|
276
|
+
setImportMode(kind);
|
|
277
|
+
setSelectedImportFile(null);
|
|
278
|
+
setGoogleSlidesUrl("");
|
|
279
|
+
}, []);
|
|
280
|
+
|
|
281
|
+
const returnToPrompt = useCallback(() => {
|
|
282
|
+
if (importingSource) return;
|
|
283
|
+
setImportMode(null);
|
|
284
|
+
setSelectedImportFile(null);
|
|
285
|
+
setGoogleSlidesUrl("");
|
|
286
|
+
}, [importingSource]);
|
|
287
|
+
|
|
263
288
|
const handleGoogleSlidesImport = useCallback(() => {
|
|
264
289
|
const url = googleSlidesUrl.trim();
|
|
265
290
|
if (!url) return;
|
|
@@ -271,13 +296,29 @@ export default function PromptPopover({
|
|
|
271
296
|
setPromptText("");
|
|
272
297
|
setGoogleDocContext("");
|
|
273
298
|
setGoogleSlidesUrl("");
|
|
274
|
-
|
|
299
|
+
setImportMode(null);
|
|
300
|
+
setSelectedImportFile(null);
|
|
275
301
|
setImportingSource(null);
|
|
302
|
+
setSubmitting(false);
|
|
276
303
|
}
|
|
277
304
|
}, [open]);
|
|
278
305
|
|
|
279
306
|
if (!open) return null;
|
|
280
307
|
|
|
308
|
+
const importEnabled = Boolean(onImport && importFromLabel);
|
|
309
|
+
const importFromCopy = importFromLabel?.trim().toLowerCase().startsWith("or ")
|
|
310
|
+
? importFromLabel.trim()
|
|
311
|
+
: importFromLabel
|
|
312
|
+
? `or ${importFromLabel.trim().toLowerCase()}`
|
|
313
|
+
: "";
|
|
314
|
+
const importModeLabel =
|
|
315
|
+
importMode === "google-slides"
|
|
316
|
+
? t("home.googleSlidesReferenceTitle")
|
|
317
|
+
: importMode === "pdf"
|
|
318
|
+
? "PDF"
|
|
319
|
+
: "PPT";
|
|
320
|
+
const importInputRef = importMode === "pdf" ? pdfInputRef : pptxInputRef;
|
|
321
|
+
|
|
281
322
|
const popover = (
|
|
282
323
|
<>
|
|
283
324
|
{centered && (
|
|
@@ -296,7 +337,7 @@ export default function PromptPopover({
|
|
|
296
337
|
>
|
|
297
338
|
<div className="flex items-center justify-between gap-3 px-4 pb-2.5 pt-3.5">
|
|
298
339
|
<span className="text-sm font-medium text-foreground">{title}</span>
|
|
299
|
-
{onSkip && (
|
|
340
|
+
{onSkip && !importMode && !submitting && (
|
|
300
341
|
<button
|
|
301
342
|
type="button"
|
|
302
343
|
onClick={() => {
|
|
@@ -310,132 +351,214 @@ export default function PromptPopover({
|
|
|
310
351
|
)}
|
|
311
352
|
</div>
|
|
312
353
|
|
|
313
|
-
|
|
314
|
-
<
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
initialText={initialText}
|
|
325
|
-
initialTextKey={initialTextKey}
|
|
354
|
+
{importEnabled && (
|
|
355
|
+
<input
|
|
356
|
+
ref={pdfInputRef}
|
|
357
|
+
type="file"
|
|
358
|
+
accept=".pdf,application/pdf"
|
|
359
|
+
className="sr-only"
|
|
360
|
+
aria-label={t("editorToolbar.importFile")}
|
|
361
|
+
onChange={(event) => {
|
|
362
|
+
handleFileImport("pdf", event.target.files?.[0]);
|
|
363
|
+
event.target.value = "";
|
|
364
|
+
}}
|
|
326
365
|
/>
|
|
327
|
-
|
|
366
|
+
)}
|
|
367
|
+
{importEnabled && (
|
|
368
|
+
<input
|
|
369
|
+
ref={pptxInputRef}
|
|
370
|
+
type="file"
|
|
371
|
+
accept=".pptx,application/vnd.openxmlformats-officedocument.presentationml.presentation"
|
|
372
|
+
className="sr-only"
|
|
373
|
+
aria-label={t("editorToolbar.importFile")}
|
|
374
|
+
onChange={(event) => {
|
|
375
|
+
handleFileImport("pptx", event.target.files?.[0]);
|
|
376
|
+
event.target.value = "";
|
|
377
|
+
}}
|
|
378
|
+
/>
|
|
379
|
+
)}
|
|
328
380
|
|
|
329
|
-
{
|
|
330
|
-
<div
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
event.target.value = "";
|
|
355
|
-
}}
|
|
381
|
+
<div className={importEnabled ? "grid" : undefined}>
|
|
382
|
+
<div
|
|
383
|
+
className={[
|
|
384
|
+
importEnabled ? "col-start-1 row-start-1" : "",
|
|
385
|
+
importMode ? "invisible pointer-events-none" : "",
|
|
386
|
+
]
|
|
387
|
+
.filter(Boolean)
|
|
388
|
+
.join(" ")}
|
|
389
|
+
aria-hidden={importMode ? true : undefined}
|
|
390
|
+
>
|
|
391
|
+
<div className="px-2.5 pb-2.5">
|
|
392
|
+
<PromptComposer
|
|
393
|
+
autoFocus
|
|
394
|
+
attachmentsEnabled
|
|
395
|
+
maxDocumentAttachmentBytes={MAX_REFERENCE_FILE_BYTES}
|
|
396
|
+
documentAttachmentLimitLabel="Slides reference files"
|
|
397
|
+
disabled={
|
|
398
|
+
loading || uploading || submitting || Boolean(importMode)
|
|
399
|
+
}
|
|
400
|
+
placeholder={placeholder}
|
|
401
|
+
onSubmit={handleSubmit}
|
|
402
|
+
onTextChange={setPromptText}
|
|
403
|
+
draftScope={draftScope}
|
|
404
|
+
initialText={initialText}
|
|
405
|
+
initialTextKey={initialTextKey}
|
|
356
406
|
/>
|
|
357
|
-
<Button
|
|
358
|
-
type="button"
|
|
359
|
-
variant="ghost"
|
|
360
|
-
size="sm"
|
|
361
|
-
className="h-7 gap-1.5 px-2 text-xs text-muted-foreground hover:text-foreground"
|
|
362
|
-
disabled={importingSource !== null || loading || uploading}
|
|
363
|
-
onClick={() => pdfInputRef.current?.click()}
|
|
364
|
-
>
|
|
365
|
-
<IconFileTypePdf className="size-3.5" />
|
|
366
|
-
PDF
|
|
367
|
-
</Button>
|
|
368
|
-
<Button
|
|
369
|
-
type="button"
|
|
370
|
-
variant="ghost"
|
|
371
|
-
size="sm"
|
|
372
|
-
className="h-7 gap-1.5 px-2 text-xs text-muted-foreground hover:text-foreground"
|
|
373
|
-
disabled={importingSource !== null || loading || uploading}
|
|
374
|
-
onClick={() => setGoogleSlidesInputOpen((current) => !current)}
|
|
375
|
-
>
|
|
376
|
-
<IconBrandGoogle className="size-3.5" />
|
|
377
|
-
{t("home.googleSlidesReferenceTitle")}
|
|
378
|
-
</Button>
|
|
379
|
-
<Button
|
|
380
|
-
type="button"
|
|
381
|
-
variant="ghost"
|
|
382
|
-
size="sm"
|
|
383
|
-
className="h-7 gap-1.5 px-2 text-xs text-muted-foreground hover:text-foreground"
|
|
384
|
-
disabled={importingSource !== null || loading || uploading}
|
|
385
|
-
onClick={() => pptxInputRef.current?.click()}
|
|
386
|
-
>
|
|
387
|
-
<IconPresentation className="size-3.5" />
|
|
388
|
-
PPT
|
|
389
|
-
</Button>
|
|
390
|
-
{importingSource && (
|
|
391
|
-
<span className="ml-auto text-xs text-muted-foreground">
|
|
392
|
-
{importingLabel}
|
|
393
|
-
</span>
|
|
394
|
-
)}
|
|
395
407
|
</div>
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
408
|
+
|
|
409
|
+
{submitting && (
|
|
410
|
+
<div
|
|
411
|
+
className="flex items-center gap-2 border-t border-border/60 px-4 py-2.5 text-xs text-muted-foreground"
|
|
412
|
+
role="status"
|
|
413
|
+
aria-live="polite"
|
|
414
|
+
>
|
|
415
|
+
<IconLoader2 className="size-3.5 animate-spin" />
|
|
416
|
+
<span>{t("raw.uploading")}</span>
|
|
417
|
+
</div>
|
|
418
|
+
)}
|
|
419
|
+
|
|
420
|
+
{importEnabled && (
|
|
421
|
+
<div className="border-t border-border/60 px-4 pb-3 pt-2.5">
|
|
422
|
+
<div className="flex flex-wrap items-center gap-1.5">
|
|
423
|
+
<span className="mr-1 text-xs text-muted-foreground">
|
|
424
|
+
{importFromCopy}
|
|
425
|
+
</span>
|
|
426
|
+
<Button
|
|
427
|
+
type="button"
|
|
428
|
+
variant="ghost"
|
|
429
|
+
size="sm"
|
|
430
|
+
className="h-7 gap-1.5 px-2 text-xs text-muted-foreground hover:text-foreground"
|
|
431
|
+
disabled={loading || uploading || submitting}
|
|
432
|
+
onClick={() => chooseImportMode("pdf")}
|
|
433
|
+
>
|
|
434
|
+
<IconFileTypePdf className="size-3.5" />
|
|
435
|
+
PDF
|
|
436
|
+
</Button>
|
|
413
437
|
<Button
|
|
414
438
|
type="button"
|
|
439
|
+
variant="ghost"
|
|
415
440
|
size="sm"
|
|
416
|
-
className="h-
|
|
417
|
-
disabled={
|
|
418
|
-
|
|
419
|
-
importingSource !== null ||
|
|
420
|
-
loading ||
|
|
421
|
-
uploading
|
|
422
|
-
}
|
|
423
|
-
onClick={handleGoogleSlidesImport}
|
|
441
|
+
className="h-7 gap-1.5 px-2 text-xs text-muted-foreground hover:text-foreground"
|
|
442
|
+
disabled={loading || uploading || submitting}
|
|
443
|
+
onClick={() => chooseImportMode("google-slides")}
|
|
424
444
|
>
|
|
425
|
-
|
|
445
|
+
<IconBrandGoogle className="size-3.5" />
|
|
446
|
+
{t("home.googleSlidesImportLabel")}
|
|
447
|
+
</Button>
|
|
448
|
+
<Button
|
|
449
|
+
type="button"
|
|
450
|
+
variant="ghost"
|
|
451
|
+
size="sm"
|
|
452
|
+
className="h-7 gap-1.5 px-2 text-xs text-muted-foreground hover:text-foreground"
|
|
453
|
+
disabled={loading || uploading || submitting}
|
|
454
|
+
onClick={() => chooseImportMode("pptx")}
|
|
455
|
+
>
|
|
456
|
+
<IconPresentation className="size-3.5" />
|
|
457
|
+
PPT
|
|
426
458
|
</Button>
|
|
427
459
|
</div>
|
|
428
460
|
</div>
|
|
429
461
|
)}
|
|
462
|
+
|
|
463
|
+
{children}
|
|
464
|
+
|
|
465
|
+
<GoogleDocImportHint
|
|
466
|
+
promptText={promptText}
|
|
467
|
+
onSourceContextChange={setGoogleDocContext}
|
|
468
|
+
/>
|
|
430
469
|
</div>
|
|
431
|
-
)}
|
|
432
470
|
|
|
433
|
-
|
|
471
|
+
{importEnabled && importMode && (
|
|
472
|
+
<div className="col-start-1 row-start-1 flex min-h-full flex-col px-4 pb-4 pt-3">
|
|
473
|
+
<div className="flex items-center gap-2">
|
|
474
|
+
<button
|
|
475
|
+
type="button"
|
|
476
|
+
onClick={returnToPrompt}
|
|
477
|
+
disabled={importingSource !== null}
|
|
478
|
+
className="-ms-1 inline-flex size-7 shrink-0 items-center justify-center rounded-md text-muted-foreground transition-colors hover:bg-accent hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50"
|
|
479
|
+
aria-label={
|
|
480
|
+
"Back to prompt" /* i18n-ignore -- local import-flow control label */
|
|
481
|
+
}
|
|
482
|
+
>
|
|
483
|
+
<IconArrowLeft className="size-4" />
|
|
484
|
+
</button>
|
|
485
|
+
<span className="text-sm font-medium text-foreground">
|
|
486
|
+
Import {importModeLabel}
|
|
487
|
+
</span>
|
|
488
|
+
</div>
|
|
434
489
|
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
490
|
+
<div className="flex flex-1 flex-col justify-center gap-3">
|
|
491
|
+
{importMode === "google-slides" ? (
|
|
492
|
+
<>
|
|
493
|
+
<GoogleDriveConnectionCta />
|
|
494
|
+
<div className="flex gap-2">
|
|
495
|
+
<Input
|
|
496
|
+
autoFocus
|
|
497
|
+
type="url"
|
|
498
|
+
value={googleSlidesUrl}
|
|
499
|
+
placeholder={t("home.googleSlidesReferenceUrl")}
|
|
500
|
+
aria-label={t("home.googleSlidesReferenceUrl")}
|
|
501
|
+
className="h-8 text-xs"
|
|
502
|
+
disabled={importingSource !== null || loading}
|
|
503
|
+
onChange={(event) =>
|
|
504
|
+
setGoogleSlidesUrl(event.target.value)
|
|
505
|
+
}
|
|
506
|
+
onKeyDown={(event) => {
|
|
507
|
+
if (event.key === "Enter") handleGoogleSlidesImport();
|
|
508
|
+
}}
|
|
509
|
+
/>
|
|
510
|
+
<Button
|
|
511
|
+
type="button"
|
|
512
|
+
size="sm"
|
|
513
|
+
className="h-8 shrink-0 px-3 text-xs"
|
|
514
|
+
disabled={
|
|
515
|
+
!googleSlidesUrl.trim() ||
|
|
516
|
+
importingSource !== null ||
|
|
517
|
+
loading
|
|
518
|
+
}
|
|
519
|
+
onClick={handleGoogleSlidesImport}
|
|
520
|
+
>
|
|
521
|
+
Import
|
|
522
|
+
</Button>
|
|
523
|
+
</div>
|
|
524
|
+
</>
|
|
525
|
+
) : (
|
|
526
|
+
<>
|
|
527
|
+
<p className="text-xs text-muted-foreground">
|
|
528
|
+
Choose a {importModeLabel} file to open it as a deck.
|
|
529
|
+
</p>
|
|
530
|
+
<Button
|
|
531
|
+
type="button"
|
|
532
|
+
variant="outline"
|
|
533
|
+
className="w-full justify-center gap-2"
|
|
534
|
+
disabled={importingSource !== null || loading}
|
|
535
|
+
onClick={() => importInputRef.current?.click()}
|
|
536
|
+
>
|
|
537
|
+
<IconUpload className="size-4" />
|
|
538
|
+
Upload {importModeLabel}
|
|
539
|
+
</Button>
|
|
540
|
+
{selectedImportFile && (
|
|
541
|
+
<p className="truncate text-center text-xs text-muted-foreground">
|
|
542
|
+
{selectedImportFile.name}
|
|
543
|
+
</p>
|
|
544
|
+
)}
|
|
545
|
+
</>
|
|
546
|
+
)}
|
|
547
|
+
</div>
|
|
548
|
+
|
|
549
|
+
{importingSource && (
|
|
550
|
+
<div
|
|
551
|
+
className="flex items-center justify-center gap-2 text-xs text-muted-foreground"
|
|
552
|
+
role="status"
|
|
553
|
+
aria-live="polite"
|
|
554
|
+
>
|
|
555
|
+
<IconLoader2 className="size-3.5 animate-spin" />
|
|
556
|
+
<span>{importingLabel}</span>
|
|
557
|
+
</div>
|
|
558
|
+
)}
|
|
559
|
+
</div>
|
|
560
|
+
)}
|
|
561
|
+
</div>
|
|
439
562
|
</div>
|
|
440
563
|
</>
|
|
441
564
|
);
|
|
@@ -150,7 +150,7 @@ function ExcalidrawExitButton(props: { onExit: () => void; label: string }) {
|
|
|
150
150
|
<Button
|
|
151
151
|
variant="ghost"
|
|
152
152
|
size="icon"
|
|
153
|
-
className="absolute right-3 top-3 z-20 h-8 w-8 cursor-pointer border border-border bg-popover/95 shadow-lg
|
|
153
|
+
className="absolute right-3 top-3 z-20 h-8 w-8 cursor-pointer border border-border bg-popover/95 shadow-lg"
|
|
154
154
|
onClick={props.onExit}
|
|
155
155
|
aria-label={props.label}
|
|
156
156
|
>
|
|
@@ -652,7 +652,7 @@ function SameSlidePresenceIndicator({ users }: { users: CollabUser[] }) {
|
|
|
652
652
|
? `${users[0].name} is here`
|
|
653
653
|
: `${users.length} others here`;
|
|
654
654
|
return (
|
|
655
|
-
<div className="pointer-events-auto flex items-center gap-1.5 rounded-full border border-border bg-popover/95 py-1 pl-1 pr-2.5 text-xs text-popover-foreground shadow-lg
|
|
655
|
+
<div className="pointer-events-auto flex items-center gap-1.5 rounded-full border border-border bg-popover/95 py-1 pl-1 pr-2.5 text-xs text-popover-foreground shadow-lg">
|
|
656
656
|
<div className="flex items-center">
|
|
657
657
|
{visible.map((u) => (
|
|
658
658
|
<SamePresenceAvatar key={u.email} user={u} />
|
|
@@ -4246,7 +4246,7 @@ export default function SlideEditor({
|
|
|
4246
4246
|
</div>
|
|
4247
4247
|
) : (
|
|
4248
4248
|
<div className="relative h-full bg-background">
|
|
4249
|
-
<div className="absolute right-3 top-3 z-20 flex h-8 items-center gap-0.5 rounded-md border border-border bg-popover/95 px-1 shadow-lg
|
|
4249
|
+
<div className="absolute right-3 top-3 z-20 flex h-8 items-center gap-0.5 rounded-md border border-border bg-popover/95 px-1 shadow-lg">
|
|
4250
4250
|
<Tooltip>
|
|
4251
4251
|
<TooltipTrigger asChild>
|
|
4252
4252
|
<Button
|
|
@@ -87,7 +87,7 @@ export function AgentWorkIndicator() {
|
|
|
87
87
|
|
|
88
88
|
return (
|
|
89
89
|
<div className="pointer-events-none fixed bottom-4 left-1/2 z-50 w-[calc(100vw-2rem)] max-w-sm -translate-x-1/2 md:bottom-5">
|
|
90
|
-
<div className="pointer-events-auto flex items-center justify-between gap-3 rounded-lg border border-border bg-popover/95 px-3 py-2 text-popover-foreground shadow-xl shadow-black/20
|
|
90
|
+
<div className="pointer-events-auto flex items-center justify-between gap-3 rounded-lg border border-border bg-popover/95 px-3 py-2 text-popover-foreground shadow-xl shadow-black/20">
|
|
91
91
|
<div className="flex min-w-0 items-center gap-2">
|
|
92
92
|
<IconLoader2 className="h-4 w-4 shrink-0 animate-spin text-[#609FF8]" />
|
|
93
93
|
<span className="truncate text-sm font-medium">
|
|
@@ -12,13 +12,12 @@ import { useSidebarCollapsed } from "@/hooks/use-sidebar-collapsed";
|
|
|
12
12
|
import { TAB_ID } from "@/lib/tab-id";
|
|
13
13
|
import { cn } from "@/lib/utils";
|
|
14
14
|
|
|
15
|
-
import { GoogleDriveConnectionCta } from "../editor/GoogleDriveConnectionCta";
|
|
16
15
|
import { AgentWorkIndicator } from "./AgentWorkIndicator";
|
|
17
16
|
import { Header } from "./Header";
|
|
18
17
|
import {
|
|
19
|
-
getDeckChatScopeLabel,
|
|
20
18
|
getEffectiveSlidesSidebarCollapsed,
|
|
21
19
|
isSlidesEditorRoute,
|
|
20
|
+
shouldShowSlidesAppSidebar,
|
|
22
21
|
} from "./layout-route-policy";
|
|
23
22
|
import { Sidebar } from "./Sidebar";
|
|
24
23
|
|
|
@@ -50,26 +49,24 @@ export function Layout({ children }: LayoutProps) {
|
|
|
50
49
|
useState<EditorSidebarOverride | null>(null);
|
|
51
50
|
const { collapsed: sidebarCollapsed, setCollapsed: setSidebarCollapsed } =
|
|
52
51
|
useSidebarCollapsed();
|
|
53
|
-
const { decks,
|
|
52
|
+
const { decks, loading: decksLoading } = useDecks();
|
|
54
53
|
const isEmptyDecksState =
|
|
55
54
|
location.pathname === "/" && !decksLoading && decks.length === 0;
|
|
56
55
|
|
|
57
56
|
// Scope new chats to the deck the user is currently editing. The route
|
|
58
57
|
// is `/deck/:id`; everywhere else (list, presentation) leaves
|
|
59
|
-
// scope null so chats stay in the general pool.
|
|
60
|
-
//
|
|
61
|
-
// streamed in — once the title arrives the badge updates in place.
|
|
58
|
+
// scope null so chats stay in the general pool. Keep the visible label
|
|
59
|
+
// semantic so an imported deck id never leaks into the composer chip.
|
|
62
60
|
const deckScope = useMemo(() => {
|
|
63
61
|
const match = location.pathname.match(/^\/deck\/([^/]+)/);
|
|
64
62
|
const deckId = match?.[1];
|
|
65
63
|
if (!deckId) return null;
|
|
66
|
-
const deck = getDeck(deckId);
|
|
67
64
|
return {
|
|
68
65
|
type: "deck" as const,
|
|
69
66
|
id: deckId,
|
|
70
|
-
label:
|
|
67
|
+
label: t("agent.thisDeck"),
|
|
71
68
|
};
|
|
72
|
-
}, [location.pathname,
|
|
69
|
+
}, [location.pathname, t]);
|
|
73
70
|
|
|
74
71
|
useEffect(() => {
|
|
75
72
|
setSidebarOpen(false);
|
|
@@ -84,6 +81,7 @@ export function Layout({ children }: LayoutProps) {
|
|
|
84
81
|
}, []);
|
|
85
82
|
|
|
86
83
|
const ownToolbar = pageHasOwnToolbar(location.pathname);
|
|
84
|
+
const showAppSidebar = shouldShowSlidesAppSidebar(location.pathname);
|
|
87
85
|
const editorSidebarOverrideForLocation =
|
|
88
86
|
editorSidebarOverride?.locationKey === location.key
|
|
89
87
|
? editorSidebarOverride.collapsed
|
|
@@ -120,35 +118,36 @@ export function Layout({ children }: LayoutProps) {
|
|
|
120
118
|
agentPageHref="/settings/agent"
|
|
121
119
|
suppressFirstRunOnboarding={isSlidesEditorRoute(location.pathname)}
|
|
122
120
|
composerSlot={<CreativeContextComposerChip />}
|
|
123
|
-
threadFooterSlot={<GoogleDriveConnectionCta />}
|
|
124
121
|
>
|
|
125
122
|
<div className="agent-layout-shell flex h-screen w-full overflow-hidden bg-background text-foreground">
|
|
126
|
-
{
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
<div
|
|
134
|
-
className={cn(
|
|
135
|
-
"agent-layout-left-drawer fixed inset-y-0 start-0 z-50 transition-transform duration-200 ease-out md:static md:z-auto md:transition-none",
|
|
136
|
-
sidebarOpen
|
|
137
|
-
? "translate-x-0"
|
|
138
|
-
: "-translate-x-full rtl:translate-x-full md:translate-x-0 md:rtl:translate-x-0",
|
|
123
|
+
{!isEmptyDecksState && showAppSidebar && (
|
|
124
|
+
<>
|
|
125
|
+
{sidebarOpen && (
|
|
126
|
+
<div
|
|
127
|
+
className="fixed inset-0 z-40 bg-foreground/50 md:hidden"
|
|
128
|
+
onClick={() => setSidebarOpen(false)}
|
|
129
|
+
/>
|
|
139
130
|
)}
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
131
|
+
<div
|
|
132
|
+
className={cn(
|
|
133
|
+
"agent-layout-left-drawer fixed inset-y-0 start-0 z-50 transition-transform duration-200 ease-out md:static md:z-auto md:transition-none",
|
|
134
|
+
sidebarOpen
|
|
135
|
+
? "translate-x-0"
|
|
136
|
+
: "-translate-x-full rtl:translate-x-full md:translate-x-0 md:rtl:translate-x-0",
|
|
137
|
+
)}
|
|
138
|
+
>
|
|
139
|
+
<Sidebar
|
|
140
|
+
collapsed={effectiveSidebarCollapsed && !sidebarOpen}
|
|
141
|
+
// In the mobile drawer the sidebar is forced expanded, so the
|
|
142
|
+
// desktop collapse toggle would be a silent no-op (worse: it'd
|
|
143
|
+
// mutate the desktop preference). Hide it while the drawer is
|
|
144
|
+
// open.
|
|
145
|
+
onToggleCollapsed={
|
|
146
|
+
sidebarOpen ? undefined : toggleSidebarCollapsed
|
|
147
|
+
}
|
|
148
|
+
/>
|
|
149
|
+
</div>
|
|
150
|
+
</>
|
|
152
151
|
)}
|
|
153
152
|
<div className="agent-layout-main-surface flex h-full min-w-0 flex-1 flex-col overflow-hidden">
|
|
154
153
|
{/* Mobile-only nav strip with hamburger — only when there's no page toolbar */}
|
|
@@ -2,11 +2,8 @@ export function isSlidesEditorRoute(pathname: string): boolean {
|
|
|
2
2
|
return /^\/deck\/[^/]+\/?$/.test(pathname);
|
|
3
3
|
}
|
|
4
4
|
|
|
5
|
-
export function
|
|
6
|
-
|
|
7
|
-
untitledDeckLabel: string,
|
|
8
|
-
): string {
|
|
9
|
-
return deckTitle === "Untitled Deck" ? untitledDeckLabel : deckTitle || "";
|
|
5
|
+
export function shouldShowSlidesAppSidebar(pathname: string): boolean {
|
|
6
|
+
return !isSlidesEditorRoute(pathname);
|
|
10
7
|
}
|
|
11
8
|
|
|
12
9
|
export function getEffectiveSlidesSidebarCollapsed({
|