@agent-native/core 0.153.10 → 0.154.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/corpus/README.md +1 -1
- package/corpus/templates/analytics/AGENTS.md +20 -30
- package/corpus/templates/analytics/actions/search-dashboard-references.ts +58 -0
- package/corpus/templates/analytics/app/components/layout/Sidebar.tsx +1 -1
- package/corpus/templates/analytics/app/lib/utils.ts +1 -1
- package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/ViewSqlPopover.tsx +1 -1
- package/corpus/templates/analytics/server/lib/agent-chat-plan-mode.ts +2 -0
- package/corpus/templates/analytics/server/lib/dashboards-store.ts +366 -3
- package/corpus/templates/analytics/server/lib/real-data-actions.ts +3 -1
- package/corpus/templates/analytics/server/plugins/agent-chat.ts +6 -2
- package/corpus/templates/calendar/app/components/calendar/EventDialog.tsx +1 -1
- package/corpus/templates/calendar/app/lib/utils.ts +1 -1
- package/corpus/templates/clips/app/components/recorder/recorder-engine.ts +32 -5
- package/corpus/templates/clips/app/components/sharing/share-ui.tsx +15 -15
- package/corpus/templates/clips/app/i18n/en-US.ts +3 -3
- package/corpus/templates/clips/app/lib/utils.ts +5 -6
- package/corpus/templates/clips/chrome-extension/src/offscreen.ts +43 -8
- package/corpus/templates/clips/desktop/src/app.tsx +26 -12
- package/corpus/templates/clips/desktop/src/lib/recorder.ts +23 -9
- package/corpus/templates/clips/desktop/src-tauri/src/native_screen/custom_capture.rs +261 -11
- package/corpus/templates/clips/desktop/src-tauri/src/native_screen.rs +21 -3
- package/corpus/templates/clips/shared/mic-audio-cleanup.ts +350 -0
- package/corpus/templates/content/actions/resolve-content-landing.ts +162 -32
- package/corpus/templates/content/app/i18n-data.ts +10 -10
- package/corpus/templates/content/scripts/check-native-deps.mjs +181 -15
- package/corpus/templates/content/shared/content-landing.ts +5 -0
- package/corpus/templates/design/.agents/skills/design-generation/SKILL.md +79 -7
- package/corpus/templates/design/.agents/skills/design-systems/SKILL.md +14 -2
- package/corpus/templates/design/.agents/skills/visual-edit/SKILL.md +2 -2
- package/corpus/templates/design/.generated/bridge/editor-chrome.generated.ts +35 -11
- package/corpus/templates/design/actions/get-design-system.ts +103 -3
- package/corpus/templates/design/actions/import-design-tokens.ts +37 -8
- package/corpus/templates/design/actions/import-github.ts +32 -138
- package/corpus/templates/design/actions/index-design-system-with-builder.ts +49 -16
- package/corpus/templates/design/actions/present-design-variants.ts +93 -4
- package/corpus/templates/design/actions/run-design-audit.ts +229 -0
- package/corpus/templates/design/actions/sync-design-system-with-builder.ts +64 -0
- package/corpus/templates/design/app/components/design/code-workbench/commands.ts +2 -2
- package/corpus/templates/design/app/components/design/inspector/DesignColorPicker.tsx +17 -4
- package/corpus/templates/design/app/components/design/inspector/GradientEditor.tsx +5 -1
- package/corpus/templates/design/app/components/design/inspector/ImageFillControls.tsx +5 -1
- package/corpus/templates/design/app/hooks/use-question-flow.ts +81 -5
- package/corpus/templates/design/app/i18n/en-US.ts +1 -1
- package/corpus/templates/design/app/i18n-data.ts +32 -1
- package/corpus/templates/design/app/i18n-keyboard-shortcuts.ts +10 -10
- package/corpus/templates/design/app/pages/DesignSystemSetup.tsx +83 -11
- package/corpus/templates/design/app/pages/DesignSystems.tsx +14 -0
- package/corpus/templates/design/app/pages/design-editor/generation-prompt-directives.ts +22 -0
- package/corpus/templates/design/changelog/2026-08-13-a-screen-that-would-render-behind-an-invisible-alpine-overla.md +6 -0
- package/corpus/templates/design/changelog/2026-08-13-attach-a-ui-screenshot-and-the-design-is-reproduced-not-rein.md +6 -0
- package/corpus/templates/design/changelog/2026-08-13-linking-a-design-system-means-you-never-get-generic-directio.md +6 -0
- package/corpus/templates/design/changelog/2026-08-13-the-design-audit-now-reports-when-a-screen-ignores-its-linke.md +6 -0
- package/corpus/templates/design/changelog/2026-08-13-the-design-system-preview-now-shows-how-many-named-tokens-we.md +6 -0
- package/corpus/templates/design/changelog/2026-08-13-your-design-system-prompt-and-reference-screenshots-now-reac.md +6 -0
- package/corpus/templates/design/server/lib/builder-design-system-proxy.ts +18 -2
- package/corpus/templates/design/shared/design-review.ts +1 -0
- package/corpus/templates/design/shared/html-integrity.ts +171 -0
- package/corpus/templates/mail/app/lib/utils.ts +1 -1
- package/corpus/templates/slides/.agents/skills/design-systems/SKILL.md +10 -6
- package/corpus/templates/slides/actions/index-design-system-with-builder.ts +41 -3
- package/corpus/templates/slides/actions/patch-deck.ts +12 -3
- package/corpus/templates/slides/actions/sync-design-system-with-builder.ts +64 -0
- package/corpus/templates/slides/app/components/deck/DeckCard.tsx +1 -1
- package/corpus/templates/slides/app/components/design-system/DesignSystemSetup.tsx +193 -20
- package/corpus/templates/slides/app/components/editor/EditorActionCluster.tsx +73 -1
- package/corpus/templates/slides/app/components/editor/EditorSidebar.tsx +64 -34
- package/corpus/templates/slides/app/components/editor/EditorToolbar.tsx +34 -41
- package/corpus/templates/slides/app/components/editor/ExportMenu.tsx +119 -91
- package/corpus/templates/slides/app/components/editor/ImageOverlay.tsx +16 -0
- package/corpus/templates/slides/app/components/editor/NewDeckReferenceStep.tsx +15 -0
- package/corpus/templates/slides/app/components/editor/SlideEditor.tsx +158 -31
- package/corpus/templates/slides/app/components/editor/SlideOverflowWarning.tsx +30 -14
- package/corpus/templates/slides/app/components/editor/SpeakerNotesPanel.tsx +2 -2
- package/corpus/templates/slides/app/components/editor/canvas-interactions/slides-canvas-adapter.ts +4 -1
- package/corpus/templates/slides/app/components/editor/rich-text-selection.ts +14 -0
- package/corpus/templates/slides/app/components/editor/slide-text-targets.ts +24 -0
- package/corpus/templates/slides/app/context/DeckContext.tsx +19 -31
- package/corpus/templates/slides/app/global.css +24 -0
- package/corpus/templates/slides/app/hooks/use-navigation-state.ts +20 -17
- package/corpus/templates/slides/app/i18n/en-US.ts +19 -10
- package/corpus/templates/slides/app/lib/deck-filter.ts +47 -0
- package/corpus/templates/slides/app/lib/export-google-slides-client.ts +15 -4
- package/corpus/templates/slides/app/lib/export-pptx-client.ts +637 -1
- package/corpus/templates/slides/app/lib/image-download.ts +84 -0
- package/corpus/templates/slides/app/lib/utils.ts +1 -1
- package/corpus/templates/slides/app/pages/DeckEditor.tsx +9 -0
- package/corpus/templates/slides/app/pages/Index.tsx +25 -9
- package/corpus/templates/slides/changelog/2026-08-13-clicking-a-slide-thumbnail-now-focuses-it-so-the-slide-shortc.md +6 -0
- package/corpus/templates/slides/changelog/2026-08-13-duplicating-or-restoring-a-slide-now-keeps-its-transition-ani.md +6 -0
- package/corpus/templates/slides/changelog/2026-08-13-opening-presentation-mode-from-the-agent-now-starts-on-the-re.md +6 -0
- package/corpus/templates/slides/changelog/2026-08-13-slide-transitions-can-be-set-from-a-visible-control-in-the-sl.md +6 -0
- package/corpus/templates/slides/netlify.toml +1 -0
- package/corpus/templates/slides/server/lib/builder-design-system-proxy.ts +14 -2
- package/dist/a2a/audience.d.ts +2 -0
- package/dist/a2a/audience.js +23 -0
- package/dist/a2a/client.d.ts +4 -0
- package/dist/a2a/client.js +32 -14
- package/dist/a2a/server.d.ts +9 -4
- package/dist/a2a/server.js +52 -25
- package/dist/a2a/types.d.ts +4 -4
- package/dist/agent/production-agent.js +1 -1
- package/dist/brand-kit/types.d.ts +10 -2
- package/dist/cli/code.js +1 -1
- package/dist/cli/index.js +30 -1
- package/dist/cli/native-dependencies.d.ts +24 -0
- package/dist/cli/native-dependencies.js +264 -0
- package/dist/cli/plan-local.js +1 -1
- package/dist/cli/skills-content/design-visual-edit-skill.d.ts +1 -1
- package/dist/cli/skills-content/design-visual-edit-skill.js +2 -2
- package/dist/cli/visualize-repo.js +1 -1
- package/dist/client/FeedbackButton.js +12 -12
- package/dist/client/agent-chat-adapter.js +3 -4
- package/dist/client/assistant-ui-recovery.d.ts +1 -1
- package/dist/client/assistant-ui-recovery.js +4 -0
- package/dist/client/chat/message-components.d.ts +11 -3
- package/dist/client/chat/message-components.js +31 -5
- package/dist/client/chat/tool-call-display.d.ts +2 -1
- package/dist/client/chat/tool-call-display.js +33 -15
- package/dist/client/db-admin/SqlEditor.js +1 -1
- package/dist/client/dev-overlay/DevOverlay.js +2 -2
- package/dist/client/extensions/portable-extension.js +4 -0
- package/dist/client/guided-questions.d.ts +8 -0
- package/dist/client/guided-questions.js +12 -3
- package/dist/client/sharing/ShareButton.js +16 -24
- package/dist/client/sse-event-processor.js +35 -15
- package/dist/client/tool-display.d.ts +9 -0
- package/dist/client/tool-display.js +39 -0
- package/dist/client/vite-dev-recovery-script.js +1 -1
- package/dist/collab/awareness.d.ts +2 -2
- package/dist/collab/routes.d.ts +1 -1
- package/dist/collab/struct-routes.d.ts +1 -1
- package/dist/extensions/html-shell.js +5 -0
- package/dist/notifications/routes.d.ts +1 -1
- package/dist/observability/routes.d.ts +3 -3
- package/dist/provider-api/actions/custom-provider-registration.d.ts +2 -2
- package/dist/server/agent-capabilities.js +0 -0
- package/dist/server/agent-chat/action-filters-a2a.d.ts +14 -13
- package/dist/server/agent-chat/action-filters-a2a.js +59 -23
- package/dist/server/agent-chat/prompt-resources.js +17 -16
- package/dist/server/agent-chat-plugin.d.ts +9 -0
- package/dist/server/agent-chat-plugin.js +29 -7
- package/dist/server/agent-engine-api-key-route.d.ts +1 -1
- package/dist/server/builder-design-systems.d.ts +33 -1
- package/dist/server/builder-design-systems.js +293 -11
- package/dist/server/design-token-utils.d.ts +11 -0
- package/dist/server/design-token-utils.js +68 -16
- package/dist/server/index.d.ts +1 -1
- package/dist/server/index.js +1 -1
- package/package.json +2 -2
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
type CollabUser,
|
|
7
7
|
} from "@agent-native/core/client/collab";
|
|
8
8
|
import {
|
|
9
|
+
readClientAppState,
|
|
9
10
|
setClientAppState,
|
|
10
11
|
usePinchZoom,
|
|
11
12
|
useAvatarUrl,
|
|
@@ -17,15 +18,7 @@ import {
|
|
|
17
18
|
} from "@agent-native/toolkit/collab-ui";
|
|
18
19
|
import { appStateKeyForBrowserTab } from "@shared/app-state-tabs";
|
|
19
20
|
import { hashSlideContent } from "@shared/slide-fit";
|
|
20
|
-
import {
|
|
21
|
-
IconBrush,
|
|
22
|
-
IconClipboard,
|
|
23
|
-
IconCopy,
|
|
24
|
-
IconStackBack,
|
|
25
|
-
IconStackFront,
|
|
26
|
-
IconTrash,
|
|
27
|
-
IconX,
|
|
28
|
-
} from "@tabler/icons-react";
|
|
21
|
+
import { IconX } from "@tabler/icons-react";
|
|
29
22
|
import {
|
|
30
23
|
useState,
|
|
31
24
|
useCallback,
|
|
@@ -73,6 +66,7 @@ import {
|
|
|
73
66
|
MAX_CANVAS_ZOOM,
|
|
74
67
|
MIN_CANVAS_ZOOM,
|
|
75
68
|
} from "@/lib/canvas-zoom";
|
|
69
|
+
import { downloadImage } from "@/lib/image-download";
|
|
76
70
|
import { extractMermaidBlocks } from "@/lib/mermaid-blocks";
|
|
77
71
|
import { publishSlidesSelection } from "@/lib/slide-agent-context";
|
|
78
72
|
import {
|
|
@@ -113,6 +107,7 @@ import {
|
|
|
113
107
|
getInlineTextStyleSnapshot,
|
|
114
108
|
getInlineTextStyleSnapshotForRange,
|
|
115
109
|
restoreEditableTextRange,
|
|
110
|
+
selectAllEditableText,
|
|
116
111
|
snapshotEditableTextRange,
|
|
117
112
|
type InlineTextStylePatch,
|
|
118
113
|
type InlineTextStyleSnapshot,
|
|
@@ -167,6 +162,7 @@ import { type SlideStylePatch, type SlideStyleSnapshot } from "./slide-style";
|
|
|
167
162
|
import {
|
|
168
163
|
findSmartBlock,
|
|
169
164
|
isInlineTextElement,
|
|
165
|
+
isSlideTextEditingTarget,
|
|
170
166
|
isTextLeaf,
|
|
171
167
|
shouldStampBuilderId,
|
|
172
168
|
} from "./slide-text-targets";
|
|
@@ -1064,6 +1060,13 @@ function syncOverflowToAppState(
|
|
|
1064
1060
|
}
|
|
1065
1061
|
}
|
|
1066
1062
|
|
|
1063
|
+
function layoutWarningDismissalStateKey(
|
|
1064
|
+
deckId: string | undefined,
|
|
1065
|
+
slideId: string,
|
|
1066
|
+
): string {
|
|
1067
|
+
return `slides-layout-warning-dismissed:${deckId ?? "local"}:${slideId}`;
|
|
1068
|
+
}
|
|
1069
|
+
|
|
1067
1070
|
export default function SlideEditor({
|
|
1068
1071
|
slide,
|
|
1069
1072
|
onUpdateSlide,
|
|
@@ -1222,12 +1225,54 @@ export default function SlideEditor({
|
|
|
1222
1225
|
const repairRequestTimerRef = useRef<ReturnType<typeof setTimeout> | null>(
|
|
1223
1226
|
null,
|
|
1224
1227
|
);
|
|
1225
|
-
const
|
|
1228
|
+
const overflowWarningStateKey = layoutWarningDismissalStateKey(
|
|
1229
|
+
deckId,
|
|
1230
|
+
slide.id,
|
|
1231
|
+
);
|
|
1232
|
+
const overflowContentHash = hashSlideContent(slide.content);
|
|
1233
|
+
const [dismissedOverflowWarningHash, setDismissedOverflowWarningHash] =
|
|
1226
1234
|
useState<string | null>(null);
|
|
1227
|
-
const
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1235
|
+
const [overflowWarningDismissalStatus, setOverflowWarningDismissalStatus] =
|
|
1236
|
+
useState<"loading" | "loaded" | "error">("loading");
|
|
1237
|
+
useEffect(() => {
|
|
1238
|
+
let cancelled = false;
|
|
1239
|
+
setDismissedOverflowWarningHash(null);
|
|
1240
|
+
setOverflowWarningDismissalStatus("loading");
|
|
1241
|
+
void readClientAppState<unknown>(overflowWarningStateKey)
|
|
1242
|
+
.then((value) => {
|
|
1243
|
+
if (cancelled) return;
|
|
1244
|
+
setDismissedOverflowWarningHash(
|
|
1245
|
+
typeof value === "string" ? value : null,
|
|
1246
|
+
);
|
|
1247
|
+
setOverflowWarningDismissalStatus("loaded");
|
|
1248
|
+
})
|
|
1249
|
+
.catch((error: unknown) => {
|
|
1250
|
+
if (cancelled) return;
|
|
1251
|
+
console.error("Could not read slide overflow warning dismissal", error);
|
|
1252
|
+
setDismissedOverflowWarningHash(null);
|
|
1253
|
+
setOverflowWarningDismissalStatus("error");
|
|
1254
|
+
});
|
|
1255
|
+
return () => {
|
|
1256
|
+
cancelled = true;
|
|
1257
|
+
};
|
|
1258
|
+
}, [overflowWarningStateKey]);
|
|
1259
|
+
const warningVisible =
|
|
1260
|
+
overflowWarningDismissalStatus === "error" ||
|
|
1261
|
+
(overflowWarningDismissalStatus === "loaded" &&
|
|
1262
|
+
dismissedOverflowWarningHash !== overflowContentHash);
|
|
1263
|
+
const dismissWarning = useCallback(() => {
|
|
1264
|
+
setDismissedOverflowWarningHash(overflowContentHash);
|
|
1265
|
+
setOverflowWarningDismissalStatus("loaded");
|
|
1266
|
+
void setClientAppState(overflowWarningStateKey, overflowContentHash, {
|
|
1267
|
+
keepalive: true,
|
|
1268
|
+
requestSource: TAB_ID,
|
|
1269
|
+
}).catch((error: unknown) => {
|
|
1270
|
+
console.error(
|
|
1271
|
+
"Could not persist slide overflow warning dismissal",
|
|
1272
|
+
error,
|
|
1273
|
+
);
|
|
1274
|
+
});
|
|
1275
|
+
}, [overflowContentHash, overflowWarningStateKey]);
|
|
1231
1276
|
const dims = getAspectRatioDims(aspectRatio);
|
|
1232
1277
|
const [fitCanvasZoom, setFitCanvasZoom] = useState(100);
|
|
1233
1278
|
const userSetCanvasZoomRef = useRef(false);
|
|
@@ -1930,6 +1975,17 @@ export default function SlideEditor({
|
|
|
1930
1975
|
return () => editingEl.removeEventListener("input", handleInput);
|
|
1931
1976
|
}, [captureInlineEditDraft, editingEl, slide.id]);
|
|
1932
1977
|
|
|
1978
|
+
// Keep canvas gesture handlers from stealing the browser's native text
|
|
1979
|
+
// selection stream once an inline edit has started.
|
|
1980
|
+
useEffect(() => {
|
|
1981
|
+
if (!editingEl) return;
|
|
1982
|
+
const onPointerDown = (event: PointerEvent) => {
|
|
1983
|
+
if (event.button === 0) event.stopPropagation();
|
|
1984
|
+
};
|
|
1985
|
+
editingEl.addEventListener("pointerdown", onPointerDown);
|
|
1986
|
+
return () => editingEl.removeEventListener("pointerdown", onPointerDown);
|
|
1987
|
+
}, [editingEl]);
|
|
1988
|
+
|
|
1933
1989
|
useEffect(() => {
|
|
1934
1990
|
if (!editingEl) return;
|
|
1935
1991
|
|
|
@@ -1981,6 +2037,17 @@ export default function SlideEditor({
|
|
|
1981
2037
|
(isTextLeaf(editingEl) && RICH_BLOCK_TAGS.has(editingEl.tagName)) ||
|
|
1982
2038
|
isBulletList(editingEl);
|
|
1983
2039
|
const onKey = (e: KeyboardEvent) => {
|
|
2040
|
+
const isSelectAll =
|
|
2041
|
+
(e.metaKey || e.ctrlKey) && !e.altKey && e.key.toLowerCase() === "a";
|
|
2042
|
+
if (isSelectAll) {
|
|
2043
|
+
const editing = editingElRef.current;
|
|
2044
|
+
if (!editing || !editing.isConnected) return;
|
|
2045
|
+
e.preventDefault();
|
|
2046
|
+
e.stopPropagation();
|
|
2047
|
+
selectAllEditableText(editing);
|
|
2048
|
+
return;
|
|
2049
|
+
}
|
|
2050
|
+
|
|
1984
2051
|
// Ignore keys from outside the slide (e.g. the style dock's font-size
|
|
1985
2052
|
// input). Guard against the LIVE slide content, not `editingEl`, which a
|
|
1986
2053
|
// re-render may have detached — a stale ref would wrongly bail here and
|
|
@@ -2036,6 +2103,7 @@ export default function SlideEditor({
|
|
|
2036
2103
|
exitInlineEdit,
|
|
2037
2104
|
editingEl,
|
|
2038
2105
|
captureInlineEditDraft,
|
|
2106
|
+
selectAllEditableText,
|
|
2039
2107
|
slide.id,
|
|
2040
2108
|
getSlideContent,
|
|
2041
2109
|
]);
|
|
@@ -4123,10 +4191,12 @@ export default function SlideEditor({
|
|
|
4123
4191
|
|
|
4124
4192
|
useEffect(() => {
|
|
4125
4193
|
if (readOnly || editingEl) return;
|
|
4126
|
-
if (multiSelection.size === 0 && !selectedElementSelector)
|
|
4194
|
+
if (multiSelection.size === 0 && !selectedElementSelector && !selectedImg)
|
|
4195
|
+
return;
|
|
4127
4196
|
const onKey = (e: KeyboardEvent) => {
|
|
4128
4197
|
if (!e.key.startsWith("Arrow")) return;
|
|
4129
4198
|
const active = document.activeElement;
|
|
4199
|
+
if (isSlideTextEditingTarget(e.target, active, editingEl)) return;
|
|
4130
4200
|
if (
|
|
4131
4201
|
active?.tagName === "INPUT" ||
|
|
4132
4202
|
active?.tagName === "TEXTAREA" ||
|
|
@@ -4139,7 +4209,7 @@ export default function SlideEditor({
|
|
|
4139
4209
|
if (!nudge) return;
|
|
4140
4210
|
const { x: dx, y: dy } = nudge.delta;
|
|
4141
4211
|
|
|
4142
|
-
if (multiSelection.size > 0 && !e.
|
|
4212
|
+
if (multiSelection.size > 0 && !e.metaKey && !e.ctrlKey) {
|
|
4143
4213
|
const slideContent = getSlideContent();
|
|
4144
4214
|
if (!slideContent) return;
|
|
4145
4215
|
const elements = Array.from(multiSelection)
|
|
@@ -4188,17 +4258,51 @@ export default function SlideEditor({
|
|
|
4188
4258
|
return;
|
|
4189
4259
|
}
|
|
4190
4260
|
|
|
4191
|
-
const
|
|
4192
|
-
|
|
4261
|
+
const slideContent = getSlideContent();
|
|
4262
|
+
const element =
|
|
4263
|
+
resolveSelectedElement() ??
|
|
4264
|
+
(selectedImg && slideContent
|
|
4265
|
+
? (findPersistedImageObject(selectedImg, slideContent) ?? selectedImg)
|
|
4266
|
+
: null);
|
|
4267
|
+
if (!element) return;
|
|
4268
|
+
|
|
4269
|
+
// Arrow nudging is also a first-class way to move flow-layout text and
|
|
4270
|
+
// images. Promote those elements using the same reversible freeform
|
|
4271
|
+
// boundary as a real drag, then persist the exact resulting HTML.
|
|
4272
|
+
const frozen = isPersistedFreeformObject(element)
|
|
4273
|
+
? { element }
|
|
4274
|
+
: freezeElementForFreeformSelection(element);
|
|
4275
|
+
if (!frozen || !isPersistedFreeformObject(frozen.element)) {
|
|
4276
|
+
frozen?.restoreMarkdownTree?.();
|
|
4277
|
+
return;
|
|
4278
|
+
}
|
|
4193
4279
|
e.preventDefault();
|
|
4194
|
-
const geometry = getObjectGeometry(element);
|
|
4280
|
+
const geometry = getObjectGeometry(frozen.element);
|
|
4195
4281
|
geometry.x += dx;
|
|
4196
4282
|
geometry.y += dy;
|
|
4197
|
-
applyObjectGeometry(element, geometry);
|
|
4283
|
+
applyObjectGeometry(frozen.element, geometry);
|
|
4198
4284
|
const html = readCurrentSlideContentHtml();
|
|
4285
|
+
|
|
4286
|
+
if (frozen.restoreMarkdownTree) {
|
|
4287
|
+
const objectId = frozen.element.getAttribute("data-slide-object-id");
|
|
4288
|
+
if (objectId) {
|
|
4289
|
+
const owner =
|
|
4290
|
+
frozen.element.parentElement ?? frozen.element.ownerDocument;
|
|
4291
|
+
owner
|
|
4292
|
+
.querySelectorAll<HTMLElement>("[data-slide-layout-spacer-for]")
|
|
4293
|
+
.forEach((spacer) => {
|
|
4294
|
+
if (
|
|
4295
|
+
spacer.getAttribute("data-slide-layout-spacer-for") === objectId
|
|
4296
|
+
) {
|
|
4297
|
+
spacer.remove();
|
|
4298
|
+
}
|
|
4299
|
+
});
|
|
4300
|
+
}
|
|
4301
|
+
frozen.restoreMarkdownTree();
|
|
4302
|
+
}
|
|
4199
4303
|
if (html !== null) onUpdateSlideRef.current({ content: html });
|
|
4200
|
-
const selector = getBuilderSelector(element);
|
|
4201
|
-
if (selector) selectElementForStyling(element, selector);
|
|
4304
|
+
const selector = getBuilderSelector(frozen.element);
|
|
4305
|
+
if (selector) selectElementForStyling(frozen.element, selector);
|
|
4202
4306
|
};
|
|
4203
4307
|
window.addEventListener("keydown", onKey);
|
|
4204
4308
|
return () => window.removeEventListener("keydown", onKey);
|
|
@@ -4206,6 +4310,8 @@ export default function SlideEditor({
|
|
|
4206
4310
|
applyObjectGeometry,
|
|
4207
4311
|
commitMultiObjectChange,
|
|
4208
4312
|
editingEl,
|
|
4313
|
+
findPersistedImageObject,
|
|
4314
|
+
freezeElementForFreeformSelection,
|
|
4209
4315
|
getObjectGeometry,
|
|
4210
4316
|
getSlideContent,
|
|
4211
4317
|
multiSelection,
|
|
@@ -4214,6 +4320,7 @@ export default function SlideEditor({
|
|
|
4214
4320
|
refreshMultiSelectionRects,
|
|
4215
4321
|
resolveSelectedElement,
|
|
4216
4322
|
selectElementForStyling,
|
|
4323
|
+
selectedImg,
|
|
4217
4324
|
selectedElementSelector,
|
|
4218
4325
|
]);
|
|
4219
4326
|
|
|
@@ -4268,7 +4375,11 @@ export default function SlideEditor({
|
|
|
4268
4375
|
onExitTextBoxMode?.();
|
|
4269
4376
|
return;
|
|
4270
4377
|
}
|
|
4271
|
-
if (
|
|
4378
|
+
if (
|
|
4379
|
+
isSlideTextEditingTarget(e.target, document.activeElement, editingEl)
|
|
4380
|
+
) {
|
|
4381
|
+
return;
|
|
4382
|
+
}
|
|
4272
4383
|
|
|
4273
4384
|
// Pointer-down on a member of the current multi-selection drags the
|
|
4274
4385
|
// whole group instead of the single-object flow below.
|
|
@@ -5185,6 +5296,28 @@ export default function SlideEditor({
|
|
|
5185
5296
|
horizontalOverflow={
|
|
5186
5297
|
overflowInfo.horizontalOverflow
|
|
5187
5298
|
}
|
|
5299
|
+
warningLabel={t(
|
|
5300
|
+
"deckEditor.layoutOverflowWarning",
|
|
5301
|
+
)}
|
|
5302
|
+
overflowDetails={[
|
|
5303
|
+
overflowInfo.verticalOverflow > 0
|
|
5304
|
+
? t("deckEditor.layoutOverflowVertical", {
|
|
5305
|
+
pixels: overflowInfo.verticalOverflow,
|
|
5306
|
+
})
|
|
5307
|
+
: null,
|
|
5308
|
+
overflowInfo.horizontalOverflow > 0
|
|
5309
|
+
? t("deckEditor.layoutOverflowHorizontal", {
|
|
5310
|
+
pixels: overflowInfo.horizontalOverflow,
|
|
5311
|
+
})
|
|
5312
|
+
: null,
|
|
5313
|
+
]
|
|
5314
|
+
.filter((detail): detail is string =>
|
|
5315
|
+
Boolean(detail),
|
|
5316
|
+
)
|
|
5317
|
+
.join(" · ")}
|
|
5318
|
+
overflowDetailsLabel={t(
|
|
5319
|
+
"deckEditor.layoutOverflowDetails",
|
|
5320
|
+
)}
|
|
5188
5321
|
isAskingAgentToFix={isAskingAgentToFix}
|
|
5189
5322
|
dismissLabel={t(
|
|
5190
5323
|
"deckEditor.dismissLayoutWarning",
|
|
@@ -5200,7 +5333,6 @@ export default function SlideEditor({
|
|
|
5200
5333
|
disabled={!hasObjectSelection}
|
|
5201
5334
|
onSelect={copySelectedObjects}
|
|
5202
5335
|
>
|
|
5203
|
-
<IconCopy className="me-2 h-4 w-4" />
|
|
5204
5336
|
{t("styleInspector.copy")}
|
|
5205
5337
|
<ContextMenuShortcut>
|
|
5206
5338
|
{shortcutLabel("cmd+c")}
|
|
@@ -5210,7 +5342,6 @@ export default function SlideEditor({
|
|
|
5210
5342
|
disabled={!hasObjectSelection}
|
|
5211
5343
|
onSelect={pasteSelectedObjects}
|
|
5212
5344
|
>
|
|
5213
|
-
<IconClipboard className="me-2 h-4 w-4" />
|
|
5214
5345
|
{t("styleInspector.paste")}
|
|
5215
5346
|
<ContextMenuShortcut>
|
|
5216
5347
|
{shortcutLabel("cmd+v")}
|
|
@@ -5220,7 +5351,6 @@ export default function SlideEditor({
|
|
|
5220
5351
|
disabled={!hasObjectSelection}
|
|
5221
5352
|
onSelect={duplicateSelectedObjects}
|
|
5222
5353
|
>
|
|
5223
|
-
<IconCopy className="me-2 h-4 w-4" />
|
|
5224
5354
|
{t("editorSidebar.duplicate")}
|
|
5225
5355
|
<ContextMenuShortcut>
|
|
5226
5356
|
{shortcutLabel("cmd+d")}
|
|
@@ -5230,7 +5360,6 @@ export default function SlideEditor({
|
|
|
5230
5360
|
disabled={!hasObjectSelection}
|
|
5231
5361
|
onSelect={deleteSelectedElements}
|
|
5232
5362
|
>
|
|
5233
|
-
<IconTrash className="me-2 h-4 w-4" />
|
|
5234
5363
|
{t("editorSidebar.delete")}
|
|
5235
5364
|
<ContextMenuShortcut>⌫</ContextMenuShortcut>
|
|
5236
5365
|
</ContextMenuItem>
|
|
@@ -5239,7 +5368,6 @@ export default function SlideEditor({
|
|
|
5239
5368
|
disabled={getStyleTargets().length === 0}
|
|
5240
5369
|
onSelect={copySelectedElementStyle}
|
|
5241
5370
|
>
|
|
5242
|
-
<IconBrush className="me-2 h-4 w-4" />
|
|
5243
5371
|
{t("styleInspector.copyStyle")}
|
|
5244
5372
|
<ContextMenuShortcut>
|
|
5245
5373
|
{shortcutLabel("cmd+alt+c")}
|
|
@@ -5252,7 +5380,6 @@ export default function SlideEditor({
|
|
|
5252
5380
|
}
|
|
5253
5381
|
onSelect={pasteCopiedElementStyle}
|
|
5254
5382
|
>
|
|
5255
|
-
<IconBrush className="me-2 h-4 w-4" />
|
|
5256
5383
|
{t("styleInspector.pasteStyle")}
|
|
5257
5384
|
<ContextMenuShortcut>
|
|
5258
5385
|
{shortcutLabel("cmd+alt+v")}
|
|
@@ -5263,14 +5390,12 @@ export default function SlideEditor({
|
|
|
5263
5390
|
disabled={!selectedElementSelector}
|
|
5264
5391
|
onSelect={() => handleArrangeSelected("front")}
|
|
5265
5392
|
>
|
|
5266
|
-
<IconStackFront className="me-2 h-4 w-4" />
|
|
5267
5393
|
{t("styleInspector.bringToFront")}
|
|
5268
5394
|
</ContextMenuItem>
|
|
5269
5395
|
<ContextMenuItem
|
|
5270
5396
|
disabled={!selectedElementSelector}
|
|
5271
5397
|
onSelect={() => handleArrangeSelected("back")}
|
|
5272
5398
|
>
|
|
5273
|
-
<IconStackBack className="me-2 h-4 w-4" />
|
|
5274
5399
|
{t("styleInspector.sendToBack")}
|
|
5275
5400
|
</ContextMenuItem>
|
|
5276
5401
|
</ContextMenuContent>
|
|
@@ -5357,10 +5482,12 @@ export default function SlideEditor({
|
|
|
5357
5482
|
{imageOverlay && (
|
|
5358
5483
|
<ImageOverlay
|
|
5359
5484
|
anchorRect={imageOverlay.rect}
|
|
5485
|
+
src={imageOverlay.src}
|
|
5360
5486
|
objectFit={imageOverlay.objectFit}
|
|
5361
5487
|
onGenerate={onGenerateImage}
|
|
5362
5488
|
onLibrary={() => onOpenAssetLibrary(imageOverlay.src)}
|
|
5363
5489
|
onUpload={() => onUploadImage(imageOverlay.src)}
|
|
5490
|
+
onDownload={() => void downloadImage(imageOverlay.src)}
|
|
5364
5491
|
onToggleObjectFit={() => {
|
|
5365
5492
|
const newFit =
|
|
5366
5493
|
imageOverlay.objectFit === "cover" ? "contain" : "cover";
|
|
@@ -1,10 +1,19 @@
|
|
|
1
|
-
import { IconAlertTriangle, IconX } from "@tabler/icons-react";
|
|
1
|
+
import { IconAlertTriangle, IconInfoCircle, IconX } from "@tabler/icons-react";
|
|
2
2
|
|
|
3
3
|
import { Button } from "@/components/ui/button";
|
|
4
|
+
import {
|
|
5
|
+
Tooltip,
|
|
6
|
+
TooltipContent,
|
|
7
|
+
TooltipProvider,
|
|
8
|
+
TooltipTrigger,
|
|
9
|
+
} from "@/components/ui/tooltip";
|
|
4
10
|
|
|
5
11
|
interface SlideOverflowWarningProps {
|
|
6
12
|
verticalOverflow: number;
|
|
7
13
|
horizontalOverflow?: number;
|
|
14
|
+
warningLabel: string;
|
|
15
|
+
overflowDetails: string;
|
|
16
|
+
overflowDetailsLabel: string;
|
|
8
17
|
isAskingAgentToFix: boolean;
|
|
9
18
|
dismissLabel: string;
|
|
10
19
|
onFix: () => void;
|
|
@@ -12,21 +21,14 @@ interface SlideOverflowWarningProps {
|
|
|
12
21
|
}
|
|
13
22
|
|
|
14
23
|
export function SlideOverflowWarning({
|
|
15
|
-
|
|
16
|
-
|
|
24
|
+
warningLabel,
|
|
25
|
+
overflowDetails,
|
|
26
|
+
overflowDetailsLabel,
|
|
17
27
|
isAskingAgentToFix,
|
|
18
28
|
dismissLabel,
|
|
19
29
|
onFix,
|
|
20
30
|
onDismiss,
|
|
21
31
|
}: SlideOverflowWarningProps) {
|
|
22
|
-
const overflowLabel = [
|
|
23
|
-
verticalOverflow > 0 ? `vertical ${verticalOverflow}px` : "",
|
|
24
|
-
horizontalOverflow > 0 ? `horizontal ${horizontalOverflow}px` : "",
|
|
25
|
-
]
|
|
26
|
-
.filter(Boolean)
|
|
27
|
-
.join(", ");
|
|
28
|
-
const visibleOverflowLabel =
|
|
29
|
-
horizontalOverflow > 0 ? overflowLabel : `${verticalOverflow}px`;
|
|
30
32
|
return (
|
|
31
33
|
<div
|
|
32
34
|
role="status"
|
|
@@ -42,9 +44,23 @@ export function SlideOverflowWarning({
|
|
|
42
44
|
className="h-3.5 w-3.5 flex-shrink-0 text-foreground"
|
|
43
45
|
stroke={2}
|
|
44
46
|
/>
|
|
45
|
-
<span className="leading-tight">
|
|
46
|
-
|
|
47
|
-
|
|
47
|
+
<span className="leading-tight">{warningLabel}</span>
|
|
48
|
+
<TooltipProvider delayDuration={200}>
|
|
49
|
+
<Tooltip>
|
|
50
|
+
<TooltipTrigger asChild>
|
|
51
|
+
<Button
|
|
52
|
+
type="button"
|
|
53
|
+
size="icon"
|
|
54
|
+
variant="ghost"
|
|
55
|
+
className="size-6 cursor-help text-muted-foreground hover:bg-transparent hover:text-foreground"
|
|
56
|
+
aria-label={overflowDetailsLabel}
|
|
57
|
+
>
|
|
58
|
+
<IconInfoCircle className="size-3.5" />
|
|
59
|
+
</Button>
|
|
60
|
+
</TooltipTrigger>
|
|
61
|
+
<TooltipContent side="top">{overflowDetails}</TooltipContent>
|
|
62
|
+
</Tooltip>
|
|
63
|
+
</TooltipProvider>
|
|
48
64
|
<Button
|
|
49
65
|
size="sm"
|
|
50
66
|
variant="ghost"
|
|
@@ -85,9 +85,9 @@ export function SpeakerNotesPanel({
|
|
|
85
85
|
)}
|
|
86
86
|
<button
|
|
87
87
|
onClick={toggle}
|
|
88
|
-
className="flex w-full cursor-pointer items-center justify-between px-4 py-1.5"
|
|
88
|
+
className="group flex w-full cursor-pointer items-center justify-between px-4 py-1.5"
|
|
89
89
|
>
|
|
90
|
-
<span className="text-[10px] font-medium text-muted-foreground uppercase tracking-wider">
|
|
90
|
+
<span className="text-[10px] font-medium text-muted-foreground uppercase tracking-wider transition-colors group-hover:text-foreground/80">
|
|
91
91
|
{t("raw.speakerNotes")}
|
|
92
92
|
</span>
|
|
93
93
|
{expanded ? (
|
package/corpus/templates/slides/app/components/editor/canvas-interactions/slides-canvas-adapter.ts
CHANGED
|
@@ -151,8 +151,11 @@ export function resolveSlidesCanvasPointerIntent({
|
|
|
151
151
|
) {
|
|
152
152
|
return "move-object-perimeter";
|
|
153
153
|
}
|
|
154
|
+
// The interior of an editable text object belongs to native text selection.
|
|
155
|
+
// Only the measured outer edge is reserved for moving the selected object.
|
|
156
|
+
if (targetIsEditableText) return "edit-text";
|
|
154
157
|
if (hasSelectedObject && targetWithinSelectedObject) {
|
|
155
158
|
return "move-object-body";
|
|
156
159
|
}
|
|
157
|
-
return
|
|
160
|
+
return "none";
|
|
158
161
|
}
|
|
@@ -151,6 +151,20 @@ export function snapshotEditableTextRange(
|
|
|
151
151
|
return getEditableTextRange(editable, selection)?.cloneRange() ?? null;
|
|
152
152
|
}
|
|
153
153
|
|
|
154
|
+
/** Selects the complete contents of one live slide text block. */
|
|
155
|
+
export function selectAllEditableText(
|
|
156
|
+
editable: HTMLElement,
|
|
157
|
+
selection: Selection | null = window.getSelection(),
|
|
158
|
+
) {
|
|
159
|
+
if (!selection) return false;
|
|
160
|
+
editable.focus({ preventScroll: true });
|
|
161
|
+
const range = document.createRange();
|
|
162
|
+
range.selectNodeContents(editable);
|
|
163
|
+
selection.removeAllRanges();
|
|
164
|
+
selection.addRange(range);
|
|
165
|
+
return true;
|
|
166
|
+
}
|
|
167
|
+
|
|
154
168
|
/** Restores a captured range only while it is still safe to use in this block. */
|
|
155
169
|
export function restoreEditableTextRange(
|
|
156
170
|
editable: HTMLElement,
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { findEnclosingList } from "./bullet-editing";
|
|
2
2
|
|
|
3
|
+
type EditingTarget = EventTarget | Element | null;
|
|
4
|
+
|
|
3
5
|
/** Inline markup changes presentation, but does not create a canvas target. */
|
|
4
6
|
const INLINE_TEXT_TAGS = new Set([
|
|
5
7
|
"SPAN",
|
|
@@ -23,6 +25,28 @@ export function isInlineTextElement(element: Element): boolean {
|
|
|
23
25
|
return INLINE_TEXT_TAGS.has(element.tagName);
|
|
24
26
|
}
|
|
25
27
|
|
|
28
|
+
function isEditingTarget(target: EditingTarget): boolean {
|
|
29
|
+
if (!(target instanceof Element)) return false;
|
|
30
|
+
return (
|
|
31
|
+
(target instanceof HTMLElement && target.isContentEditable) ||
|
|
32
|
+
target.closest('[contenteditable="true"], [data-editing-block="true"]') !==
|
|
33
|
+
null
|
|
34
|
+
);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/** Whether a keyboard/pointer event belongs to an active slide text edit. */
|
|
38
|
+
export function isSlideTextEditingTarget(
|
|
39
|
+
target: EditingTarget,
|
|
40
|
+
activeElement: Element | null = null,
|
|
41
|
+
editingElement: Element | null = null,
|
|
42
|
+
): boolean {
|
|
43
|
+
return (
|
|
44
|
+
isEditingTarget(target) ||
|
|
45
|
+
isEditingTarget(activeElement) ||
|
|
46
|
+
isEditingTarget(editingElement)
|
|
47
|
+
);
|
|
48
|
+
}
|
|
49
|
+
|
|
26
50
|
export function shouldStampBuilderId(element: HTMLElement): boolean {
|
|
27
51
|
return (
|
|
28
52
|
!element.classList.contains("fmd-layout-spacer") &&
|
|
@@ -40,12 +40,10 @@ type GranularOp =
|
|
|
40
40
|
op: "add-slide";
|
|
41
41
|
slideId: string;
|
|
42
42
|
afterSlideId?: string;
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
background?: string;
|
|
48
|
-
};
|
|
43
|
+
/** Everything but `id` and the transient `imageLoading` flag. A slide
|
|
44
|
+
* copied or restored through this op keeps its transition, animations,
|
|
45
|
+
* and image/Excalidraw data instead of silently losing them on reload. */
|
|
46
|
+
fields: Omit<Partial<Slide>, "id" | "imageLoading"> & { content: string };
|
|
49
47
|
}
|
|
50
48
|
| {
|
|
51
49
|
op: "patch-deck-fields";
|
|
@@ -57,6 +55,16 @@ type GranularOp =
|
|
|
57
55
|
| { op: "full-replace"; deck: Deck };
|
|
58
56
|
|
|
59
57
|
export type PatchDeckOp = Exclude<GranularOp, { op: "full-replace" }>;
|
|
58
|
+
|
|
59
|
+
/** Slide payload for an `add-slide` op. `imageLoading` is transient UI state
|
|
60
|
+
* and must not persist; everything else on the slide has to survive the round
|
|
61
|
+
* trip or a duplicated/restored slide comes back missing fields. */
|
|
62
|
+
function addSlideFields(
|
|
63
|
+
slide: Slide,
|
|
64
|
+
): Extract<GranularOp, { op: "add-slide" }>["fields"] {
|
|
65
|
+
const { id: _id, imageLoading: _imageLoading, ...fields } = slide;
|
|
66
|
+
return fields;
|
|
67
|
+
}
|
|
60
68
|
export type DeckReloadStatus = "loaded" | "failed" | "stale";
|
|
61
69
|
export interface UpdateSlideOptions {
|
|
62
70
|
persistence?: "debounced" | "immediate";
|
|
@@ -683,13 +691,11 @@ export function applyOpToDeck(deck: Deck, op: PatchDeckOp): Deck {
|
|
|
683
691
|
case "add-slide": {
|
|
684
692
|
if (deck.slides.some((s) => s.id === op.slideId)) return deck; // idempotent
|
|
685
693
|
const newSlide: Slide = {
|
|
694
|
+
...op.fields,
|
|
686
695
|
id: op.slideId,
|
|
687
696
|
content: op.fields.content,
|
|
688
697
|
notes: op.fields.notes ?? "",
|
|
689
698
|
layout: (op.fields.layout as SlideLayout) ?? "content",
|
|
690
|
-
...(op.fields.background !== undefined
|
|
691
|
-
? { background: op.fields.background }
|
|
692
|
-
: {}),
|
|
693
699
|
};
|
|
694
700
|
const slides = [...deck.slides];
|
|
695
701
|
const afterIdx = op.afterSlideId
|
|
@@ -822,14 +828,7 @@ export function deriveInverseOp(
|
|
|
822
828
|
op: "add-slide",
|
|
823
829
|
slideId: prior.id,
|
|
824
830
|
afterSlideId,
|
|
825
|
-
fields:
|
|
826
|
-
content: prior.content,
|
|
827
|
-
notes: prior.notes,
|
|
828
|
-
layout: prior.layout,
|
|
829
|
-
...(prior.background !== undefined
|
|
830
|
-
? { background: prior.background }
|
|
831
|
-
: {}),
|
|
832
|
-
},
|
|
831
|
+
fields: addSlideFields(prior),
|
|
833
832
|
},
|
|
834
833
|
{
|
|
835
834
|
op: "reorder-slides",
|
|
@@ -2127,12 +2126,7 @@ export function DeckProvider({ children }: { children: ReactNode }) {
|
|
|
2127
2126
|
op: "add-slide",
|
|
2128
2127
|
slideId: newSlide.id,
|
|
2129
2128
|
afterSlideId,
|
|
2130
|
-
fields:
|
|
2131
|
-
content: newSlide.content,
|
|
2132
|
-
notes: newSlide.notes,
|
|
2133
|
-
layout: newSlide.layout,
|
|
2134
|
-
background: newSlide.background,
|
|
2135
|
-
},
|
|
2129
|
+
fields: addSlideFields(newSlide),
|
|
2136
2130
|
};
|
|
2137
2131
|
enqueueDeckOp(deckId, op);
|
|
2138
2132
|
if (before) recordUndo(before, op, { label: "Add slide" });
|
|
@@ -2240,17 +2234,11 @@ export function DeckProvider({ children }: { children: ReactNode }) {
|
|
|
2240
2234
|
// Granular add-slide op — inserts the copy after the original. Build it
|
|
2241
2235
|
// from the current deck before scheduling the React state update; the
|
|
2242
2236
|
// functional updater runs later and cannot be used to produce the op.
|
|
2243
|
-
const { id: newSlideId, ...rest } = copiedSlide;
|
|
2244
2237
|
const op: PatchDeckOp = {
|
|
2245
2238
|
op: "add-slide",
|
|
2246
|
-
slideId:
|
|
2239
|
+
slideId: copiedSlide.id,
|
|
2247
2240
|
afterSlideId: slideId,
|
|
2248
|
-
fields:
|
|
2249
|
-
content: rest.content,
|
|
2250
|
-
notes: rest.notes,
|
|
2251
|
-
layout: rest.layout,
|
|
2252
|
-
background: rest.background,
|
|
2253
|
-
},
|
|
2241
|
+
fields: addSlideFields(copiedSlide),
|
|
2254
2242
|
};
|
|
2255
2243
|
enqueueDeckOp(deckId, op);
|
|
2256
2244
|
recordUndo(before, op, { label: "Duplicate slide" });
|
|
@@ -69,6 +69,7 @@ button[title="Open agent sidebar"] {
|
|
|
69
69
|
--sidebar-ring: 0 0% 50%;
|
|
70
70
|
|
|
71
71
|
--accent-cyan: 187 100% 50%;
|
|
72
|
+
--slides-thumbnail-shadow: 0 0% 0%;
|
|
72
73
|
}
|
|
73
74
|
|
|
74
75
|
.light {
|
|
@@ -115,6 +116,29 @@ button[title="Open agent sidebar"] {
|
|
|
115
116
|
--accent-cyan: 193 82% 31%;
|
|
116
117
|
}
|
|
117
118
|
|
|
119
|
+
[data-slides-thumbnail-scroll]::before {
|
|
120
|
+
position: absolute;
|
|
121
|
+
z-index: 20;
|
|
122
|
+
inset: 0.5rem 0.5rem auto;
|
|
123
|
+
height: 1rem;
|
|
124
|
+
pointer-events: none;
|
|
125
|
+
content: "";
|
|
126
|
+
opacity: 0;
|
|
127
|
+
background: radial-gradient(
|
|
128
|
+
ellipse 82% 125% at 50% 0%,
|
|
129
|
+
hsl(var(--slides-thumbnail-shadow) / 0.18) 0%,
|
|
130
|
+
hsl(var(--slides-thumbnail-shadow) / 0.08) 48%,
|
|
131
|
+
transparent 100%
|
|
132
|
+
);
|
|
133
|
+
box-shadow: 0 0.2rem 0.45rem -0.25rem
|
|
134
|
+
hsl(var(--slides-thumbnail-shadow) / 0.64);
|
|
135
|
+
transition: opacity 150ms ease-out;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
[data-slides-thumbnail-scroll="scrolled"]::before {
|
|
139
|
+
opacity: 1;
|
|
140
|
+
}
|
|
141
|
+
|
|
118
142
|
/* Slides keeps the shared visual inspector behavior, but the dock and context toolbar need the
|
|
119
143
|
flatter, filled-control treatment used by the slide editor. */
|
|
120
144
|
.slide-style-inspector,
|