@agent-native/core 0.146.1 → 0.146.3
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/slides/actions/import-file.ts +144 -99
- package/corpus/templates/slides/app/components/editor/EditorToolbar.tsx +56 -23
- package/corpus/templates/slides/app/components/editor/SlideEditor.tsx +1 -0
- package/corpus/templates/slides/app/components/editor/SlideOverflowWarning.tsx +4 -1
- package/corpus/templates/slides/app/context/DeckContext.tsx +91 -25
- package/corpus/templates/slides/app/hooks/use-deck-role.ts +14 -4
- package/corpus/templates/slides/app/i18n/en-US.ts +2 -0
- package/corpus/templates/slides/app/pages/DeckEditor.tsx +19 -7
- package/corpus/templates/slides/app/pages/Index.tsx +11 -2
- package/corpus/templates/slides/app/root.tsx +5 -1
- package/corpus/templates/slides/package.json +1 -0
- package/corpus/templates/slides/server/handlers/import/html-converter.ts +9 -0
- package/corpus/templates/slides/server/handlers/import/pdf-fidelity-parser.ts +960 -0
- package/dist/file-upload/actions/upload-image.d.ts +1 -1
- package/dist/ingestion/pptx.d.ts +1 -0
- package/dist/ingestion/pptx.js +353 -28
- package/dist/notifications/routes.d.ts +1 -1
- package/dist/resources/handlers.d.ts +1 -1
- package/docs/content/docs-components.mdx +122 -11
- package/package.json +2 -2
|
@@ -14,10 +14,18 @@ interface SharesResponse {
|
|
|
14
14
|
* `Viewer` = no edit affordances, but the editor shell is still navigable;
|
|
15
15
|
* any other role (Owner / Editor / Admin) gets full editing.
|
|
16
16
|
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
17
|
+
* `assumeEditorWhileLoading` should only be true when a faster, already-known
|
|
18
|
+
* signal (e.g. `deck.createdByMe`) confirms the caller is the owner — that
|
|
19
|
+
* avoids a flash of view-only chrome for owners without ever defaulting a
|
|
20
|
+
* viewer into edit affordances while the real role is still loading. Without
|
|
21
|
+
* that signal, `canEdit` stays false until the role query resolves, so a
|
|
22
|
+
* viewer can never trigger an edit that the server will reject and silently
|
|
23
|
+
* roll back.
|
|
19
24
|
*/
|
|
20
|
-
export function useDeckRole(
|
|
25
|
+
export function useDeckRole(
|
|
26
|
+
deckId: string | undefined,
|
|
27
|
+
assumeEditorWhileLoading = false,
|
|
28
|
+
): {
|
|
21
29
|
role: SharesResponse["role"] | undefined;
|
|
22
30
|
canEdit: boolean;
|
|
23
31
|
isLoading: boolean;
|
|
@@ -29,6 +37,8 @@ export function useDeckRole(deckId: string | undefined): {
|
|
|
29
37
|
);
|
|
30
38
|
const role = query.data?.role;
|
|
31
39
|
const canEdit =
|
|
32
|
-
role === undefined
|
|
40
|
+
role === undefined
|
|
41
|
+
? assumeEditorWhileLoading
|
|
42
|
+
: role === "owner" || role !== "viewer";
|
|
33
43
|
return { role, canEdit, isLoading: query.isLoading };
|
|
34
44
|
}
|
|
@@ -212,8 +212,10 @@ const messages = {
|
|
|
212
212
|
assetLibrary: "Asset Library",
|
|
213
213
|
diagrams: "Diagrams",
|
|
214
214
|
insertMermaidDiagram: "Insert Mermaid Diagram",
|
|
215
|
+
insertMermaidFailed: "Failed to insert diagram",
|
|
215
216
|
excalidrawCanvas: "Excalidraw Canvas",
|
|
216
217
|
convertMermaidToExcalidraw: "Convert Mermaid to Excalidraw",
|
|
218
|
+
convertMermaidFailed: "Failed to convert diagram",
|
|
217
219
|
removeExcalidrawCanvas: "Remove Excalidraw Canvas",
|
|
218
220
|
transition: "Transition",
|
|
219
221
|
transition_instant: "Instant",
|
|
@@ -44,7 +44,7 @@ import { QuestionFlow } from "@/components/editor/QuestionFlow";
|
|
|
44
44
|
import SlideEditor from "@/components/editor/SlideEditor";
|
|
45
45
|
import { TweaksPanel } from "@/components/editor/TweaksPanel";
|
|
46
46
|
import { Button } from "@/components/ui/button";
|
|
47
|
-
import { useDecks } from "@/context/DeckContext";
|
|
47
|
+
import { deckIdFromPathname, useDecks } from "@/context/DeckContext";
|
|
48
48
|
import { useAgentGenerating } from "@/hooks/use-agent-generating";
|
|
49
49
|
import { useDeckDesignSystem } from "@/hooks/use-deck-design-system";
|
|
50
50
|
import { useDeckPresence } from "@/hooks/use-deck-presence";
|
|
@@ -275,8 +275,10 @@ export default function DeckEditor() {
|
|
|
275
275
|
});
|
|
276
276
|
// Mirror Google Slides: viewers see the editor shell with edit affordances
|
|
277
277
|
// disabled (rather than a separate "viewer" route). Owners/Editors/Admins
|
|
278
|
-
// get the full editor.
|
|
279
|
-
|
|
278
|
+
// get the full editor. Only assume edit access while the role is still
|
|
279
|
+
// loading when `createdByMe` already confirms ownership — otherwise a
|
|
280
|
+
// viewer would briefly see (and could click) edit affordances.
|
|
281
|
+
const { canEdit } = useDeckRole(id, deck?.createdByMe === true);
|
|
280
282
|
const isNewDeckGenerating = shouldShowNewDeckGeneratingProgress({
|
|
281
283
|
generating,
|
|
282
284
|
isNewDeckCreation: wasNewDeckCreation.current,
|
|
@@ -1001,9 +1003,10 @@ export default function DeckEditor() {
|
|
|
1001
1003
|
onShowHistory={() => setHistoryOpen(!historyOpen)}
|
|
1002
1004
|
historyButtonRef={historyButtonRef}
|
|
1003
1005
|
currentSlide={currentSlide}
|
|
1004
|
-
onUpdateSlide={(updates) =>
|
|
1005
|
-
|
|
1006
|
-
|
|
1006
|
+
onUpdateSlide={(updates, slideIdOverride) => {
|
|
1007
|
+
const targetId = slideIdOverride ?? currentSlide?.id;
|
|
1008
|
+
if (targetId) updateSlide(id, targetId, updates);
|
|
1009
|
+
}}
|
|
1007
1010
|
activeUsers={slideActiveUsers.filter((u) => u.email !== session?.email)}
|
|
1008
1011
|
agentPresent={agentPresent}
|
|
1009
1012
|
agentActive={agentActive}
|
|
@@ -1025,7 +1028,16 @@ export default function DeckEditor() {
|
|
|
1025
1028
|
onToggleTextBoxMode={toggleTextBoxMode}
|
|
1026
1029
|
onDuplicateDeck={() => {
|
|
1027
1030
|
const newId = `deck-${nanoid()}`;
|
|
1028
|
-
const optimistic = duplicateDeck(id, newId)
|
|
1031
|
+
const optimistic = duplicateDeck(id, newId, undefined, () => {
|
|
1032
|
+
// The background duplicate-deck action failed after we already
|
|
1033
|
+
// navigated to the optimistic copy. If the user is still there,
|
|
1034
|
+
// send them back instead of stranding them on a "Deck
|
|
1035
|
+
// unavailable" screen for a deck that no longer exists.
|
|
1036
|
+
if (deckIdFromPathname(window.location.pathname) === newId) {
|
|
1037
|
+
navigate("/");
|
|
1038
|
+
}
|
|
1039
|
+
toast.error(t("home.duplicateFailed"));
|
|
1040
|
+
});
|
|
1029
1041
|
if (optimistic) navigate(`/deck/${optimistic.id}`);
|
|
1030
1042
|
}}
|
|
1031
1043
|
onExportPdf={async () => {
|
|
@@ -45,7 +45,7 @@ import {
|
|
|
45
45
|
describeDeckPersistenceFailure,
|
|
46
46
|
type Deck,
|
|
47
47
|
} from "@/context/DeckContext";
|
|
48
|
-
import { useDecks } from "@/context/DeckContext";
|
|
48
|
+
import { deckIdFromPathname, useDecks } from "@/context/DeckContext";
|
|
49
49
|
import { useAgentGenerating } from "@/hooks/use-agent-generating";
|
|
50
50
|
import { useDesignSystems } from "@/hooks/use-design-systems";
|
|
51
51
|
import { useWorkspaceDefaults } from "@/hooks/use-workspace-defaults";
|
|
@@ -1088,7 +1088,16 @@ export default function Index() {
|
|
|
1088
1088
|
(id: string) => {
|
|
1089
1089
|
let copy: ReturnType<typeof duplicateDeck> | undefined;
|
|
1090
1090
|
flushSync(() => {
|
|
1091
|
-
copy = duplicateDeck(id, `deck-${nanoid()}
|
|
1091
|
+
copy = duplicateDeck(id, `deck-${nanoid()}`, undefined, () => {
|
|
1092
|
+
// The background duplicate-deck action failed after we already
|
|
1093
|
+
// navigated to the optimistic copy's route. If the user is still
|
|
1094
|
+
// there, send them back to the deck list instead of stranding them
|
|
1095
|
+
// on a "Deck unavailable" screen for a deck that no longer exists.
|
|
1096
|
+
if (deckIdFromPathname(window.location.pathname) === copy?.id) {
|
|
1097
|
+
navigate("/");
|
|
1098
|
+
}
|
|
1099
|
+
toast.error(t("home.duplicateFailed"));
|
|
1100
|
+
});
|
|
1092
1101
|
});
|
|
1093
1102
|
// The context refuses a second copy of the same deck while the first
|
|
1094
1103
|
// one's action is still in flight.
|
|
@@ -97,7 +97,11 @@ function useExitSelectionOnOutsideClick() {
|
|
|
97
97
|
const target = e.target as HTMLElement;
|
|
98
98
|
if (
|
|
99
99
|
target.closest(".slide-content") ||
|
|
100
|
-
target.closest(".slide-image-clickable")
|
|
100
|
+
target.closest(".slide-image-clickable") ||
|
|
101
|
+
target.closest("[data-slide-context-toolbar]") ||
|
|
102
|
+
target.closest(
|
|
103
|
+
'[role="dialog"], [role="menu"], [role="listbox"], [data-radix-popper-content-wrapper], [data-radix-menu-content]',
|
|
104
|
+
)
|
|
101
105
|
) {
|
|
102
106
|
return;
|
|
103
107
|
}
|
|
@@ -334,9 +334,18 @@ function formatFidelityRun(
|
|
|
334
334
|
`font-style:${run.italic ? "italic" : "normal"}`,
|
|
335
335
|
`text-decoration:${run.underline ? "underline" : "none"}`,
|
|
336
336
|
].join(";");
|
|
337
|
+
const href = run.href && isSafeLinkHref(run.href) ? run.href : undefined;
|
|
338
|
+
if (href) {
|
|
339
|
+
return `<a href="${esc(href)}" target="_blank" rel="noopener noreferrer" style="${styles};">${esc(run.content)}</a>`;
|
|
340
|
+
}
|
|
337
341
|
return `<span style="${styles};">${esc(run.content)}</span>`;
|
|
338
342
|
}
|
|
339
343
|
|
|
344
|
+
/** A source PDF/PPTX link annotation is untrusted input — only render schemes a browser treats as navigation, never `javascript:`/`data:`/etc. */
|
|
345
|
+
function isSafeLinkHref(href: string): boolean {
|
|
346
|
+
return /^(https?:|mailto:)/i.test(href);
|
|
347
|
+
}
|
|
348
|
+
|
|
340
349
|
function fontWeightForFamily(
|
|
341
350
|
fontFamily: string | undefined,
|
|
342
351
|
fallback: number,
|