@agent-native/core 0.153.10 → 0.154.0
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/app/i18n-data.ts +10 -10
- package/corpus/templates/content/scripts/check-native-deps.mjs +181 -15
- 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/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/EditorSidebar.tsx +58 -33
- package/corpus/templates/slides/app/components/editor/EditorToolbar.tsx +28 -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/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/global.css +24 -0
- package/corpus/templates/slides/app/hooks/use-navigation-state.ts +5 -1
- 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/Index.tsx +25 -9
- package/corpus/templates/slides/netlify.toml +1 -0
- package/corpus/templates/slides/server/lib/builder-design-system-proxy.ts +14 -2
- 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/resources/handlers.d.ts +1 -1
- package/dist/secrets/routes.d.ts +3 -3
- 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/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 +3 -3
|
@@ -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") &&
|
|
@@ -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,
|
|
@@ -4,6 +4,7 @@ import { useQuery, useQueryClient } from "@tanstack/react-query";
|
|
|
4
4
|
import { useEffect, useRef } from "react";
|
|
5
5
|
import { useLocation, useNavigate } from "react-router";
|
|
6
6
|
|
|
7
|
+
import { readStoredDeckFilter, resolveDeckFilter } from "@/lib/deck-filter";
|
|
7
8
|
import { TAB_ID } from "@/lib/tab-id";
|
|
8
9
|
|
|
9
10
|
export interface NavigationState {
|
|
@@ -59,7 +60,10 @@ export function useNavigationState() {
|
|
|
59
60
|
} else {
|
|
60
61
|
const params = new URLSearchParams(location.search);
|
|
61
62
|
state.deckFilter =
|
|
62
|
-
params.get("createdBy") ===
|
|
63
|
+
resolveDeckFilter(params.get("createdBy"), readStoredDeckFilter()) ===
|
|
64
|
+
"mine"
|
|
65
|
+
? "created-by-me"
|
|
66
|
+
: "all";
|
|
63
67
|
}
|
|
64
68
|
|
|
65
69
|
const write = (key: string) =>
|
|
@@ -242,8 +242,7 @@ const messages = {
|
|
|
242
242
|
code: "Code",
|
|
243
243
|
comments: "Comments",
|
|
244
244
|
editorLink: "Editor link",
|
|
245
|
-
editorLinkDescription:
|
|
246
|
-
"Opens the deck in the editor. Anyone with access can use this link.",
|
|
245
|
+
editorLinkDescription: "Opens the deck in the editor",
|
|
247
246
|
presentationLink: "Presentation link",
|
|
248
247
|
presentationLinkDescription:
|
|
249
248
|
"Opens directly in fullscreen presentation mode.",
|
|
@@ -251,7 +250,7 @@ const messages = {
|
|
|
251
250
|
more: "More",
|
|
252
251
|
importing: "Importing...",
|
|
253
252
|
importFile: "Import file",
|
|
254
|
-
savedVersions: "
|
|
253
|
+
savedVersions: "History",
|
|
255
254
|
lightTheme: "Light theme",
|
|
256
255
|
darkTheme: "Dark theme",
|
|
257
256
|
},
|
|
@@ -273,8 +272,8 @@ const messages = {
|
|
|
273
272
|
exportPdf: "Export as PDF",
|
|
274
273
|
exportPptx: "Export as PPTX",
|
|
275
274
|
connectGoogle: "Connect Google",
|
|
276
|
-
openInGoogleSlides: "
|
|
277
|
-
googleSlidesCreated: "
|
|
275
|
+
openInGoogleSlides: "Export to Google Slides",
|
|
276
|
+
googleSlidesCreated: "Exported to Google Slides",
|
|
278
277
|
googleSlidesCreatedHint:
|
|
279
278
|
"A copy of this deck was created in your Google Drive.",
|
|
280
279
|
duplicateDeck: "Duplicate deck",
|
|
@@ -494,17 +493,17 @@ const messages = {
|
|
|
494
493
|
"The deck was rolled back to the selected snapshot.",
|
|
495
494
|
restoreFailed: "Restore failed",
|
|
496
495
|
restoreFailedDescription: "Could not restore this deck version.",
|
|
497
|
-
backToSavedVersions: "Back to
|
|
498
|
-
savedVersions: "
|
|
499
|
-
description: "Browse
|
|
496
|
+
backToSavedVersions: "Back to history",
|
|
497
|
+
savedVersions: "History",
|
|
498
|
+
description: "Browse deck history and restore a previous snapshot.",
|
|
500
499
|
untitled: "Untitled",
|
|
501
500
|
snapshotUnavailable: "Snapshot unavailable",
|
|
502
501
|
slideNumber: "Slide {{number}}",
|
|
503
502
|
noSlidesInSnapshot: "No slides in this snapshot.",
|
|
504
503
|
restoreThisVersion: "Restore this version",
|
|
505
|
-
noSavedVersions: "No
|
|
504
|
+
noSavedVersions: "No history yet",
|
|
506
505
|
noSavedVersionsDescription:
|
|
507
|
-
"
|
|
506
|
+
"History is saved automatically before future deck edits.",
|
|
508
507
|
},
|
|
509
508
|
editorSidebar: {
|
|
510
509
|
selectSlide: "Select slide {{number}}",
|
|
@@ -561,6 +560,10 @@ const messages = {
|
|
|
561
560
|
deckHasNoSlides: "Deck has no slides.",
|
|
562
561
|
pdfRenderFailed: "Could not render PDF.",
|
|
563
562
|
buildingDeck: "Building deck",
|
|
563
|
+
layoutOverflowWarning: "Layout overflows",
|
|
564
|
+
layoutOverflowDetails: "Show overflow details",
|
|
565
|
+
layoutOverflowVertical: "Vertical overflow: {{pixels}}px",
|
|
566
|
+
layoutOverflowHorizontal: "Horizontal overflow: {{pixels}}px",
|
|
564
567
|
dismissLayoutWarning: "Dismiss layout warning",
|
|
565
568
|
slidesAdded_one: "{{count}} slide added",
|
|
566
569
|
slidesAdded_other: "{{count}} slides added",
|
|
@@ -606,6 +609,8 @@ const messages = {
|
|
|
606
609
|
websitePlaceholder: "example.com or Nike",
|
|
607
610
|
add: "Add",
|
|
608
611
|
githubRepository: "Connect Code: GitHub repository",
|
|
612
|
+
githubRef: "Branch, tag, or commit (optional)",
|
|
613
|
+
githubPaths: "Files or folders, comma-separated (optional)",
|
|
609
614
|
codeFiles: "Connect Code Files",
|
|
610
615
|
codeFilesDrop:
|
|
611
616
|
"CSS, Tailwind config, theme files, design.md — drop or click",
|
|
@@ -639,6 +644,10 @@ const messages = {
|
|
|
639
644
|
sourceUnavailableDescription:
|
|
640
645
|
"Builder could not load the latest source results.",
|
|
641
646
|
sourceOpenInBuilder: "Open in Builder",
|
|
647
|
+
syncSource: "Sync source",
|
|
648
|
+
syncingSource: "Syncing source...",
|
|
649
|
+
syncStarted: "Design system sync started",
|
|
650
|
+
syncFailed: "Could not sync design system",
|
|
642
651
|
loading: "Loading design system",
|
|
643
652
|
loadFailed: "We couldn't load this design system. Close and try again.",
|
|
644
653
|
cancel: "Cancel",
|