@kywk/open-slide-core 2.3.0 → 2.3.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.
Files changed (51) hide show
  1. package/LICENSE +21 -0
  2. package/dist/{build-CZ6Qtrng.js → build-46dO14h1.js} +1 -1
  3. package/dist/cli/bin.js +4 -4
  4. package/dist/{config-CpmGqpQi.d.ts → config-CDrYgaEj.d.ts} +6 -2
  5. package/dist/{config-B5LArqpp.js → config-KyYvWqq-.js} +2061 -355
  6. package/dist/{dev-CCgdakbZ.js → dev-C0n-o0-c.js} +2 -2
  7. package/dist/index.d.ts +3 -3
  8. package/dist/index.js +73 -32
  9. package/dist/locale/index.d.ts +1 -1
  10. package/dist/locale/index.js +1 -1
  11. package/dist/{preview-AwAqvG17.js → preview-CDWAGSfB.js} +1 -1
  12. package/dist/{types-CvAqFqsY.d.ts → types-DFK02XVv.d.ts} +110 -0
  13. package/dist/{update-C_RlnKu4.js → update-CqZqQRQk.js} +128 -7
  14. package/dist/vite/index.d.ts +1 -1
  15. package/dist/vite/index.js +1 -1
  16. package/dist/{zh-tw-B7_Oqv2q.js → zh-tw-B1pkKQsc.js} +535 -0
  17. package/package.json +15 -9
  18. package/src/agent/prompt.ts +141 -0
  19. package/src/agent/provider.ts +123 -0
  20. package/src/agent/types.ts +151 -0
  21. package/src/app/components/agent/agent-dock.tsx +35 -0
  22. package/src/app/components/agent/agent-markdown.tsx +45 -0
  23. package/src/app/components/agent/agent-panel.tsx +692 -0
  24. package/src/app/components/agent/agent-presence.tsx +74 -0
  25. package/src/app/components/agent/composer.tsx +120 -0
  26. package/src/app/components/agent/connect-view.tsx +184 -0
  27. package/src/app/components/agent/history-view.tsx +135 -0
  28. package/src/app/components/agent/provider-logo.tsx +34 -0
  29. package/src/app/components/agent/transcript.tsx +609 -0
  30. package/src/app/components/command/slide-command-menu.tsx +10 -0
  31. package/src/app/components/inline-svg.tsx +52 -21
  32. package/src/app/components/inspector/comment-layer.tsx +20 -1
  33. package/src/app/components/inspector/image-crop-dialog.tsx +1 -5
  34. package/src/app/components/inspector/inspector-panel.tsx +1 -1
  35. package/src/app/components/mermaid.tsx +42 -11
  36. package/src/app/components/panel/editor-sidebar.tsx +85 -16
  37. package/src/app/components/panel/panel-shell.tsx +2 -2
  38. package/src/app/components/panel/resize-handle.tsx +129 -0
  39. package/src/app/lib/agent/use-agent.ts +543 -0
  40. package/src/app/lib/use-auto-grow.ts +15 -0
  41. package/src/app/lib/utils.ts +4 -0
  42. package/src/app/routes/home.tsx +43 -7
  43. package/src/app/routes/slide.tsx +103 -104
  44. package/src/app/styles.css +170 -0
  45. package/src/editing/placeholder-id.ts +1 -0
  46. package/src/locale/en.ts +106 -0
  47. package/src/locale/es.ts +108 -0
  48. package/src/locale/ja.ts +99 -0
  49. package/src/locale/types.ts +109 -0
  50. package/src/locale/zh-cn.ts +96 -0
  51. package/src/locale/zh-tw.ts +96 -0
@@ -14,6 +14,7 @@ import {
14
14
  MoreHorizontal,
15
15
  Play,
16
16
  Presentation,
17
+ Sparkles,
17
18
  Terminal,
18
19
  } from 'lucide-react';
