@agent-native/core 0.184.0 → 0.184.1-nightly-20260922203115
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/changelog/2026-09-22-apps-start-with-an-app-shaped-skeleton-while-session-data-lo.md +6 -0
- package/corpus/templates/calendar/changelog/2026-09-22-apps-start-with-an-app-shaped-skeleton-while-session-data-lo.md +6 -0
- package/corpus/templates/clips/changelog/2026-09-22-apps-start-with-an-app-shaped-skeleton-while-session-data-lo.md +6 -0
- package/corpus/templates/content/AGENTS.md +1 -1
- package/corpus/templates/content/actions/_document-search-ranking.ts +191 -0
- package/corpus/templates/content/actions/search-documents.ts +184 -49
- package/corpus/templates/content/changelog/2026-09-21-search-ranks-title-matches-first.md +6 -0
- package/corpus/templates/content/docs/product/capabilities/content.knowledge.search.md +11 -8
- package/corpus/templates/design/.generated/bridge/editor-chrome.generated.ts +10 -3
- package/corpus/templates/design/.generated/bridge/nav.generated.ts +1 -0
- package/corpus/templates/design/app/components/design/MultiScreenCanvas.tsx +67 -6
- package/corpus/templates/design/app/components/design/bridge/nav.bridge.ts +16 -0
- package/corpus/templates/design/app/components/design/multi-screen/board-surface-html.ts +26 -0
- package/corpus/templates/design/changelog/2026-09-22-apps-start-with-an-app-shaped-skeleton-while-session-data-lo.md +6 -0
- package/corpus/templates/design/changelog/2026-09-22-blocks-can-be-selected-with-one-click-inside-screens.md +6 -0
- package/corpus/templates/mail/app/components/layout/AppLayout.tsx +136 -123
- package/corpus/templates/mail/changelog/2026-09-22-apps-start-with-an-app-shaped-skeleton-while-session-data-lo.md +6 -0
- package/corpus/templates/slides/changelog/2026-09-22-apps-start-with-an-app-shaped-skeleton-while-session-data-lo.md +6 -0
- package/dist/client/AppShellSkeleton.d.ts +5 -0
- package/dist/client/AppShellSkeleton.js +69 -0
- package/dist/client/ClientOnly.d.ts +1 -1
- package/dist/client/ClientOnly.js +1 -1
- package/dist/client/LanguagePicker.d.ts +6 -0
- package/dist/client/LanguagePicker.js +128 -0
- package/dist/client/analytics.d.ts +3 -2
- package/dist/client/analytics.js +1 -1
- package/dist/client/app-providers.d.ts +3 -3
- package/dist/client/app-providers.js +53 -23
- package/dist/client/client-status-requests.d.ts +5 -0
- package/dist/client/client-status-requests.js +31 -18
- package/dist/client/i18n.d.ts +1 -6
- package/dist/client/i18n.js +3 -117
- package/dist/client/onboarding/first-run-startup-gate.js +14 -3
- package/dist/client/require-session.d.ts +1 -1
- package/dist/client/require-session.js +3 -3
- package/dist/client/session-replay-context.d.ts +16 -0
- package/dist/client/session-replay-context.js +93 -0
- package/dist/client/session-replay.d.ts +2 -26
- package/dist/client/session-replay.js +1 -64
- 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/deploy/build.js +19 -29
- package/dist/file-upload/actions/upload-image.d.ts +1 -1
- package/dist/observability/routes.d.ts +1 -1
- package/dist/progress/routes.d.ts +1 -1
- package/dist/resources/handlers.d.ts +1 -1
- package/dist/secrets/routes.d.ts +6 -6
- package/dist/server/agent-engine-api-key-route.d.ts +1 -1
- package/dist/server/core-routes-plugin.d.ts +1 -1
- package/dist/shared/ssr-session-bootstrap.d.ts +3 -0
- package/dist/shared/ssr-session-bootstrap.js +44 -0
- package/dist/sharing/actions/list-resource-shares.d.ts +1 -1
- package/dist/triggers/actions/manage-automation.d.ts +2 -2
- package/package.json +3 -3
|
@@ -294,7 +294,9 @@ import {
|
|
|
294
294
|
getBoardSurfaceHtml,
|
|
295
295
|
getBoardSurfaceRenderContent,
|
|
296
296
|
getBoardSurfaceStaticPreviewContent,
|
|
297
|
+
hasBoardRuntimeSurfaceContent,
|
|
297
298
|
hasBoardSurfaceContent,
|
|
299
|
+
shouldMountBoardSurface,
|
|
298
300
|
shouldRenderEmptyBoardReviewCanvas,
|
|
299
301
|
} from "./multi-screen/board-surface-html";
|
|
300
302
|
import {
|
|
@@ -729,6 +731,10 @@ export const MultiScreenCanvas = memo(function MultiScreenCanvas({
|
|
|
729
731
|
// before the first layout measurement.
|
|
730
732
|
const [surfaceSize, setSurfaceSize] = useState({ width: 0, height: 0 });
|
|
731
733
|
const [crossScreenDragActive, setCrossScreenDragActive] = useState(false);
|
|
734
|
+
const [boardRuntimeSurfaceState, setBoardRuntimeSurfaceState] = useState<{
|
|
735
|
+
boardFileId: string | null;
|
|
736
|
+
requestKeys: string[];
|
|
737
|
+
}>({ boardFileId: null, requestKeys: [] });
|
|
732
738
|
const [frameGeometry, setFrameGeometry] = useState<FrameGeometryById>({});
|
|
733
739
|
const frameGeometryRef = useRef(frameGeometry);
|
|
734
740
|
const renderedScreenIdsRef = useRef<Set<string>>(new Set());
|
|
@@ -898,10 +904,65 @@ export const MultiScreenCanvas = memo(function MultiScreenCanvas({
|
|
|
898
904
|
// Keep the empty board unmounted during normal editing so the text/shape
|
|
899
905
|
// creation path can still own its first mount. Mount it for the duration of
|
|
900
906
|
// a cross-screen drag so the drop has a real DOM target before release.
|
|
901
|
-
const boardSurfaceHtml =
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
907
|
+
const boardSurfaceHtml = shouldMountBoardSurface({
|
|
908
|
+
hasAuthoredContent: hasBoardSurfaceContent(boardFileContent),
|
|
909
|
+
crossScreenDragActive,
|
|
910
|
+
hasPendingRuntimeInsert: Boolean(boardRuntimeStructureInsertRequest),
|
|
911
|
+
hasRuntimeContent: hasBoardRuntimeSurfaceContent({
|
|
912
|
+
boardFileId,
|
|
913
|
+
runtimeBoardFileId: boardRuntimeSurfaceState.boardFileId,
|
|
914
|
+
runtimeRequestKeys: boardRuntimeSurfaceState.requestKeys,
|
|
915
|
+
}),
|
|
916
|
+
})
|
|
917
|
+
? getBoardSurfaceHtml(boardFileContent)
|
|
918
|
+
: undefined;
|
|
919
|
+
const handleBoardRuntimeStructureInsertApplied = useCallback(
|
|
920
|
+
(details: {
|
|
921
|
+
requestId: string;
|
|
922
|
+
transactionId?: string;
|
|
923
|
+
routePath?: string;
|
|
924
|
+
selector: string;
|
|
925
|
+
sourceId?: string;
|
|
926
|
+
applied?: boolean;
|
|
927
|
+
}) => {
|
|
928
|
+
if (details.applied !== false && details.selector && boardFileId) {
|
|
929
|
+
const requestKey = details.transactionId ?? details.requestId;
|
|
930
|
+
setBoardRuntimeSurfaceState((current) => {
|
|
931
|
+
const base =
|
|
932
|
+
current.boardFileId === boardFileId
|
|
933
|
+
? current
|
|
934
|
+
: { boardFileId, requestKeys: [] };
|
|
935
|
+
return base.requestKeys.includes(requestKey)
|
|
936
|
+
? base
|
|
937
|
+
: { ...base, requestKeys: [...base.requestKeys, requestKey] };
|
|
938
|
+
});
|
|
939
|
+
}
|
|
940
|
+
onBoardRuntimeStructureInsertApplied?.(details);
|
|
941
|
+
},
|
|
942
|
+
[boardFileId, onBoardRuntimeStructureInsertApplied],
|
|
943
|
+
);
|
|
944
|
+
const handleBoardRuntimeStructureRollbackResult = useCallback(
|
|
945
|
+
(details: {
|
|
946
|
+
requestId: string;
|
|
947
|
+
transactionId?: string;
|
|
948
|
+
applied: boolean;
|
|
949
|
+
reason?: string;
|
|
950
|
+
}) => {
|
|
951
|
+
if (details.applied && details.transactionId && boardFileId) {
|
|
952
|
+
setBoardRuntimeSurfaceState((current) => {
|
|
953
|
+
if (current.boardFileId !== boardFileId) return current;
|
|
954
|
+
const requestKeys = current.requestKeys.filter(
|
|
955
|
+
(key) => key !== details.transactionId,
|
|
956
|
+
);
|
|
957
|
+
return requestKeys.length > 0
|
|
958
|
+
? { ...current, requestKeys }
|
|
959
|
+
: { boardFileId: null, requestKeys: [] };
|
|
960
|
+
});
|
|
961
|
+
}
|
|
962
|
+
onBoardRuntimeStructureRollbackResult?.(details);
|
|
963
|
+
},
|
|
964
|
+
[boardFileId, onBoardRuntimeStructureRollbackResult],
|
|
965
|
+
);
|
|
905
966
|
const boardHasSurfaceContent = boardSurfaceHtml !== undefined;
|
|
906
967
|
const boardReviewGeometry = boardSurfaceRenderGeometry ?? {
|
|
907
968
|
x: 0,
|
|
@@ -10739,10 +10800,10 @@ export const MultiScreenCanvas = memo(function MultiScreenCanvas({
|
|
|
10739
10800
|
onBoardRuntimeStructureInsertRejected
|
|
10740
10801
|
}
|
|
10741
10802
|
onRuntimeStructureInsertApplied={
|
|
10742
|
-
|
|
10803
|
+
handleBoardRuntimeStructureInsertApplied
|
|
10743
10804
|
}
|
|
10744
10805
|
onRuntimeStructureRollbackResult={
|
|
10745
|
-
|
|
10806
|
+
handleBoardRuntimeStructureRollbackResult
|
|
10746
10807
|
}
|
|
10747
10808
|
clearSelectionRequest={boardClearSelectionRequest}
|
|
10748
10809
|
selectedSelector={boardSelectedSelector ?? null}
|
|
@@ -11,6 +11,13 @@
|
|
|
11
11
|
* - internal/relative links (or an explicit `data-screen`): asked to switch
|
|
12
12
|
* to the matching screen in a multi-screen design; otherwise a no-op so the
|
|
13
13
|
* prototype never blows itself away.
|
|
14
|
+
* - Cmd/Ctrl/Shift-click and middle-click on a real `<a href>`: left
|
|
15
|
+
* completely alone so the browser's own new-tab/new-window gesture runs
|
|
16
|
+
* (see the modifier-click guard below). A screen switch is a same-tab
|
|
17
|
+
* mutation of the live editor's mode/selection state; forcing it on a
|
|
18
|
+
* click whose entire point was "don't disturb my current tab" corrupts
|
|
19
|
+
* that state instead of opening a new one, which is what produced the
|
|
20
|
+
* Interact-mode crash this guard fixes.
|
|
14
21
|
*
|
|
15
22
|
* Protocol (iframe → parent):
|
|
16
23
|
*
|
|
@@ -47,6 +54,15 @@
|
|
|
47
54
|
if (!t || !t.closest) return;
|
|
48
55
|
var a = t.closest("a[href], [data-screen]") as HTMLElement | null;
|
|
49
56
|
if (!a) return;
|
|
57
|
+
// Cmd/Ctrl-click (new tab), Shift-click (new window), and middle-click
|
|
58
|
+
// are the browser's own "open this somewhere else, leave my tab alone"
|
|
59
|
+
// gestures. Forcing our same-tab screen-switch/postMessage path on top
|
|
60
|
+
// of one of these ignores that intent and mutates the live editor's
|
|
61
|
+
// mode/selection state on a click the user never meant to touch the
|
|
62
|
+
// current tab with. Step aside entirely and let the browser (or, for a
|
|
63
|
+
// `data-screen`-only element with no real destination, simply nothing)
|
|
64
|
+
// handle it instead.
|
|
65
|
+
if (e.metaKey || e.ctrlKey || e.shiftKey || e.button !== 0) return;
|
|
50
66
|
var ds = a.getAttribute && a.getAttribute("data-screen");
|
|
51
67
|
// In-page anchors ('#...') and empty hrefs must be handled in-document.
|
|
52
68
|
// A srcdoc document resolves '#'/'' against the PARENT app URL, so the
|
|
@@ -17,6 +17,32 @@ export function getBoardSurfaceHtml(html: string | undefined) {
|
|
|
17
17
|
return hasBoardSurfaceContent(html) ? html : EMPTY_BOARD_SURFACE_HTML;
|
|
18
18
|
}
|
|
19
19
|
|
|
20
|
+
export function shouldMountBoardSurface(args: {
|
|
21
|
+
hasAuthoredContent: boolean;
|
|
22
|
+
crossScreenDragActive: boolean;
|
|
23
|
+
hasPendingRuntimeInsert: boolean;
|
|
24
|
+
hasRuntimeContent: boolean;
|
|
25
|
+
}): boolean {
|
|
26
|
+
return (
|
|
27
|
+
args.hasAuthoredContent ||
|
|
28
|
+
args.crossScreenDragActive ||
|
|
29
|
+
args.hasPendingRuntimeInsert ||
|
|
30
|
+
args.hasRuntimeContent
|
|
31
|
+
);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export function hasBoardRuntimeSurfaceContent(args: {
|
|
35
|
+
boardFileId?: string;
|
|
36
|
+
runtimeBoardFileId: string | null;
|
|
37
|
+
runtimeRequestKeys: readonly string[];
|
|
38
|
+
}): boolean {
|
|
39
|
+
return (
|
|
40
|
+
args.boardFileId !== undefined &&
|
|
41
|
+
args.runtimeBoardFileId === args.boardFileId &&
|
|
42
|
+
args.runtimeRequestKeys.length > 0
|
|
43
|
+
);
|
|
44
|
+
}
|
|
45
|
+
|
|
20
46
|
export function shouldRenderEmptyBoardReviewCanvas(args: {
|
|
21
47
|
hasSurfaceContent: boolean;
|
|
22
48
|
reviewPinMode: boolean;
|
|
@@ -44,14 +44,19 @@ import {
|
|
|
44
44
|
IconFilter,
|
|
45
45
|
} from "@tabler/icons-react";
|
|
46
46
|
import { useQueryClient } from "@tanstack/react-query";
|
|
47
|
-
import {
|
|
47
|
+
import {
|
|
48
|
+
lazy,
|
|
49
|
+
Suspense,
|
|
50
|
+
useState,
|
|
51
|
+
useCallback,
|
|
52
|
+
useRef,
|
|
53
|
+
useEffect,
|
|
54
|
+
useMemo,
|
|
55
|
+
} from "react";
|
|
48
56
|
import { Link, useNavigate, useLocation, useSearchParams } from "react-router";
|
|
49
57
|
import { toast } from "sonner";
|
|
50
58
|
|
|
51
|
-
import {
|
|
52
|
-
ComposeModal,
|
|
53
|
-
type ComposePaletteCommands,
|
|
54
|
-
} from "@/components/email/ComposeModal";
|
|
59
|
+
import type { ComposePaletteCommands } from "@/components/email/ComposeModal";
|
|
55
60
|
import { SnoozeModal } from "@/components/email/SnoozeModal";
|
|
56
61
|
import { GoogleConnectBanner } from "@/components/GoogleConnectBanner";
|
|
57
62
|
import { ThemeToggle } from "@/components/ThemeToggle";
|
|
@@ -121,6 +126,12 @@ import { useHeaderTitle, useHeaderActions } from "./HeaderActions";
|
|
|
121
126
|
import { SearchBar } from "./SearchBar";
|
|
122
127
|
import { useCommandPaletteFocus } from "./use-command-palette-focus";
|
|
123
128
|
|
|
129
|
+
const ComposeModal = lazy(() =>
|
|
130
|
+
import("@/components/email/ComposeModal").then(({ ComposeModal }) => ({
|
|
131
|
+
default: ComposeModal,
|
|
132
|
+
})),
|
|
133
|
+
);
|
|
134
|
+
|
|
124
135
|
const BARE_ROUTES = new Set(["/email"]);
|
|
125
136
|
const EMPTY_SAVED_FILTERS: SavedMailFilter[] = [];
|
|
126
137
|
|
|
@@ -2068,138 +2079,140 @@ function AppLayoutInner({ children }: AppLayoutProps) {
|
|
|
2068
2079
|
const popoutActiveDraft =
|
|
2069
2080
|
popoutDrafts.find((d) => d.id === popoutActiveId) ?? null;
|
|
2070
2081
|
return (
|
|
2071
|
-
<
|
|
2072
|
-
|
|
2073
|
-
|
|
2074
|
-
|
|
2075
|
-
|
|
2076
|
-
|
|
2077
|
-
|
|
2078
|
-
|
|
2079
|
-
|
|
2080
|
-
|
|
2081
|
-
|
|
2082
|
-
|
|
2083
|
-
|
|
2084
|
-
|
|
2085
|
-
|
|
2086
|
-
|
|
2087
|
-
|
|
2088
|
-
|
|
2089
|
-
|
|
2090
|
-
|
|
2091
|
-
|
|
2092
|
-
label: t("mail.compose.reopenDraft"),
|
|
2093
|
-
onClick: async () => {
|
|
2094
|
-
const savedSnapshot = applyDraftSaveResult(
|
|
2095
|
-
snapshot,
|
|
2096
|
-
await savePromise,
|
|
2097
|
-
);
|
|
2098
|
-
const { id: _id, ...reopenData } = savedSnapshot;
|
|
2099
|
-
compose.open(reopenData);
|
|
2100
|
-
},
|
|
2101
|
-
},
|
|
2102
|
-
cancel: {
|
|
2103
|
-
label: t("mail.compose.deleteDraft"),
|
|
2104
|
-
onClick: async () => {
|
|
2105
|
-
const savedSnapshot = applyDraftSaveResult(
|
|
2106
|
-
snapshot,
|
|
2107
|
-
await savePromise,
|
|
2108
|
-
);
|
|
2109
|
-
if (savedSnapshot.savedDraftId) {
|
|
2110
|
-
await compose.deleteSavedDraft(savedSnapshot);
|
|
2111
|
-
}
|
|
2112
|
-
},
|
|
2113
|
-
},
|
|
2114
|
-
});
|
|
2115
|
-
}
|
|
2116
|
-
}}
|
|
2117
|
-
onCloseAll={() => {
|
|
2118
|
-
const draftsWithContent = popoutDrafts.filter(
|
|
2119
|
-
(d) =>
|
|
2120
|
-
!!(
|
|
2121
|
-
d.to?.trim() ||
|
|
2122
|
-
d.cc?.trim() ||
|
|
2123
|
-
d.bcc?.trim() ||
|
|
2124
|
-
d.subject?.trim() ||
|
|
2125
|
-
d.body?.trim()
|
|
2126
|
-
),
|
|
2127
|
-
);
|
|
2128
|
-
const snapshots = draftsWithContent.map((d) => ({ ...d }));
|
|
2129
|
-
const savePromises = compose.closeAll(
|
|
2130
|
-
popoutDrafts.map((draft) => draft.id),
|
|
2131
|
-
);
|
|
2132
|
-
if (snapshots.length > 0) {
|
|
2133
|
-
toast(
|
|
2134
|
-
t("mail.toasts.draftsClosed", { count: snapshots.length }),
|
|
2135
|
-
{
|
|
2082
|
+
<Suspense fallback={null}>
|
|
2083
|
+
<ComposeModal
|
|
2084
|
+
drafts={popoutDrafts}
|
|
2085
|
+
activeId={popoutActiveId}
|
|
2086
|
+
activeDraft={popoutActiveDraft}
|
|
2087
|
+
initialExpanded={composeInitialExpanded}
|
|
2088
|
+
onSetActiveId={compose.setActiveId}
|
|
2089
|
+
onUpdate={compose.update}
|
|
2090
|
+
onClose={(id) => {
|
|
2091
|
+
const draft = popoutDrafts.find((d) => d.id === id);
|
|
2092
|
+
const hasContent = !!(
|
|
2093
|
+
draft?.to?.trim() ||
|
|
2094
|
+
draft?.cc?.trim() ||
|
|
2095
|
+
draft?.bcc?.trim() ||
|
|
2096
|
+
draft?.subject?.trim() ||
|
|
2097
|
+
draft?.body?.trim()
|
|
2098
|
+
);
|
|
2099
|
+
const snapshot = draft ? { ...draft } : null;
|
|
2100
|
+
const savePromise = compose.close(id);
|
|
2101
|
+
if (hasContent && snapshot) {
|
|
2102
|
+
toast(t("mail.toasts.draftClosed"), {
|
|
2136
2103
|
action: {
|
|
2137
2104
|
label: t("mail.compose.reopenDraft"),
|
|
2138
2105
|
onClick: async () => {
|
|
2139
|
-
const
|
|
2140
|
-
|
|
2141
|
-
|
|
2142
|
-
result: await savePromises.get(snapshot.id),
|
|
2143
|
-
})),
|
|
2106
|
+
const savedSnapshot = applyDraftSaveResult(
|
|
2107
|
+
snapshot,
|
|
2108
|
+
await savePromise,
|
|
2144
2109
|
);
|
|
2145
|
-
|
|
2146
|
-
|
|
2147
|
-
result?.status === "failed" ||
|
|
2148
|
-
result?.status === "unavailable" ||
|
|
2149
|
-
result?.status === "cancelled"
|
|
2150
|
-
) {
|
|
2151
|
-
compose.setActiveId(snapshot.id);
|
|
2152
|
-
continue;
|
|
2153
|
-
}
|
|
2154
|
-
const savedSnapshot = applyDraftSaveResult(
|
|
2155
|
-
snapshot,
|
|
2156
|
-
result,
|
|
2157
|
-
);
|
|
2158
|
-
const { id: _id, ...reopenData } = savedSnapshot;
|
|
2159
|
-
compose.open(reopenData);
|
|
2160
|
-
}
|
|
2110
|
+
const { id: _id, ...reopenData } = savedSnapshot;
|
|
2111
|
+
compose.open(reopenData);
|
|
2161
2112
|
},
|
|
2162
2113
|
},
|
|
2163
2114
|
cancel: {
|
|
2164
|
-
label: t("mail.compose.
|
|
2115
|
+
label: t("mail.compose.deleteDraft"),
|
|
2165
2116
|
onClick: async () => {
|
|
2166
|
-
const
|
|
2167
|
-
|
|
2168
|
-
|
|
2169
|
-
result: await savePromises.get(snapshot.id),
|
|
2170
|
-
})),
|
|
2117
|
+
const savedSnapshot = applyDraftSaveResult(
|
|
2118
|
+
snapshot,
|
|
2119
|
+
await savePromise,
|
|
2171
2120
|
);
|
|
2172
|
-
|
|
2173
|
-
|
|
2174
|
-
|
|
2175
|
-
|
|
2176
|
-
|
|
2177
|
-
|
|
2178
|
-
|
|
2179
|
-
|
|
2121
|
+
if (savedSnapshot.savedDraftId) {
|
|
2122
|
+
await compose.deleteSavedDraft(savedSnapshot);
|
|
2123
|
+
}
|
|
2124
|
+
},
|
|
2125
|
+
},
|
|
2126
|
+
});
|
|
2127
|
+
}
|
|
2128
|
+
}}
|
|
2129
|
+
onCloseAll={() => {
|
|
2130
|
+
const draftsWithContent = popoutDrafts.filter(
|
|
2131
|
+
(d) =>
|
|
2132
|
+
!!(
|
|
2133
|
+
d.to?.trim() ||
|
|
2134
|
+
d.cc?.trim() ||
|
|
2135
|
+
d.bcc?.trim() ||
|
|
2136
|
+
d.subject?.trim() ||
|
|
2137
|
+
d.body?.trim()
|
|
2138
|
+
),
|
|
2139
|
+
);
|
|
2140
|
+
const snapshots = draftsWithContent.map((d) => ({ ...d }));
|
|
2141
|
+
const savePromises = compose.closeAll(
|
|
2142
|
+
popoutDrafts.map((draft) => draft.id),
|
|
2143
|
+
);
|
|
2144
|
+
if (snapshots.length > 0) {
|
|
2145
|
+
toast(
|
|
2146
|
+
t("mail.toasts.draftsClosed", { count: snapshots.length }),
|
|
2147
|
+
{
|
|
2148
|
+
action: {
|
|
2149
|
+
label: t("mail.compose.reopenDraft"),
|
|
2150
|
+
onClick: async () => {
|
|
2151
|
+
const saveResults = await Promise.all(
|
|
2152
|
+
snapshots.map(async (snapshot) => ({
|
|
2153
|
+
snapshot,
|
|
2154
|
+
result: await savePromises.get(snapshot.id),
|
|
2155
|
+
})),
|
|
2156
|
+
);
|
|
2157
|
+
for (const { snapshot, result } of saveResults) {
|
|
2158
|
+
if (
|
|
2159
|
+
result?.status === "failed" ||
|
|
2160
|
+
result?.status === "unavailable" ||
|
|
2161
|
+
result?.status === "cancelled"
|
|
2162
|
+
) {
|
|
2163
|
+
compose.setActiveId(snapshot.id);
|
|
2164
|
+
continue;
|
|
2165
|
+
}
|
|
2166
|
+
const savedSnapshot = applyDraftSaveResult(
|
|
2167
|
+
snapshot,
|
|
2168
|
+
result,
|
|
2169
|
+
);
|
|
2170
|
+
const { id: _id, ...reopenData } = savedSnapshot;
|
|
2171
|
+
compose.open(reopenData);
|
|
2180
2172
|
}
|
|
2181
|
-
|
|
2182
|
-
|
|
2183
|
-
|
|
2173
|
+
},
|
|
2174
|
+
},
|
|
2175
|
+
cancel: {
|
|
2176
|
+
label: t("mail.compose.deleteDrafts"),
|
|
2177
|
+
onClick: async () => {
|
|
2178
|
+
const saveResults = await Promise.all(
|
|
2179
|
+
snapshots.map(async (snapshot) => ({
|
|
2180
|
+
snapshot,
|
|
2181
|
+
result: await savePromises.get(snapshot.id),
|
|
2182
|
+
})),
|
|
2184
2183
|
);
|
|
2185
|
-
|
|
2186
|
-
|
|
2184
|
+
for (const { snapshot, result } of saveResults) {
|
|
2185
|
+
if (
|
|
2186
|
+
result?.status === "failed" ||
|
|
2187
|
+
result?.status === "unavailable" ||
|
|
2188
|
+
result?.status === "cancelled"
|
|
2189
|
+
) {
|
|
2190
|
+
compose.discard(snapshot.id);
|
|
2191
|
+
continue;
|
|
2192
|
+
}
|
|
2193
|
+
const savedSnapshot = applyDraftSaveResult(
|
|
2194
|
+
snapshot,
|
|
2195
|
+
result,
|
|
2196
|
+
);
|
|
2197
|
+
if (savedSnapshot.savedDraftId) {
|
|
2198
|
+
await compose.deleteSavedDraft(savedSnapshot);
|
|
2199
|
+
}
|
|
2187
2200
|
}
|
|
2188
|
-
}
|
|
2201
|
+
},
|
|
2189
2202
|
},
|
|
2190
2203
|
},
|
|
2191
|
-
|
|
2192
|
-
|
|
2193
|
-
}
|
|
2194
|
-
|
|
2195
|
-
|
|
2196
|
-
|
|
2197
|
-
|
|
2198
|
-
|
|
2199
|
-
|
|
2200
|
-
|
|
2201
|
-
|
|
2202
|
-
|
|
2204
|
+
);
|
|
2205
|
+
}
|
|
2206
|
+
}}
|
|
2207
|
+
onDiscard={compose.discard}
|
|
2208
|
+
onStageForSend={compose.stageForSend}
|
|
2209
|
+
onRestoreAfterSend={compose.restoreAfterSend}
|
|
2210
|
+
onNewDraft={handleCompose}
|
|
2211
|
+
onFlush={compose.flush}
|
|
2212
|
+
onInitialExpandedConsumed={clearComposeInitialExpanded}
|
|
2213
|
+
onRegisterComposeCommands={registerComposePaletteCommands}
|
|
2214
|
+
/>
|
|
2215
|
+
</Suspense>
|
|
2203
2216
|
);
|
|
2204
2217
|
})()}
|
|
2205
2218
|
<CommandPalette
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
const BLOCK_STYLE = {
|
|
3
|
+
display: "block",
|
|
4
|
+
backgroundColor: "hsl(var(--muted, 240 5% 96.1%))",
|
|
5
|
+
borderRadius: 6,
|
|
6
|
+
opacity: 0.7,
|
|
7
|
+
};
|
|
8
|
+
function Block({ style }) {
|
|
9
|
+
return _jsx("span", { "aria-hidden": true, style: { ...BLOCK_STYLE, ...style } });
|
|
10
|
+
}
|
|
11
|
+
export function AppShellSkeleton({ ariaLabel = "Loading application", height = "var(--agent-native-viewport-height, 100vh)", }) {
|
|
12
|
+
return (_jsxs("div", { role: "status", "aria-label": ariaLabel, "data-agent-native-app-skeleton": "true", style: {
|
|
13
|
+
display: "flex",
|
|
14
|
+
height,
|
|
15
|
+
width: "100%",
|
|
16
|
+
overflow: "hidden",
|
|
17
|
+
backgroundColor: "hsl(var(--background, 0 0% 100%))",
|
|
18
|
+
color: "hsl(var(--foreground, 240 10% 3.9%))",
|
|
19
|
+
}, children: [_jsx("style", { children: `
|
|
20
|
+
[data-agent-native-app-skeleton] [aria-hidden="true"] {
|
|
21
|
+
animation: an-app-shell-skeleton-pulse 1.2s ease-in-out infinite;
|
|
22
|
+
}
|
|
23
|
+
@keyframes an-app-shell-skeleton-pulse {
|
|
24
|
+
0%, 100% { opacity: 0.45; }
|
|
25
|
+
50% { opacity: 0.85; }
|
|
26
|
+
}
|
|
27
|
+
@media (prefers-reduced-motion: reduce) {
|
|
28
|
+
[data-agent-native-app-skeleton] [aria-hidden="true"] { animation: none; }
|
|
29
|
+
}
|
|
30
|
+
@media (max-width: 767px) {
|
|
31
|
+
[data-agent-native-app-skeleton] [data-agent-native-app-skeleton-sidebar] { display: none; }
|
|
32
|
+
}
|
|
33
|
+
` }), _jsxs("aside", { "data-agent-native-app-skeleton-sidebar": "true", "aria-hidden": "true", style: {
|
|
34
|
+
display: "flex",
|
|
35
|
+
width: 248,
|
|
36
|
+
flexShrink: 0,
|
|
37
|
+
flexDirection: "column",
|
|
38
|
+
gap: 16,
|
|
39
|
+
borderRight: "1px solid hsl(var(--border, 240 5.9% 90%))",
|
|
40
|
+
padding: 16,
|
|
41
|
+
}, children: [_jsx(Block, { style: { width: 132, height: 32 } }), _jsx("div", { style: { display: "flex", flexDirection: "column", gap: 10 }, children: [0, 1, 2, 3, 4, 5].map((item) => (_jsx(Block, { style: { width: `${68 + (item % 3) * 14}%`, height: 14 } }, item))) })] }), _jsxs("main", { style: {
|
|
42
|
+
display: "flex",
|
|
43
|
+
minWidth: 0,
|
|
44
|
+
flex: 1,
|
|
45
|
+
flexDirection: "column",
|
|
46
|
+
}, children: [_jsxs("header", { "aria-hidden": "true", style: {
|
|
47
|
+
display: "flex",
|
|
48
|
+
height: 48,
|
|
49
|
+
flexShrink: 0,
|
|
50
|
+
alignItems: "center",
|
|
51
|
+
gap: 12,
|
|
52
|
+
borderBottom: "1px solid hsl(var(--border, 240 5.9% 90%))",
|
|
53
|
+
padding: "0 16px",
|
|
54
|
+
}, children: [_jsx(Block, { style: { width: 32, height: 32, borderRadius: 8 } }), _jsx(Block, { style: { width: 128, height: 14 } })] }), _jsxs("section", { "aria-hidden": "true", style: {
|
|
55
|
+
display: "flex",
|
|
56
|
+
width: "100%",
|
|
57
|
+
maxWidth: 960,
|
|
58
|
+
flex: 1,
|
|
59
|
+
flexDirection: "column",
|
|
60
|
+
gap: 12,
|
|
61
|
+
margin: "0 auto",
|
|
62
|
+
padding: 24,
|
|
63
|
+
}, children: [_jsx(Block, { style: { width: "38%", height: 28, marginBottom: 8 } }), _jsx(Block, { style: { width: "24%", height: 14, marginBottom: 16 } }), [0, 1, 2, 3, 4, 5].map((item) => (_jsxs("div", { style: { display: "flex", alignItems: "center", gap: 12 }, children: [_jsx(Block, { style: { width: 32, height: 32, borderRadius: 8 } }), _jsxs("div", { style: {
|
|
64
|
+
display: "flex",
|
|
65
|
+
flex: 1,
|
|
66
|
+
flexDirection: "column",
|
|
67
|
+
gap: 8,
|
|
68
|
+
}, children: [_jsx(Block, { style: { width: `${52 + (item % 3) * 12}%`, height: 12 } }), _jsx(Block, { style: { width: `${34 + (item % 4) * 10}%`, height: 10 } })] })] }, item)))] })] })] }));
|
|
69
|
+
}
|
|
@@ -3,7 +3,7 @@ import { type ReactNode } from "react";
|
|
|
3
3
|
* Renders children only on the client (after hydration).
|
|
4
4
|
*
|
|
5
5
|
* Used in root.tsx to wrap all app content so the server only renders
|
|
6
|
-
* the HTML shell (meta tags, styles, scripts) +
|
|
6
|
+
* the HTML shell (meta tags, styles, scripts) + an app-shell skeleton.
|
|
7
7
|
* This prevents hydration mismatches from browser-only APIs like
|
|
8
8
|
* window, localStorage, new Date(), next-themes, etc.
|
|
9
9
|
*/
|
|
@@ -4,7 +4,7 @@ const useBrowserLayoutEffect = typeof window === "undefined" ? useEffect : useLa
|
|
|
4
4
|
* Renders children only on the client (after hydration).
|
|
5
5
|
*
|
|
6
6
|
* Used in root.tsx to wrap all app content so the server only renders
|
|
7
|
-
* the HTML shell (meta tags, styles, scripts) +
|
|
7
|
+
* the HTML shell (meta tags, styles, scripts) + an app-shell skeleton.
|
|
8
8
|
* This prevents hydration mismatches from browser-only APIs like
|
|
9
9
|
* window, localStorage, new Date(), next-themes, etc.
|
|
10
10
|
*/
|