19
20
  import {
@@ -21,12 +22,16 @@ import {
21
22
  type RefObject,
22
23
  useCallback,
23
24
  useEffect,
25
+ useLayoutEffect,
24
26
  useMemo,
25
27
  useRef,
26
28
  useState,
27
29
  } from 'react';
28
30
  import { Link, useNavigate, useParams, useSearchParams } from 'react-router-dom';
29
31
  import { toast } from 'sonner';
32
+ import { AgentToggleButton } from '@/components/agent/agent-dock';
33
+ import { AgentPanel } from '@/components/agent/agent-panel';
34
+ import { AgentPresence } from '@/components/agent/agent-presence';
30
35
  import { AssetView } from '@/components/asset-view';
31
36
  import { HistoryProvider } from '@/components/history-provider';
32
37
  import { CommentLayer } from '@/components/inspector/comment-layer';
@@ -40,6 +45,7 @@ import {
40
45
  } from '@/components/inspector/inspector-provider';
41
46
  import { SaveBar } from '@/components/inspector/save-bar';
42
47
  import { EditorSidebar } from '@/components/panel/editor-sidebar';
48
+ import { ResizeHandle, useStoredWidth } from '@/components/panel/resize-handle';
43
49
  import { DesignProvider } from '@/components/style-panel/design-provider';
44
50
  import { DesignToggleButton } from '@/components/style-panel/style-panel';
45
51
  import { Button, buttonVariants } from '@/components/ui/button';
@@ -57,6 +63,7 @@ import {
57
63
  } from '@/components/ui/dropdown-menu';
58
64
  import { Tabs, TabsList, TabsTrigger } from '@/components/ui/tabs';
59
65
  import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/components/ui/tooltip';
66
+ import { setDockOpen, useAgentActivity, useAgentDock } from '@/lib/agent/use-agent';
60
67
  import { useFolders } from '@/lib/folders';
61
68
  import {
62
69
  hasModifier,
@@ -74,6 +81,7 @@ import { format, useLocale } from '@/lib/use-locale';
74
81
  import { useMediaQuery } from '@/lib/use-media-query';
75
82
  import { useWheelPageNavigation } from '@/lib/use-wheel-page-navigation';
76
83
  import { cn } from '@/lib/utils';
84
+ import type { AgentTurnContext } from '../../agent/types';
77
85
  import { SlideCommandMenu } from '../components/command/slide-command-menu';
78
86
  import { PdfProgressToast, PptxProgressToast } from '../components/export-progress-toast';
79
87
  import { NotesDrawer } from '../components/notes-drawer';
@@ -132,6 +140,13 @@ export function Slide() {
132
140
  const [designOpen, setDesignOpen] = useState(false);
133
141
  const [overviewOpen, setOverviewOpen] = useState(false);
134
142
  const [commandOpen, setCommandOpen] = useState(false);
143
+ const { open: agentOpen, toggle: toggleAgent } = useAgentDock();
144
+ useLayoutEffect(() => {
145
+ if (agentOpen) setDesignOpen(false);
146
+ }, [agentOpen]);
147
+ useLayoutEffect(() => {
148
+ if (designOpen) setDockOpen(false);
149
+ }, [designOpen]);
135
150
  const [, setWarmedTick] = useState(0);
136
151
  const handleAssetsWarmed = useCallback(() => {
137
152
  markDeckWarmed(slideId);
@@ -195,6 +210,16 @@ export function Slide() {
195
210
  [pageCount, setSearchParams],
196
211
  );
197
212
 
213
+ // While an agent writes this deck page by page, jump to each page as it
214
+ // lands so the user can follow along.
215
+ const agentActivity = useAgentActivity(slideId);
216
+ const agentWriting = agentActivity !== null;
217
+ const seenPageCount = useRef(pageCount);
218
+ useEffect(() => {
219
+ if (agentWriting && pageCount > seenPageCount.current) goTo(pageCount - 1);
220
+ seenPageCount.current = pageCount;
221
+ }, [agentWriting, pageCount, goTo]);
222
+
198
223
  const reorderPage = useCallback(
199
224
  async (from: number, to: number) => {
200
225
  if (from === to) return;
@@ -351,11 +376,13 @@ export function Slide() {
351
376
  setPlayMode('window');
352
377
  } else if (import.meta.env.DEV && (e.key === 'd' || e.key === 'D')) {
353
378
  setDesignOpen((v) => !v);
379
+ } else if (import.meta.env.DEV && (e.key === 'a' || e.key === 'A')) {
380
+ toggleAgent();
354
381
  }
355
382
  };
356
383
  window.addEventListener('keydown', onKey);
357
384
  return () => window.removeEventListener('keydown', onKey);
358
- }, [index, goTo, playMode, slideId, overviewOpen]);
385
+ }, [index, goTo, playMode, slideId, overviewOpen, toggleAgent]);
359
386
 
360
387
  if (error) {
361
388
  return (
@@ -619,8 +646,11 @@ export function Slide() {
619
646
  <InspectorProvider
620
647
  slideId={slideId}
621
648
  pageIndex={index}
622
- panelHidden={designOpen}
623
- onPanelOpen={() => setDesignOpen(false)}
649
+ panelHidden={designOpen || agentOpen}
650
+ onPanelOpen={() => {
651
+ setDesignOpen(false);
652
+ setDockOpen(false);
653
+ }}
624
654
  >
625
655
  <SelectionReporter />
626
656
  <div className="flex h-dvh flex-col overflow-hidden bg-sidebar text-foreground">
@@ -739,6 +769,12 @@ export function Slide() {
739
769
  />
740
770
  )}
741
771
  <DropdownMenuGroup>
772
+ {narrowToolbar && import.meta.env.DEV && (
773
+ <DropdownMenuItem onClick={toggleAgent}>
774
+ <Sparkles />
775
+ {t.agent.openTitle}
776
+ </DropdownMenuItem>
777
+ )}
742
778
  <DropdownMenuItem onClick={() => setCommandOpen(true)}>
743
779
  <Terminal />
744
780
  {t.commandMenu.trigger}
@@ -753,6 +789,7 @@ export function Slide() {
753
789
  </DropdownMenuContent>
754
790
  </DropdownMenu>
755
791
  )}
792
+ {import.meta.env.DEV && view === 'slides' && !narrowToolbar && <AgentToggleButton />}
756
793
  {view === 'slides' && !narrowToolbar && (
757
794
  <DesignToggleButton active={designOpen} onToggle={() => setDesignOpen((v) => !v)} />
758
795
  )}
@@ -834,6 +871,7 @@ export function Slide() {
834
871
  ref={slideViewportRef}
835
872
  data-inspector-root
836
873
  data-slide-id={slideId}
874
+ data-agent-provider={agentActivity?.provider}
837
875
  className="relative min-h-0 min-w-0 flex-1 bg-background p-2 md:mx-2 md:mb-2 md:rounded-[10px] md:p-10 md:shadow-edge md:ring-1 md:ring-foreground/[0.06]"
838
876
  >
839
877
  <SlideViewportNavigation
@@ -856,6 +894,7 @@ export function Slide() {
856
894
  <InlineEditLayer />
857
895
  <SaveBar />
858
896
  {import.meta.env.DEV && <CommentLayer />}
897
+ {agentActivity && <AgentPresence activity={agentActivity} revision={pages} />}
859
898
  </main>
860
899
  {/* Mobile-only horizontal rail. Sits below the canvas and
861
900
  pads its bottom for the iOS home indicator / Safari URL bar. */}
@@ -875,6 +914,17 @@ export function Slide() {
875
914
  <EditorSidebar
876
915
  designOpen={designOpen}
877
916
  onCloseDesign={() => setDesignOpen(false)}
917
+ agentOpen={agentOpen}
918
+ agent={
919
+ import.meta.env.DEV && (
920
+ <SlideAgentDock
921
+ slideId={slideId}
922
+ slideTitle={title}
923
+ pageNumber={index + 1}
924
+ totalPages={pageCount}
925
+ />
926
+ )
927
+ }
878
928
  />
879
929
  </div>
880
930
  {import.meta.env.DEV && (
@@ -916,6 +966,7 @@ export function Slide() {
916
966
  onCopyLink: copyLink,
917
967
  onOverview: () => setOverviewOpen(true),
918
968
  onToggleDesignPanel: () => setDesignOpen((v) => !v),
969
+ onToggleAgentPanel: toggleAgent,
919
970
  onExportHtml: exportHtml,
920
971
  onExportPdf: exportPdf,
921
972
  onExportPptx: exportPptx,
@@ -935,14 +986,6 @@ const DEFAULT_RAIL_WIDTH = 264;
935
986
  const MIN_RAIL_WIDTH = 200;
936
987
  const MAX_RAIL_WIDTH = 480;
937
988
 
938
- function readStoredRailWidth(): number {
939
- if (typeof window === 'undefined') return DEFAULT_RAIL_WIDTH;
940
- const raw = window.localStorage.getItem(RAIL_WIDTH_STORAGE_KEY);
941
- const parsed = raw == null ? Number.NaN : Number(raw);
942
- if (!Number.isFinite(parsed)) return DEFAULT_RAIL_WIDTH;
943
- return Math.min(MAX_RAIL_WIDTH, Math.max(MIN_RAIL_WIDTH, parsed));
944
- }
945
-
946
989
  function ResizableRail(props: {
947
990
  pages: SlideModule['default'];
948
991
  design?: SlideModule['design'];
@@ -954,103 +997,26 @@ function ResizableRail(props: {
954
997
  onOverview?: () => void;
955
998
  }) {
956
999
  const t = useLocale();
957
- const [width, setWidth] = useState<number>(readStoredRailWidth);
958
- const [resizing, setResizing] = useState(false);
959
- const dragRef = useRef<{ startX: number; startWidth: number } | null>(null);
960
-
961
- useEffect(() => {
962
- if (typeof window === 'undefined') return;
963
- window.localStorage.setItem(RAIL_WIDTH_STORAGE_KEY, String(width));
964
- }, [width]);
965
-
966
- useEffect(() => {
967
- if (!resizing) return;
968
- const prev = {
969
- cursor: document.body.style.cursor,
970
- userSelect: document.body.style.userSelect,
971
- };
972
- document.body.style.cursor = 'col-resize';
973
- document.body.style.userSelect = 'none';
974
- return () => {
975
- document.body.style.cursor = prev.cursor;
976
- document.body.style.userSelect = prev.userSelect;
977
- };
978
- }, [resizing]);
979
-
980
- const onPointerDown = (e: React.PointerEvent<HTMLDivElement>) => {
981
- e.preventDefault();
982
- e.currentTarget.setPointerCapture(e.pointerId);
983
- dragRef.current = { startX: e.clientX, startWidth: width };
984
- setResizing(true);
985
- };
986
-
987
- const onPointerMove = (e: React.PointerEvent<HTMLDivElement>) => {
988
- if (!dragRef.current) return;
989
- const delta = e.clientX - dragRef.current.startX;
990
- const next = Math.min(
991
- MAX_RAIL_WIDTH,
992
- Math.max(MIN_RAIL_WIDTH, dragRef.current.startWidth + delta),
993
- );
994
- setWidth(next);
995
- };
996
-
997
- const onPointerUp = (e: React.PointerEvent<HTMLDivElement>) => {
998
- if (e.currentTarget.hasPointerCapture(e.pointerId)) {
999
- e.currentTarget.releasePointerCapture(e.pointerId);
1000
- }
1001
- dragRef.current = null;
1002
- setResizing(false);
1003
- };
1004
-
1005
- const onKeyDown = (e: React.KeyboardEvent<HTMLDivElement>) => {
1006
- const step = e.shiftKey ? 32 : 8;
1007
- if (e.key === 'ArrowLeft') {
1008
- e.preventDefault();
1009
- e.stopPropagation();
1010
- setWidth((w) => Math.max(MIN_RAIL_WIDTH, w - step));
1011
- } else if (e.key === 'ArrowRight') {
1012
- e.preventDefault();
1013
- e.stopPropagation();
1014
- setWidth((w) => Math.min(MAX_RAIL_WIDTH, w + step));
1015
- } else if (e.key === 'Home') {
1016
- e.preventDefault();
1017
- e.stopPropagation();
1018
- setWidth(DEFAULT_RAIL_WIDTH);
1019
- }
1020
- };
1000
+ const [width, setWidth] = useStoredWidth(
1001
+ RAIL_WIDTH_STORAGE_KEY,
1002
+ DEFAULT_RAIL_WIDTH,
1003
+ MIN_RAIL_WIDTH,
1004
+ MAX_RAIL_WIDTH,
1005
+ );
1021
1006
 
1022
1007
  return (
1023
1008
  <div className="relative hidden shrink-0 md:block" style={{ width }}>
1024
1009
  <ThumbnailRail width={width} {...props} />
1025
- {/* biome-ignore lint/a11y/useSemanticElements: focusable resize handle (splitter pattern), not a static <hr> */}
1026
- <div
1027
- role="separator"
1028
- aria-orientation="vertical"
1029
- aria-label={t.thumbnailRail.resizeRail}
1030
- aria-valuenow={width}
1031
- aria-valuemin={MIN_RAIL_WIDTH}
1032
- aria-valuemax={MAX_RAIL_WIDTH}
1033
- tabIndex={0}
1034
- onPointerDown={onPointerDown}
1035
- onPointerMove={onPointerMove}
1036
- onPointerUp={onPointerUp}
1037
- onPointerCancel={onPointerUp}
1038
- onKeyDown={onKeyDown}
1039
- onDoubleClick={() => setWidth(DEFAULT_RAIL_WIDTH)}
1040
- className={cn(
1041
- 'group/resize absolute inset-y-0 right-0 z-20 w-1.5 translate-x-1/2 cursor-col-resize touch-none outline-none',
1042
- 'focus-visible:bg-brand/20',
1043
- )}
1044
- >
1045
- <span
1046
- aria-hidden
1047
- className={cn(
1048
- 'pointer-events-none absolute inset-y-0 left-1/2 w-px -translate-x-1/2 bg-brand opacity-0 transition-opacity duration-150',
1049
- 'group-hover/resize:opacity-100 group-focus-visible/resize:opacity-100',
1050
- resizing && 'opacity-100',
1051
- )}
1052
- />
1053
- </div>
1010
+ <ResizeHandle
1011
+ width={width}
1012
+ min={MIN_RAIL_WIDTH}
1013
+ max={MAX_RAIL_WIDTH}
1014
+ defaultWidth={DEFAULT_RAIL_WIDTH}
1015
+ edge="right"
1016
+ label={t.thumbnailRail.resizeRail}
1017
+ onResize={setWidth}
1018
+ className="right-0 translate-x-1/2"
1019
+ />
1054
1020
  </div>
1055
1021
  );
1056
1022
  }
@@ -1096,6 +1062,39 @@ function EditorMenuItems({
1096
1062
  );
1097
1063
  }
1098
1064
 
1065
+ function SlideAgentDock({
1066
+ slideId,
1067
+ slideTitle,
1068
+ pageNumber,
1069
+ totalPages,
1070
+ }: {
1071
+ slideId: string;
1072
+ slideTitle: string;
1073
+ pageNumber: number;
1074
+ totalPages: number;
1075
+ }) {
1076
+ const { selected, comments } = useInspector();
1077
+ const context = useMemo<AgentTurnContext>(
1078
+ () => ({
1079
+ slideId,
1080
+ slideTitle,
1081
+ pageNumber,
1082
+ totalPages,
1083
+ pendingComments: comments.length,
1084
+ selection: selected
1085
+ ? {
1086
+ line: selected.line,
1087
+ column: selected.column,
1088
+ tagName: selected.anchor.tagName.toLowerCase(),
1089
+ text: (selected.anchor.textContent ?? '').replace(/\s+/g, ' ').trim().slice(0, 120),
1090
+ }
1091
+ : null,
1092
+ }),
1093
+ [slideId, slideTitle, pageNumber, totalPages, comments.length, selected],
1094
+ );
1095
+ return <AgentPanel context={context} onClose={() => setDockOpen(false)} />;
1096
+ }
1097
+
1099
1098
  function AgentConnectedBadge() {
1100
1099
  const t = useLocale();
1101
1100
  const connected = useAgentSocketConnected();
@@ -383,3 +383,173 @@
383
383
  background-clip: content-box;
384
384
  }
385
385
  }
386
+
387
+ /* Agent presence: ring, write sweep and shimmer shown while an agent edits a deck. */
388
+ @property --agent-angle {
389
+ syntax: "<angle>";
390
+ inherits: false;
391
+ initial-value: 0deg;
392
+ }
393
+
394
+ [data-agent-provider] {
395
+ --agent-a: oklch(0.68 0.14 42);
396
+ --agent-b: oklch(0.82 0.12 75);
397
+ }
398
+
399
+ [data-agent-provider="codex"] {
400
+ --agent-a: oklch(0.64 0.13 170);
401
+ --agent-b: oklch(0.7 0.14 240);
402
+ }
403
+
404
+ @keyframes agent-spin {
405
+ to {
406
+ --agent-angle: 360deg;
407
+ }
408
+ }
409
+
410
+ @keyframes agent-fade-in {
411
+ from {
412
+ opacity: 0;
413
+ }
414
+ }
415
+
416
+ .agent-ring {
417
+ pointer-events: none;
418
+ position: absolute;
419
+ inset: 0;
420
+ border-radius: inherit;
421
+ padding: 1.5px;
422
+ background: conic-gradient(
423
+ from var(--agent-angle),
424
+ transparent 0deg,
425
+ var(--agent-a) 70deg,
426
+ var(--agent-b) 140deg,
427
+ transparent 210deg,
428
+ transparent 360deg
429
+ );
430
+ mask:
431
+ linear-gradient(#000 0 0) content-box exclude,
432
+ linear-gradient(#000 0 0);
433
+ animation:
434
+ agent-spin 3.6s linear infinite,
435
+ agent-fade-in 400ms var(--ease-swift) both;
436
+ }
437
+
438
+ .agent-glow {
439
+ pointer-events: none;
440
+ position: absolute;
441
+ inset: 0;
442
+ border-radius: inherit;
443
+ box-shadow:
444
+ inset 0 0 0 1px color-mix(in oklch, var(--agent-a) 35%, transparent),
445
+ inset 0 0 48px -12px color-mix(in oklch, var(--agent-a) 45%, transparent);
446
+ animation: agent-fade-in 400ms var(--ease-swift) both;
447
+ }
448
+
449
+ @keyframes agent-sweep {
450
+ from {
451
+ background-position: 120% 0;
452
+ opacity: 1;
453
+ }
454
+ to {
455
+ background-position: -20% 0;
456
+ opacity: 0;
457
+ }
458
+ }
459
+
460
+ .agent-sweep {
461
+ pointer-events: none;
462
+ position: absolute;
463
+ inset: 0;
464
+ border-radius: inherit;
465
+ background: linear-gradient(
466
+ 105deg,
467
+ transparent 35%,
468
+ color-mix(in oklch, var(--agent-b) 22%, transparent) 50%,
469
+ transparent 65%
470
+ );
471
+ background-size: 250% 100%;
472
+ animation: agent-sweep 900ms var(--ease-swift) both;
473
+ }
474
+
475
+ @keyframes agent-shimmer {
476
+ from {
477
+ background-position: 100% 0;
478
+ }
479
+ to {
480
+ background-position: 0% 0;
481
+ }
482
+ }
483
+
484
+ .agent-shimmer {
485
+ background: linear-gradient(
486
+ 90deg,
487
+ var(--muted-foreground) 35%,
488
+ var(--foreground) 50%,
489
+ var(--muted-foreground) 65%
490
+ );
491
+ background-size: 300% 100%;
492
+ background-clip: text;
493
+ color: transparent;
494
+ animation: agent-shimmer 1.8s linear infinite;
495
+ }
496
+
497
+ @keyframes agent-progress {
498
+ from {
499
+ transform: translateX(-100%);
500
+ }
501
+ to {
502
+ transform: translateX(250%);
503
+ }
504
+ }
505
+
506
+ .agent-progress {
507
+ pointer-events: none;
508
+ position: absolute;
509
+ inset-inline: 0;
510
+ top: 0;
511
+ height: 1.5px;
512
+ overflow: hidden;
513
+ }
514
+
515
+ .agent-progress::after {
516
+ content: "";
517
+ position: absolute;
518
+ inset-block: 0;
519
+ width: 40%;
520
+ background: linear-gradient(90deg, transparent, var(--agent-a), var(--agent-b), transparent);
521
+ animation: agent-progress 1.6s cubic-bezier(0.4, 0, 0.6, 1) infinite;
522
+ }
523
+
524
+ @media (prefers-reduced-motion: reduce) {
525
+ .agent-ring {
526
+ animation: agent-fade-in 160ms ease-out both;
527
+ --agent-angle: 120deg;
528
+ }
529
+
530
+ .agent-sweep {
531
+ display: none;
532
+ }
533
+
534
+ .agent-shimmer {
535
+ animation: none;
536
+ color: var(--foreground);
537
+ background: none;
538
+ }
539
+
540
+ .agent-progress::after {
541
+ animation: none;
542
+ width: 100%;
543
+ opacity: 0.6;
544
+ }
545
+ }
546
+
547
+ .sidebar-swap {
548
+ animation: agent-fade-in 160ms var(--ease-swift, ease-out);
549
+ }
550
+
551
+ @media (prefers-reduced-motion: reduce) {
552
+ .sidebar-swap {
553
+ animation: none;
554
+ }
555
+ }
@@ -0,0 +1 @@
1
+ export const UNTITLED_SLIDE_ID_RE = /^untitled(-\d+)?$/;
package/src/locale/en.ts CHANGED
@@ -96,6 +96,7 @@ export const en: Locale = {
96
96
  },
97
97
 
98
98
  slide: {
99
+ resizeSidePanel: 'Resize side panel',
99
100
  home: 'Home',
100
101
  backToHome: 'Back to home',
101
102
  agentConnected: 'Agent connected',
@@ -540,4 +541,109 @@ export const en: Locale = {
540
541
  expandPromptAria: 'Expand prompt',
541
542
  collapsePromptAria: 'Collapse prompt',
542
543
  },
544
+
545
+ agent: {
546
+ title: 'Agent',
547
+ beta: 'Beta',
548
+ betaTitle: 'Early preview',
549
+ betaHint: 'Replies, edits and tool calls can occasionally fail or do something unexpected.',
550
+ open: 'Agent',
551
+ openTitle: 'Open the agent panel',
552
+ close: 'Close agent panel',
553
+ newSession: 'New chat',
554
+ sessions: 'History',
555
+ noSessions: 'No conversations yet.',
556
+ deleteSession: 'Delete conversation',
557
+ historyThisDeck: 'This deck',
558
+ historyOtherDecks: 'Other decks',
559
+ historyEmptyHint: 'Chats you start in this panel show up here.',
560
+ sessionRunning: 'Working',
561
+ composerPlaceholder: 'Ask {provider} to create or edit slides…',
562
+ send: 'Send',
563
+ stop: 'Stop',
564
+ working: 'Working',
565
+ contextPage: 'Page {n}',
566
+ contextSelection: 'Selection',
567
+ pendingComments: { one: '{count} comment', other: '{count} comments' },
568
+ applyComments: 'Apply comments',
569
+ applyCommentsWith: 'Apply with {provider}',
570
+ createDeck: 'New deck',
571
+ createDeckWith: 'Create a deck with {provider}',
572
+ quickCreate: 'Draft a new deck',
573
+ quickCreateHint: 'Opens a blank deck; describe it and the agent drafts it page by page.',
574
+ quickApply: 'Apply pending comments',
575
+ quickApplyHint: 'Resolves every @slide-comment marker in this slide.',
576
+ quickTheme: 'Create a theme',
577
+ quickThemeHint: 'Extracts or designs a reusable theme under themes/.',
578
+ quickEdit: 'Edit this page',
579
+ quickEditHint: 'Describe a change to the page you are viewing.',
580
+ emptyTitle: 'What should we build?',
581
+ emptyHint: 'Describe a deck, or pick a starting point.',
582
+ questionSubmit: 'Continue',
583
+ questionOther: 'Other',
584
+ questionOtherPlaceholder: 'Type your own answer',
585
+ questionCancelled: 'Dismissed',
586
+ permissionTitle: 'Needs approval',
587
+ allow: 'Allow',
588
+ deny: 'Deny',
589
+ allowed: 'Allowed',
590
+ denied: 'Denied',
591
+ autoApprove: 'Auto-approve',
592
+ autoApproveHint: 'Run commands without asking. Edits inside the workspace are always allowed.',
593
+ resultDone: 'Done',
594
+ resultStopped: 'Stopped',
595
+ resultSeconds: '{seconds}s',
596
+ errorEyebrow: 'Error',
597
+ statusLoading: 'Checking agents',
598
+ disabled: 'The agent panel is disabled in open-slide.config.ts.',
599
+ connectTitle: 'Connect an agent',
600
+ connectDescription:
601
+ 'Drive your coding agent from here instead of a terminal. Agents installed on this machine connect automatically and reuse their sign-in.',
602
+ connect: 'Connect',
603
+ connecting: 'Connecting',
604
+ connected: 'Connected',
605
+ connectInstallHint: 'Adds {pkg} to this workspace with {pm}.',
606
+ cliFound: 'CLI found · v{version}',
607
+ cliFoundNoVersion: 'CLI found',
608
+ cliMissing: 'CLI not found on PATH',
609
+ cliHint: 'Install the {name} CLI and sign in once; the panel reuses its login.',
610
+ needCliTitle: 'Install Claude Code or Codex first',
611
+ needCliDescription:
612
+ 'The panel drives the coding agent CLI on this machine, and none was found on your PATH. Install one, sign in once in a terminal, then check again.',
613
+ checkAgain: 'Check again',
614
+ retry: 'Retry',
615
+ toastConnected: '{name} connected',
616
+ toastConnectFailed: 'Could not install {pkg}',
617
+ manageConnections: 'Manage agents',
618
+ backToChat: 'Back',
619
+ toastSendFailed: 'Could not send message',
620
+ newDeckTitle: "What's this deck about?",
621
+ newDeckHint:
622
+ 'Describe the topic, audience and tone. {provider} drafts it page by page while you watch.',
623
+ newDeckPlaceholder: 'A 10-minute talk on…',
624
+ toastCreateDeckFailed: 'Could not create a deck',
625
+ steps: { one: '{count} step', other: '{count} steps' },
626
+ questionSkip: 'Skip',
627
+ copyMessage: 'Copy',
628
+ copied: 'Copied',
629
+ editMessage: 'Edit',
630
+ cancelEdit: 'Cancel',
631
+ sendEdit: 'Send',
632
+ editHint:
633
+ 'Sending replaces the conversation after this message. Edits already made to files stay.',
634
+ modelDefault: 'Default',
635
+ reasoning: 'Reasoning',
636
+ effortAuto: 'Auto',
637
+ efforts: {
638
+ minimal: 'Minimal',
639
+ low: 'Low',
640
+ medium: 'Medium',
641
+ high: 'High',
642
+ xhigh: 'Extra high',
643
+ max: 'Max',
644
+ },
645
+ presenceEditing: '{provider} is editing',
646
+ presenceThinking: 'Thinking',
647
+ presenceWaiting: 'Waiting for you',
648
+ },
543
649
  };