@agent-native/core 0.183.0-nightly-20260921181954 → 0.183.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/calendar/CHANGELOG.md +6 -0
- package/corpus/templates/clips/CHANGELOG.md +59 -0
- package/corpus/templates/design/CHANGELOG.md +92 -0
- package/corpus/templates/design/app/components/design/EditPanel.tsx +10 -1
- package/corpus/templates/design/app/components/design/edit-panel/typography-properties.tsx +82 -22
- package/corpus/templates/design/app/lib/font-upload.ts +90 -0
- package/corpus/templates/design/app/pages/design-editor/code-layer-state.ts +61 -9
- package/corpus/templates/design/changelog/2026-09-21-design-includes-more-google-fonts-and-supports-custom-font-u.md +6 -0
- package/corpus/templates/design/server/handlers/fonts.ts +171 -0
- package/corpus/templates/design/server/routes/api/fonts.post.ts +1 -0
- package/corpus/templates/mail/CHANGELOG.md +13 -0
- package/corpus/templates/slides/CHANGELOG.md +38 -0
- package/corpus/templates/slides/app/components/editor/SlideContextToolbar.tsx +5 -1
- package/dist/automation/index.d.ts +1 -1
- package/dist/client/org/TeamPage.js +1 -1
- package/dist/client/settings/SecretsSection.js +1 -1
- package/dist/client/settings/SettingsPanel.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/file-upload/actions/upload-image.d.ts +2 -2
- package/dist/observability/routes.d.ts +4 -4
- package/dist/org/handlers.d.ts +1 -1
- package/dist/resources/handlers.d.ts +1 -1
- package/dist/server/agent-engine-api-key-route.d.ts +2 -2
- package/dist/server/core-routes-plugin.d.ts +1 -1
- package/dist/server/realtime-token.d.ts +1 -1
- package/package.json +4 -4
package/corpus/README.md
CHANGED
|
@@ -3,6 +3,12 @@
|
|
|
3
3
|
All notable user-facing changes to Agent-Native Calendar are documented here. Open it any
|
|
4
4
|
time from the command menu (Cmd+K → "What's new") or from Settings.
|
|
5
5
|
|
|
6
|
+
## 2026-09-18
|
|
7
|
+
|
|
8
|
+
### Fixed
|
|
9
|
+
|
|
10
|
+
- Calendar now preserves grouped guest counts and keeps the event detail panel keyboard accessible.
|
|
11
|
+
|
|
6
12
|
## 2026-09-16
|
|
7
13
|
|
|
8
14
|
### Fixed
|
|
@@ -3,17 +3,76 @@
|
|
|
3
3
|
All notable user-facing changes to Clips are documented here. Open it any time
|
|
4
4
|
from the command menu (Cmd+K → "What's new") or from Settings.
|
|
5
5
|
|
|
6
|
+
## 2026-09-21
|
|
7
|
+
|
|
8
|
+
### Fixed
|
|
9
|
+
|
|
10
|
+
- Clips keeps the menu-bar popover open, restarts cleanly after updates, and waits for Google sign-in without a loading indicator.
|
|
11
|
+
|
|
12
|
+
## 2026-09-19
|
|
13
|
+
|
|
14
|
+
### Fixed
|
|
15
|
+
|
|
16
|
+
- Clips library loads on older browsers that lack modern object helpers
|
|
17
|
+
- After cutting a selected range from the editor toolbar, the selection follows the playhead again.
|
|
18
|
+
|
|
19
|
+
## 2026-09-18
|
|
20
|
+
|
|
21
|
+
### Improved
|
|
22
|
+
|
|
23
|
+
- Clips loads recording and organization lists faster
|
|
24
|
+
- Desktop recording alerts use matching warning and error highlights in the recorder and recovery list
|
|
25
|
+
- Desktop recording failures notify you and stay available in Clips with upload retry and local recovery options.
|
|
26
|
+
- Desktop recording recovery opens in a compact page, and completion cards close after Open or Copy succeeds
|
|
27
|
+
- Dictation now shows listening, finalizing, pasting, and success states so release-to-paste never feels stalled.
|
|
28
|
+
- Dictation keeps a quiet waveform pill, reveals controls on hover, and falls back to the clipboard when no text field is focused.
|
|
29
|
+
- Recording shortcuts now start, stop, cancel, and pause recordings with editable platform defaults.
|
|
30
|
+
- Use Fn as the default desktop dictation shortcut and keep its live waveform responsive during capture.
|
|
31
|
+
|
|
32
|
+
### Fixed
|
|
33
|
+
|
|
34
|
+
- Desktop recorder popover dismissal now works from outside clicks, the menu-bar icon, and Escape after native capture setup.
|
|
35
|
+
- Desktop recording setup recovers cleanly from cancellation and no longer stalls on a hidden microphone request.
|
|
36
|
+
- Fix Google sign-in popups when replying while signed out
|
|
37
|
+
- Clips extension sign-in now works for self-hosted apps mounted under a path.
|
|
38
|
+
- Hide recording recovery alerts from the sign-in screen
|
|
39
|
+
- Large recordings now support on-demand frame extraction.
|
|
40
|
+
- Refresh the desktop Dictate status surface and prevent shortcut-triggered native speech crashes.
|
|
41
|
+
- Reply controls stay clickable near the bottom of a recording's comments.
|
|
42
|
+
- Recordings with different frame sizes can now be stitched together.
|
|
43
|
+
|
|
6
44
|
## 2026-09-17
|
|
7
45
|
|
|
46
|
+
### Improved
|
|
47
|
+
|
|
48
|
+
- Hardened macOS Window recording handoff and simplified the web recorder desktop menu.
|
|
49
|
+
- Recordings keep capturing through brief connection losses and resume uploading when the open tab reconnects.
|
|
50
|
+
- Refresh the Chrome extension recorder UI to match the current Desktop shell.
|
|
51
|
+
- Screen and region recording controls are clearer and more reliable.
|
|
52
|
+
|
|
8
53
|
### Fixed
|
|
9
54
|
|
|
55
|
+
- The desktop popover now closes when you click away from it.
|
|
56
|
+
- Escape now cancels native window selection regardless of pointer position.
|
|
57
|
+
- Full screen, Region, and Window now appear together in the capture menu
|
|
58
|
+
- macOS Window recording now uses the native single-window picker without the WebKit sharing-controls stall
|
|
59
|
+
- Self-hosted Clips extensions now finish sign-in reliably.
|
|
60
|
+
- Window picker cancellation responds to Escape immediately
|
|
61
|
+
- Window picker launch no longer waits on Screen Memory or exposes a parked popover artifact
|
|
62
|
+
- Window recording starts without interrupting Screen Memory or leaving the recorder stuck in finalization
|
|
10
63
|
- Clips waits for authentication before showing the recording action
|
|
11
64
|
- The Clips sort control keeps its keyboard focus ring visible
|
|
12
65
|
|
|
66
|
+
### Changed
|
|
67
|
+
|
|
68
|
+
- Chrome extension recorder labels now match the Web and Desktop capture controls.
|
|
69
|
+
- Web recording labels full-screen capture as Full screen.
|
|
70
|
+
|
|
13
71
|
## 2026-09-16
|
|
14
72
|
|
|
15
73
|
### Improved
|
|
16
74
|
|
|
75
|
+
- Folder breadcrumbs now collapse to fit and show a back button to the parent folder
|
|
17
76
|
- Agents can continue through long recording transcripts without losing later sections.
|
|
18
77
|
|
|
19
78
|
### Fixed
|
|
@@ -3,6 +3,80 @@
|
|
|
3
3
|
All notable user-facing changes to Design are documented here. Open it any time
|
|
4
4
|
from the command menu (Cmd+K → "What's new") or from Settings.
|
|
5
5
|
|
|
6
|
+
## 2026-09-21
|
|
7
|
+
|
|
8
|
+
### Added
|
|
9
|
+
|
|
10
|
+
- Design includes more Google Fonts and supports custom font uploads
|
|
11
|
+
|
|
12
|
+
### Fixed
|
|
13
|
+
|
|
14
|
+
- Keep guided design choices readable in narrow panels
|
|
15
|
+
- Live visual-edit screens stay selectable and editable while running
|
|
16
|
+
- Live visual-edit screens support direct hover, selection, movement, resizing, and safe undo/redo.
|
|
17
|
+
- Show the generation loader as soon as design questions are submitted
|
|
18
|
+
|
|
19
|
+
## 2026-09-20
|
|
20
|
+
|
|
21
|
+
### Improved
|
|
22
|
+
|
|
23
|
+
- Public visual-edit links make localhost screens selectable and editable in the browser.
|
|
24
|
+
|
|
25
|
+
### Fixed
|
|
26
|
+
|
|
27
|
+
- Deleted Design screens can be recovered with Undo and History after reload
|
|
28
|
+
- Design cross-screen moves now retain undo history after persisted saves
|
|
29
|
+
- Design cross-screen moves preserve undo history after retryable and conflicting saves
|
|
30
|
+
- Design layer drags now preview Figma-style hierarchy changes and preserve cancel and undo behavior
|
|
31
|
+
- Grouped Design drops stay atomic through Apply, undo, and reload
|
|
32
|
+
- Show a precise insertion line when dragging into an occupied grid cell
|
|
33
|
+
|
|
34
|
+
## 2026-09-19
|
|
35
|
+
|
|
36
|
+
### Improved
|
|
37
|
+
|
|
38
|
+
- Drag and drop keeps placements and ordering across screens and freeform layouts
|
|
39
|
+
- URL-based React editing keeps focused changes visible through hot reload
|
|
40
|
+
|
|
41
|
+
### Fixed
|
|
42
|
+
|
|
43
|
+
- Component variant overrides persist when the main component changes
|
|
44
|
+
- Grouped grid layers preview their final cells and move as one undoable change
|
|
45
|
+
|
|
46
|
+
## 2026-09-18
|
|
47
|
+
|
|
48
|
+
### Added
|
|
49
|
+
|
|
50
|
+
- Auto Layout inspectors now show F, H, and A keyboard shortcuts
|
|
51
|
+
- Lines and arrows support independent endpoint styles in the inspector
|
|
52
|
+
|
|
53
|
+
### Improved
|
|
54
|
+
|
|
55
|
+
- Canvas panning avoids unnecessary redraws, and zoomed-out board previews use less rendering memory
|
|
56
|
+
- Design system lists load faster
|
|
57
|
+
- Private design links now offer access requests and sign-in or sign-up CTAs.
|
|
58
|
+
- Start visual editing with the /visual-edit skill install command
|
|
59
|
+
- Visual edit opens signed-out local previews in hosted Design and keeps live edits connected
|
|
60
|
+
|
|
61
|
+
### Fixed
|
|
62
|
+
|
|
63
|
+
- Auto-layout drops preserve Screen-root flow, responsive sizing, and grid track order.
|
|
64
|
+
- Cross-screen moves recover cleanly when a save conflict interrupts either file
|
|
65
|
+
- Design auto-layout drag and drop keeps wrapped rows and grid placement previews aligned
|
|
66
|
+
- Design auto-layout drag previews preserve wrapped rows and grid cells
|
|
67
|
+
- Design imports large Figma files with hundreds of frames
|
|
68
|
+
- Design keeps URL-backed auto-layout moves through source handoff and reload
|
|
69
|
+
- Editor panels stay visible while panning zoomed-out designs with background blur, and frame labels keep a consistent size when changing zoom.
|
|
70
|
+
- Fix Selection colors scanning and matching-layer targeting
|
|
71
|
+
- Imported screens stay separated from their responsive previews on the canvas
|
|
72
|
+
- Keep large imported canvases stable while fitting, panning, and selecting.
|
|
73
|
+
- Large Design canvases stay stable while panning and zooming
|
|
74
|
+
- Large imported frames keep panning and zooming stable without dropping the canvas UI.
|
|
75
|
+
- Responsive interaction mode now exits without leaving stale canvas state
|
|
76
|
+
- Signed-out visual editing can read and write local source from hosted Design
|
|
77
|
+
- Signed-out visual editing loads localhost screens reliably
|
|
78
|
+
- The breakpoint add control sits closer to the last frame
|
|
79
|
+
|
|
6
80
|
## 2026-09-17
|
|
7
81
|
|
|
8
82
|
### Added
|
|
@@ -11,16 +85,33 @@ from the command menu (Cmd+K → "What's new") or from Settings.
|
|
|
11
85
|
|
|
12
86
|
### Improved
|
|
13
87
|
|
|
88
|
+
- Design explains how to select inner layers with Cmd/Ctrl-click
|
|
89
|
+
- Design preserves reusable component identity and props across inline and React screens
|
|
90
|
+
- Signed-out users can open and visually edit a local Design canvas
|
|
91
|
+
- Visual edit opens local apps from a signed-out Design tab
|
|
14
92
|
- Design loads faster and large .fig imports can target selected frames
|
|
15
93
|
|
|
16
94
|
### Fixed
|
|
17
95
|
|
|
96
|
+
- Canvas zoom now paints large previews without rerendering the iframe on every gesture frame.
|
|
97
|
+
- Cross-screen dragging now preserves element placement between screens and the board.
|
|
98
|
+
- Cross-screen drags preserve their target and persist correctly
|
|
99
|
+
- Design arrange shortcuts reliably move layers forward, backward, to front, and to back
|
|
100
|
+
- Cross-screen dragging reliably moves elements and rolls back refused saves.
|
|
101
|
+
- Design drag-and-drop keeps auto-layout ordering and Cmd/Ctrl free placement
|
|
102
|
+
- Design drag-and-drop keeps duplicates, cross-screen moves, and undo history in sync.
|
|
103
|
+
- Design keeps component source and live edits consistent during indexing and updates
|
|
104
|
+
- Design preserves animated styles when copying or moving layers
|
|
105
|
+
- Open vector paths no longer show fills, and Interact dimensions stay clear of the exit control.
|
|
106
|
+
- Option-drag duplication keeps board objects and root screens addressable at every zoom.
|
|
107
|
+
- Screen deletion is immediate and undoable, with history that survives reloads.
|
|
18
108
|
- Large .fig files import directly in the browser without the old upload ceiling
|
|
19
109
|
|
|
20
110
|
## 2026-09-16
|
|
21
111
|
|
|
22
112
|
### Improved
|
|
23
113
|
|
|
114
|
+
- Design components retain linked identities when edited and duplicated
|
|
24
115
|
- Design files avoid cold-start scans and open faster after idle periods
|
|
25
116
|
- Design marquee selections stay responsive on large screens
|
|
26
117
|
- Design editor agent chat uses shared conversation tabs and stays usable at narrow widths
|
|
@@ -28,6 +119,7 @@ from the command menu (Cmd+K → "What's new") or from Settings.
|
|
|
28
119
|
|
|
29
120
|
### Fixed
|
|
30
121
|
|
|
122
|
+
- Fix Design nested layer dragging and screen duplication parity
|
|
31
123
|
- Design comments keep keyboard mentions and selected recipients through drafts and replies
|
|
32
124
|
- Design Fill editing uses the visible background paint on text-bearing controls
|
|
33
125
|
- Design inspector controls match compact Figma spacing
|
|
@@ -55,6 +55,7 @@ import {
|
|
|
55
55
|
TooltipProvider,
|
|
56
56
|
TooltipTrigger,
|
|
57
57
|
} from "@/components/ui/tooltip";
|
|
58
|
+
import type { UploadedFont } from "@/lib/font-upload";
|
|
58
59
|
import { cn } from "@/lib/utils";
|
|
59
60
|
import type { EditorMode } from "@/pages/design-editor/types";
|
|
60
61
|
|
|
@@ -386,6 +387,7 @@ interface EditPanelProps {
|
|
|
386
387
|
onRequestTweaks?: (anchor: HTMLElement) => void;
|
|
387
388
|
onStyleChange: StyleChangeHandler;
|
|
388
389
|
onStylesChange?: StylesChangeHandler;
|
|
390
|
+
onFontUploaded?: (font: UploadedFont) => void | Promise<void>;
|
|
389
391
|
onExport?: (settings: ExportSettingsValue[]) => void;
|
|
390
392
|
/** Rasterizes the current selection for the export preview. Must go through
|
|
391
393
|
* the same renderer as `onExport`, or the preview lies about the output. */
|
|
@@ -1863,7 +1865,11 @@ function PageProperties({
|
|
|
1863
1865
|
const baseFontFamilyOptions = sortFontFamilyOptions(
|
|
1864
1866
|
FONT_FAMILY_OPTIONS.map((option) => ({
|
|
1865
1867
|
value: option.value,
|
|
1866
|
-
label:
|
|
1868
|
+
label:
|
|
1869
|
+
option.label ??
|
|
1870
|
+
(option.key
|
|
1871
|
+
? t(`editPanel.fontFamilies.${option.key}`)
|
|
1872
|
+
: displayFontFamilyName(option.value)),
|
|
1867
1873
|
})),
|
|
1868
1874
|
);
|
|
1869
1875
|
const fontFamily = resolveFontFamilySelectValue(styles.fontFamily);
|
|
@@ -2478,6 +2484,7 @@ export const EditPanel = memo(function EditPanel({
|
|
|
2478
2484
|
designId,
|
|
2479
2485
|
onComponentPropApplied,
|
|
2480
2486
|
onShaderSourceApplied,
|
|
2487
|
+
onFontUploaded,
|
|
2481
2488
|
reviewPanelProps,
|
|
2482
2489
|
reviewCommentsPanelProps,
|
|
2483
2490
|
reviewCommentsCount = 0,
|
|
@@ -3324,6 +3331,8 @@ export const EditPanel = memo(function EditPanel({
|
|
|
3324
3331
|
onStylesChange={
|
|
3325
3332
|
onStylesChangeProp ? onStylesChange : undefined
|
|
3326
3333
|
}
|
|
3334
|
+
designId={designId}
|
|
3335
|
+
onFontUploaded={onFontUploaded}
|
|
3327
3336
|
/>
|
|
3328
3337
|
) : null}
|
|
3329
3338
|
{selectionIsGroup ? (
|
|
@@ -18,9 +18,10 @@ import {
|
|
|
18
18
|
IconSquare,
|
|
19
19
|
IconStrikethrough,
|
|
20
20
|
IconTextSize,
|
|
21
|
+
IconUpload,
|
|
21
22
|
IconUnderline,
|
|
22
23
|
} from "@tabler/icons-react";
|
|
23
|
-
import { useId, useState } from "react";
|
|
24
|
+
import { useId, useRef, useState } from "react";
|
|
24
25
|
import { toast } from "sonner";
|
|
25
26
|
|
|
26
27
|
import { formatShortcutLabel } from "@/components/design/keyboard-shortcuts";
|
|
@@ -45,6 +46,7 @@ import {
|
|
|
45
46
|
TooltipTrigger,
|
|
46
47
|
} from "@/components/ui/tooltip";
|
|
47
48
|
import { useApplePlatform } from "@/hooks/use-shortcut-label";
|
|
49
|
+
import { uploadFont, type UploadedFont } from "@/lib/font-upload";
|
|
48
50
|
import { cn } from "@/lib/utils";
|
|
49
51
|
|
|
50
52
|
import { ScrubInput } from "../inspector";
|
|
@@ -89,11 +91,6 @@ import {
|
|
|
89
91
|
type TextResizeMode,
|
|
90
92
|
} from "./typography-helpers";
|
|
91
93
|
|
|
92
|
-
const LATO_FONT_FAMILY_OPTION = {
|
|
93
|
-
value: "'Lato', sans-serif",
|
|
94
|
-
label: "Lato",
|
|
95
|
-
} as const;
|
|
96
|
-
|
|
97
94
|
function TextResizeControls({
|
|
98
95
|
resizeMode,
|
|
99
96
|
onResizeModeChange,
|
|
@@ -386,19 +383,28 @@ export function TypographyProperties({
|
|
|
386
383
|
element,
|
|
387
384
|
onStyleChange,
|
|
388
385
|
onStylesChange,
|
|
386
|
+
designId,
|
|
387
|
+
onFontUploaded,
|
|
389
388
|
}: {
|
|
390
389
|
element: ElementInfo;
|
|
391
390
|
onStyleChange: StyleChangeHandler;
|
|
392
391
|
onStylesChange?: StylesChangeHandler;
|
|
392
|
+
designId?: string;
|
|
393
|
+
onFontUploaded?: (font: UploadedFont) => void | Promise<void>;
|
|
393
394
|
}) {
|
|
394
395
|
const t = useT();
|
|
396
|
+
const fontUploadInputRef = useRef<HTMLInputElement>(null);
|
|
397
|
+
const [fontUploading, setFontUploading] = useState(false);
|
|
395
398
|
const styles = element.computedStyles;
|
|
396
399
|
const baseFontFamilyOptions = sortFontFamilyOptions([
|
|
397
400
|
...FONT_FAMILY_OPTIONS.map((option) => ({
|
|
398
401
|
value: option.value,
|
|
399
|
-
label:
|
|
402
|
+
label:
|
|
403
|
+
option.label ??
|
|
404
|
+
(option.key
|
|
405
|
+
? t(`editPanel.fontFamilies.${option.key}`)
|
|
406
|
+
: displayFontFamilyName(option.value)),
|
|
400
407
|
})),
|
|
401
|
-
LATO_FONT_FAMILY_OPTION,
|
|
402
408
|
]);
|
|
403
409
|
// Mixed-selection guards: a multi-selection with differing values injects
|
|
404
410
|
// the MIXED_VALUE sentinel string into these computedStyles fields (see
|
|
@@ -494,6 +500,22 @@ export function TypographyProperties({
|
|
|
494
500
|
...baseFontFamilyOptions,
|
|
495
501
|
],
|
|
496
502
|
);
|
|
503
|
+
const handleFontUpload = async (file: File) => {
|
|
504
|
+
if (!designId || !onFontUploaded) return;
|
|
505
|
+
setFontUploading(true);
|
|
506
|
+
try {
|
|
507
|
+
const uploaded = await uploadFont(file, designId);
|
|
508
|
+
await onFontUploaded(uploaded);
|
|
509
|
+
} catch (error) {
|
|
510
|
+
toast.error(
|
|
511
|
+
error instanceof Error
|
|
512
|
+
? error.message
|
|
513
|
+
: t("promptDialog.failedToUploadFile"),
|
|
514
|
+
);
|
|
515
|
+
} finally {
|
|
516
|
+
setFontUploading(false);
|
|
517
|
+
}
|
|
518
|
+
};
|
|
497
519
|
const baseFontWeightOptions = FONT_WEIGHT_OPTIONS.map((option) => ({
|
|
498
520
|
value: option.value,
|
|
499
521
|
label: t(`editPanel.fontWeights.${option.key}`),
|
|
@@ -625,20 +647,58 @@ export function TypographyProperties({
|
|
|
625
647
|
dropdown instead). */}
|
|
626
648
|
<InspectorGrid layout="field-action">
|
|
627
649
|
<InspectorGridCell span={28} className="h-6 overflow-hidden">
|
|
628
|
-
<
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
650
|
+
<div className="flex min-w-0 items-center gap-1">
|
|
651
|
+
<div className="min-w-0 flex-1">
|
|
652
|
+
<VisualFontFamilyPicker
|
|
653
|
+
label={t("editPanel.labels.font")}
|
|
654
|
+
value={fontFamily}
|
|
655
|
+
options={fontFamilyOptions}
|
|
656
|
+
mixed={fontFamilyIsMixed}
|
|
657
|
+
mixedLabel={MIXED_VALUE}
|
|
658
|
+
searchable
|
|
659
|
+
searchPlaceholder={t("root.commandSearch")}
|
|
660
|
+
contentProps={{
|
|
661
|
+
"data-design-chrome-region": "right-panel",
|
|
662
|
+
}}
|
|
663
|
+
className="h-6 w-full rounded-md border-[var(--design-editor-control-border)] bg-[var(--design-editor-control-bg)] px-1.5 !text-[11px] shadow-none focus:ring-1 focus:ring-[var(--design-editor-accent-color)]"
|
|
664
|
+
onChange={(value) => onStyleChange("fontFamily", value)}
|
|
665
|
+
/>
|
|
666
|
+
</div>
|
|
667
|
+
{designId && onFontUploaded ? (
|
|
668
|
+
<>
|
|
669
|
+
<input
|
|
670
|
+
ref={fontUploadInputRef}
|
|
671
|
+
type="file"
|
|
672
|
+
accept=".woff2,.woff,.ttf,.otf,font/woff2,font/woff,font/ttf,font/otf"
|
|
673
|
+
className="sr-only"
|
|
674
|
+
aria-label={t("promptDialog.uploadFile")}
|
|
675
|
+
onChange={(event) => {
|
|
676
|
+
const file = event.target.files?.[0];
|
|
677
|
+
event.target.value = "";
|
|
678
|
+
if (file) void handleFontUpload(file);
|
|
679
|
+
}}
|
|
680
|
+
/>
|
|
681
|
+
<Tooltip>
|
|
682
|
+
<TooltipTrigger asChild>
|
|
683
|
+
<Button
|
|
684
|
+
type="button"
|
|
685
|
+
variant="ghost"
|
|
686
|
+
size="icon"
|
|
687
|
+
disabled={fontUploading}
|
|
688
|
+
aria-label={t("promptDialog.uploadFile")}
|
|
689
|
+
className="size-6 shrink-0"
|
|
690
|
+
onClick={() => fontUploadInputRef.current?.click()}
|
|
691
|
+
>
|
|
692
|
+
<IconUpload className="size-3.5" />
|
|
693
|
+
</Button>
|
|
694
|
+
</TooltipTrigger>
|
|
695
|
+
<TooltipContent>
|
|
696
|
+
{t("promptDialog.uploadFile")}
|
|
697
|
+
</TooltipContent>
|
|
698
|
+
</Tooltip>
|
|
699
|
+
</>
|
|
700
|
+
) : null}
|
|
701
|
+
</div>
|
|
642
702
|
</InspectorGridCell>
|
|
643
703
|
</InspectorGrid>
|
|
644
704
|
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { appBasePath } from "@agent-native/core/client/api-path";
|
|
2
|
+
|
|
3
|
+
export interface UploadedFont {
|
|
4
|
+
family: string;
|
|
5
|
+
url: string;
|
|
6
|
+
weight: string;
|
|
7
|
+
style: "normal" | "italic";
|
|
8
|
+
format: "woff2" | "woff" | "truetype" | "opentype";
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const FONT_WEIGHT_SUFFIX =
|
|
12
|
+
/\s+(?:thin|extra ?light|ultra ?light|light|book|regular|normal|medium|semi ?bold|demi ?bold|bold|extra ?bold|ultra ?bold|black|heavy)$/i;
|
|
13
|
+
|
|
14
|
+
export function inferFontUploadMetadata(filename: string): {
|
|
15
|
+
family: string;
|
|
16
|
+
weight: string;
|
|
17
|
+
style: "normal" | "italic";
|
|
18
|
+
} {
|
|
19
|
+
const basename = filename.replace(/\.[^.]+$/, "");
|
|
20
|
+
const normalized = basename
|
|
21
|
+
.replace(/[_-]+/g, " ")
|
|
22
|
+
.replace(/\s+/g, " ")
|
|
23
|
+
.trim();
|
|
24
|
+
const style = /(?:italic|oblique)/i.test(normalized) ? "italic" : "normal";
|
|
25
|
+
const familyAndWeight = normalized
|
|
26
|
+
.replace(/\s+(?:italic|oblique)$/i, "")
|
|
27
|
+
.trim();
|
|
28
|
+
const weightMatch = familyAndWeight.match(
|
|
29
|
+
/\s+(thin|extra ?light|ultra ?light|light|book|regular|normal|medium|semi ?bold|demi ?bold|bold|extra ?bold|ultra ?bold|black|heavy)$/i,
|
|
30
|
+
);
|
|
31
|
+
const weight =
|
|
32
|
+
{
|
|
33
|
+
thin: "100",
|
|
34
|
+
extralight: "200",
|
|
35
|
+
ultralight: "200",
|
|
36
|
+
light: "300",
|
|
37
|
+
book: "400",
|
|
38
|
+
regular: "400",
|
|
39
|
+
normal: "400",
|
|
40
|
+
medium: "500",
|
|
41
|
+
semibold: "600",
|
|
42
|
+
demibold: "600",
|
|
43
|
+
bold: "700",
|
|
44
|
+
extrabold: "800",
|
|
45
|
+
ultrabold: "800",
|
|
46
|
+
black: "900",
|
|
47
|
+
heavy: "900",
|
|
48
|
+
}[weightMatch?.[1]?.replace(/\s+/g, "").toLowerCase() ?? ""] ?? "400";
|
|
49
|
+
const family = familyAndWeight
|
|
50
|
+
.replace(FONT_WEIGHT_SUFFIX, "")
|
|
51
|
+
.replace(/\s+/g, " ")
|
|
52
|
+
.trim();
|
|
53
|
+
return { family: family || "Uploaded Font", weight, style };
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export async function uploadFont(
|
|
57
|
+
file: File,
|
|
58
|
+
designId: string,
|
|
59
|
+
): Promise<UploadedFont> {
|
|
60
|
+
const metadata = inferFontUploadMetadata(file.name);
|
|
61
|
+
const form = new FormData();
|
|
62
|
+
form.append("designId", designId);
|
|
63
|
+
form.append("family", metadata.family);
|
|
64
|
+
form.append("weight", metadata.weight);
|
|
65
|
+
form.append("style", metadata.style);
|
|
66
|
+
form.append("file", file, file.name);
|
|
67
|
+
|
|
68
|
+
const response = await fetch(`${appBasePath()}/api/fonts`, {
|
|
69
|
+
method: "POST",
|
|
70
|
+
credentials: "include",
|
|
71
|
+
body: form,
|
|
72
|
+
});
|
|
73
|
+
let payload: (UploadedFont & { error?: never }) | { error?: string } | null;
|
|
74
|
+
try {
|
|
75
|
+
payload = (await response.json()) as
|
|
76
|
+
| (UploadedFont & { error?: never })
|
|
77
|
+
| { error?: string }
|
|
78
|
+
| null;
|
|
79
|
+
} catch {
|
|
80
|
+
throw new Error("Font upload failed");
|
|
81
|
+
}
|
|
82
|
+
if (!response.ok || !payload || !("url" in payload)) {
|
|
83
|
+
throw new Error(
|
|
84
|
+
payload && "error" in payload && payload.error
|
|
85
|
+
? payload.error
|
|
86
|
+
: "Font upload failed",
|
|
87
|
+
);
|
|
88
|
+
}
|
|
89
|
+
return payload;
|
|
90
|
+
}
|
|
@@ -21,8 +21,11 @@ export {
|
|
|
21
21
|
replaceDataScreenReferences,
|
|
22
22
|
} from "@shared/screen-rename";
|
|
23
23
|
|
|
24
|
+
import { GOOGLE_FONT_QUERIES } from "@agent-native/toolkit/design-tweaks";
|
|
25
|
+
|
|
24
26
|
import type { LayersPanelNode } from "@/components/design/LayersPanel";
|
|
25
27
|
import type { ElementInfo } from "@/components/design/types";
|
|
28
|
+
import type { UploadedFont } from "@/lib/font-upload";
|
|
26
29
|
|
|
27
30
|
import { queryUniqueSelector } from "./dom-utils";
|
|
28
31
|
|
|
@@ -1272,16 +1275,9 @@ export function codeLayerPatchMessage(
|
|
|
1272
1275
|
: message;
|
|
1273
1276
|
}
|
|
1274
1277
|
|
|
1275
|
-
// Known Google Font families offered by the inspector's font-family picker.
|
|
1276
1278
|
// Lato's weight 500 comes from the pinned OFL face below because the CSS2 API
|
|
1277
1279
|
// currently serves only its 400 and 700 files.
|
|
1278
|
-
export const KNOWN_GOOGLE_FONTS
|
|
1279
|
-
Inter: "Inter:wght@400;500;600;700",
|
|
1280
|
-
Poppins: "Poppins:wght@400;500;600;700",
|
|
1281
|
-
"Playfair Display": "Playfair+Display:wght@400;500;600;700",
|
|
1282
|
-
"JetBrains Mono": "JetBrains+Mono:wght@400;500;600;700",
|
|
1283
|
-
Lato: "Lato:wght@400;700",
|
|
1284
|
-
};
|
|
1280
|
+
export const KNOWN_GOOGLE_FONTS = GOOGLE_FONT_QUERIES;
|
|
1285
1281
|
|
|
1286
1282
|
const LATO_MEDIUM_FACE_URL =
|
|
1287
1283
|
"https://raw.githubusercontent.com/google/fonts/809e4d8b8d7e9364a914909bb777679606c178b8/ofl/lato/Lato-Medium.ttf";
|
|
@@ -1346,7 +1342,18 @@ export function ensureGoogleFontLinkInHtml(
|
|
|
1346
1342
|
);
|
|
1347
1343
|
const alreadyLoaded = existingLinks.some((link) => {
|
|
1348
1344
|
const href = link.getAttribute("href") ?? "";
|
|
1349
|
-
|
|
1345
|
+
let normalizedHref = href;
|
|
1346
|
+
try {
|
|
1347
|
+
normalizedHref = decodeURIComponent(href);
|
|
1348
|
+
} catch {
|
|
1349
|
+
// coercion-ok: malformed legacy URL stays raw for conservative matching.
|
|
1350
|
+
// Keep the raw URL when a legacy link contains malformed escaping.
|
|
1351
|
+
}
|
|
1352
|
+
normalizedHref = normalizedHref.replace(/\+/g, " ").toLowerCase();
|
|
1353
|
+
return (
|
|
1354
|
+
href.includes(`family=${fontQuery}`) ||
|
|
1355
|
+
normalizedHref.includes(`family=${family.toLowerCase()}:`)
|
|
1356
|
+
);
|
|
1350
1357
|
});
|
|
1351
1358
|
let changed = ensurePinnedFontFace(doc, family);
|
|
1352
1359
|
if (!alreadyLoaded) {
|
|
@@ -1388,6 +1395,51 @@ export function ensureGoogleFontLinkInHtml(
|
|
|
1388
1395
|
}
|
|
1389
1396
|
}
|
|
1390
1397
|
|
|
1398
|
+
function escapeCssString(value: string): string {
|
|
1399
|
+
return value
|
|
1400
|
+
.replace(/\\/g, "\\\\")
|
|
1401
|
+
.replace(/"/g, '\\"')
|
|
1402
|
+
.replace(/[\r\n]/g, "");
|
|
1403
|
+
}
|
|
1404
|
+
|
|
1405
|
+
/** Persist an uploaded face next to the screen HTML that uses it. */
|
|
1406
|
+
export function ensureUploadedFontFaceInHtml(
|
|
1407
|
+
content: string,
|
|
1408
|
+
font: UploadedFont,
|
|
1409
|
+
): string {
|
|
1410
|
+
if (typeof window === "undefined") return content;
|
|
1411
|
+
if (!font.url || !font.family) return content;
|
|
1412
|
+
const doc = new DOMParser().parseFromString(content, "text/html");
|
|
1413
|
+
const head = doc.head;
|
|
1414
|
+
if (!head) return content;
|
|
1415
|
+
const alreadyLoaded = Array.from(
|
|
1416
|
+
head.querySelectorAll('style[data-agent-native-uploaded-font="true"]'),
|
|
1417
|
+
).some(
|
|
1418
|
+
(style) =>
|
|
1419
|
+
style.getAttribute("data-font-family") === font.family &&
|
|
1420
|
+
style.getAttribute("data-font-url") === font.url &&
|
|
1421
|
+
style.getAttribute("data-font-weight") === font.weight &&
|
|
1422
|
+
style.getAttribute("data-font-style") === font.style,
|
|
1423
|
+
);
|
|
1424
|
+
if (alreadyLoaded) return content;
|
|
1425
|
+
|
|
1426
|
+
const style = doc.createElement("style");
|
|
1427
|
+
style.setAttribute("data-agent-native-uploaded-font", "true");
|
|
1428
|
+
style.setAttribute("data-font-family", font.family);
|
|
1429
|
+
style.setAttribute("data-font-url", font.url);
|
|
1430
|
+
style.setAttribute("data-font-weight", font.weight);
|
|
1431
|
+
style.setAttribute("data-font-style", font.style);
|
|
1432
|
+
style.textContent = `@font-face {
|
|
1433
|
+
font-family: "${escapeCssString(font.family)}";
|
|
1434
|
+
font-style: ${font.style};
|
|
1435
|
+
font-weight: ${font.weight};
|
|
1436
|
+
font-display: swap;
|
|
1437
|
+
src: url("${escapeCssString(font.url)}") format("${font.format}");
|
|
1438
|
+
}`;
|
|
1439
|
+
head.appendChild(style);
|
|
1440
|
+
return `<!DOCTYPE html>\n${doc.documentElement.outerHTML}`;
|
|
1441
|
+
}
|
|
1442
|
+
|
|
1391
1443
|
export function refreshElementInfoFromContent(
|
|
1392
1444
|
content: string,
|
|
1393
1445
|
info: ElementInfo | null,
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
|
|
3
|
+
import { uploadFile } from "@agent-native/core/file-upload";
|
|
4
|
+
import { getSession, runWithRequestContext } from "@agent-native/core/server";
|
|
5
|
+
import { assertAccess } from "@agent-native/core/sharing";
|
|
6
|
+
import {
|
|
7
|
+
defineEventHandler,
|
|
8
|
+
getRequestHeader,
|
|
9
|
+
readMultipartFormData,
|
|
10
|
+
setResponseStatus,
|
|
11
|
+
} from "h3";
|
|
12
|
+
|
|
13
|
+
import {
|
|
14
|
+
MAX_UPLOAD_BYTES,
|
|
15
|
+
MAX_UPLOAD_MB,
|
|
16
|
+
TOTAL_BODY_LIMIT,
|
|
17
|
+
} from "../lib/request-body-limits.js";
|
|
18
|
+
|
|
19
|
+
const FONT_FORMATS = {
|
|
20
|
+
".otf": { format: "opentype", mimeType: "font/otf" },
|
|
21
|
+
".ttf": { format: "truetype", mimeType: "font/ttf" },
|
|
22
|
+
".woff": { format: "woff", mimeType: "font/woff" },
|
|
23
|
+
".woff2": { format: "woff2", mimeType: "font/woff2" },
|
|
24
|
+
} as const;
|
|
25
|
+
|
|
26
|
+
function ascii(data: Uint8Array, start: number, end: number): string {
|
|
27
|
+
return Buffer.from(data.subarray(start, end)).toString("ascii");
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function hasFontSignature(ext: keyof typeof FONT_FORMATS, data: Uint8Array) {
|
|
31
|
+
if (ext === ".woff" || ext === ".woff2") {
|
|
32
|
+
return ascii(data, 0, 4) === (ext === ".woff" ? "wOFF" : "wOF2");
|
|
33
|
+
}
|
|
34
|
+
if (ext === ".otf") return ascii(data, 0, 4) === "OTTO";
|
|
35
|
+
return data[0] === 0 && data[1] === 1 && data[2] === 0 && data[3] === 0;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function fieldText(
|
|
39
|
+
parts: Awaited<ReturnType<typeof readMultipartFormData>>,
|
|
40
|
+
name: string,
|
|
41
|
+
): string | undefined {
|
|
42
|
+
const part = parts?.find((candidate) => candidate.name === name);
|
|
43
|
+
return part?.data
|
|
44
|
+
? Buffer.from(part.data).toString("utf8").trim()
|
|
45
|
+
: undefined;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function safeFamilyName(value: string | undefined, filename: string): string {
|
|
49
|
+
const fallback = path.basename(filename, path.extname(filename));
|
|
50
|
+
const family = (value || fallback)
|
|
51
|
+
.replace(/["'\\{};,\u0000-\u001f\u007f]/g, "")
|
|
52
|
+
.replace(/\s+/g, " ")
|
|
53
|
+
.trim()
|
|
54
|
+
.slice(0, 100);
|
|
55
|
+
if (!family) throw new Error("Font family is required");
|
|
56
|
+
return family;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function safeWeight(value: string | undefined): string {
|
|
60
|
+
const weight = value?.trim() || "400";
|
|
61
|
+
if (!/^\d{3}(?:\s+\d{3})?$/.test(weight)) {
|
|
62
|
+
throw new Error("Font weight is invalid");
|
|
63
|
+
}
|
|
64
|
+
const values = weight.split(/\s+/).map(Number);
|
|
65
|
+
if (values.some((entry) => entry < 1 || entry > 1000)) {
|
|
66
|
+
throw new Error("Font weight is invalid");
|
|
67
|
+
}
|
|
68
|
+
return weight;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function safeStyle(value: string | undefined): "normal" | "italic" {
|
|
72
|
+
if (value === "italic") return "italic";
|
|
73
|
+
if (!value || value === "normal") return "normal";
|
|
74
|
+
throw new Error("Font style is invalid");
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export const uploadFont = defineEventHandler(async (event) => {
|
|
78
|
+
let session;
|
|
79
|
+
try {
|
|
80
|
+
session = await getSession(event);
|
|
81
|
+
} catch (error) {
|
|
82
|
+
console.error("[design-font-upload] session lookup failed", error);
|
|
83
|
+
setResponseStatus(event, 503);
|
|
84
|
+
return { error: "Authentication service unavailable" };
|
|
85
|
+
}
|
|
86
|
+
if (!session?.email) {
|
|
87
|
+
setResponseStatus(event, 401);
|
|
88
|
+
return { error: "Unauthorized" };
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
const contentLength = Number(getRequestHeader(event, "content-length") ?? 0);
|
|
92
|
+
if (contentLength > TOTAL_BODY_LIMIT) {
|
|
93
|
+
setResponseStatus(event, 413);
|
|
94
|
+
return { error: "Request body too large" };
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
return runWithRequestContext(
|
|
98
|
+
{ userEmail: session.email, orgId: session.orgId },
|
|
99
|
+
async () => {
|
|
100
|
+
try {
|
|
101
|
+
const parts = await readMultipartFormData(event);
|
|
102
|
+
const designId = fieldText(parts, "designId");
|
|
103
|
+
if (!designId) {
|
|
104
|
+
setResponseStatus(event, 400);
|
|
105
|
+
return { error: "Missing designId" };
|
|
106
|
+
}
|
|
107
|
+
await assertAccess("design", designId, "editor");
|
|
108
|
+
|
|
109
|
+
const filePart = parts?.find(
|
|
110
|
+
(part) => part.name === "file" && part.data,
|
|
111
|
+
);
|
|
112
|
+
if (!filePart?.data) {
|
|
113
|
+
setResponseStatus(event, 400);
|
|
114
|
+
return { error: "No font uploaded" };
|
|
115
|
+
}
|
|
116
|
+
if (filePart.data.length > MAX_UPLOAD_BYTES) {
|
|
117
|
+
setResponseStatus(event, 413);
|
|
118
|
+
return { error: `Font is too large (max ${MAX_UPLOAD_MB} MB)` };
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
const originalName = filePart.filename || "font.woff2";
|
|
122
|
+
const ext = path
|
|
123
|
+
.extname(originalName)
|
|
124
|
+
.toLowerCase() as keyof typeof FONT_FORMATS;
|
|
125
|
+
const format = FONT_FORMATS[ext];
|
|
126
|
+
if (!format || !hasFontSignature(ext, filePart.data)) {
|
|
127
|
+
setResponseStatus(event, 400);
|
|
128
|
+
return { error: "Choose a valid .woff2, .woff, .ttf, or .otf font" };
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
const family = safeFamilyName(fieldText(parts, "family"), originalName);
|
|
132
|
+
const weight = safeWeight(fieldText(parts, "weight"));
|
|
133
|
+
const style = safeStyle(fieldText(parts, "style"));
|
|
134
|
+
let uploaded;
|
|
135
|
+
try {
|
|
136
|
+
uploaded = await uploadFile({
|
|
137
|
+
data: Buffer.from(filePart.data),
|
|
138
|
+
filename: originalName,
|
|
139
|
+
mimeType: format.mimeType,
|
|
140
|
+
ownerEmail: session.email,
|
|
141
|
+
recordAsset: false,
|
|
142
|
+
});
|
|
143
|
+
} catch (error) {
|
|
144
|
+
console.error("[design-font-upload] upload failed", error);
|
|
145
|
+
setResponseStatus(event, 502);
|
|
146
|
+
return { error: "Font upload failed" };
|
|
147
|
+
}
|
|
148
|
+
if (!uploaded) {
|
|
149
|
+
setResponseStatus(event, 503);
|
|
150
|
+
return { error: "File uploads are not configured" };
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
return {
|
|
154
|
+
family,
|
|
155
|
+
url: uploaded.url,
|
|
156
|
+
weight,
|
|
157
|
+
style,
|
|
158
|
+
format: format.format,
|
|
159
|
+
};
|
|
160
|
+
} catch (error) {
|
|
161
|
+
const message =
|
|
162
|
+
error instanceof Error ? error.message : "Font upload failed";
|
|
163
|
+
setResponseStatus(
|
|
164
|
+
event,
|
|
165
|
+
/access|permission|not allowed/i.test(message) ? 403 : 400,
|
|
166
|
+
);
|
|
167
|
+
return { error: message };
|
|
168
|
+
}
|
|
169
|
+
},
|
|
170
|
+
);
|
|
171
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { uploadFont as default } from "../../handlers/fonts";
|
|
@@ -3,6 +3,19 @@
|
|
|
3
3
|
All notable user-facing changes to Agent-Native Mail are documented here. Open it any
|
|
4
4
|
time from the command menu (Cmd+K → "What's new") or from Settings.
|
|
5
5
|
|
|
6
|
+
## 2026-09-19
|
|
7
|
+
|
|
8
|
+
### Fixed
|
|
9
|
+
|
|
10
|
+
- New Mail drafts stay pinned to the bottom of the viewport.
|
|
11
|
+
- OpenAI automation settings load without a missing engine package error
|
|
12
|
+
|
|
13
|
+
## 2026-09-18
|
|
14
|
+
|
|
15
|
+
### Added
|
|
16
|
+
|
|
17
|
+
- Mail rules can tag messages or move spam out of Inbox with Jev-powered previews and feedback
|
|
18
|
+
|
|
6
19
|
## 2026-09-17
|
|
7
20
|
|
|
8
21
|
### Fixed
|
|
@@ -3,6 +3,44 @@
|
|
|
3
3
|
All notable user-facing changes to Agent-Native Slides are documented here. Open it any
|
|
4
4
|
time from the command menu (Cmd+K → "What's new").
|
|
5
5
|
|
|
6
|
+
## 2026-09-21
|
|
7
|
+
|
|
8
|
+
### Fixed
|
|
9
|
+
|
|
10
|
+
- Keep slide additions aligned with the deck's existing design system
|
|
11
|
+
|
|
12
|
+
## 2026-09-20
|
|
13
|
+
|
|
14
|
+
### Fixed
|
|
15
|
+
|
|
16
|
+
- Continue is available only after selecting a reference or design system.
|
|
17
|
+
|
|
18
|
+
## 2026-09-19
|
|
19
|
+
|
|
20
|
+
### Fixed
|
|
21
|
+
|
|
22
|
+
- HTML exports have clickable slide and fullscreen controls
|
|
23
|
+
- Creating another presentation in chat keeps the existing deck intact.
|
|
24
|
+
|
|
25
|
+
## 2026-09-18
|
|
26
|
+
|
|
27
|
+
### Improved
|
|
28
|
+
|
|
29
|
+
- New decks now keep the selected design system as the default
|
|
30
|
+
- Share Presentation now keeps its heading inset and shows the relevant link clearly
|
|
31
|
+
|
|
32
|
+
### Fixed
|
|
33
|
+
|
|
34
|
+
- Inline slide editing now preserves text changes through teardown and reload
|
|
35
|
+
- Slides keeps chat controls, queued prompts, sharing, and design-system status in sync.
|
|
36
|
+
- Slides now hydrates configured design system defaults when references load after opening.
|
|
37
|
+
|
|
38
|
+
## 2026-09-17
|
|
39
|
+
|
|
40
|
+
### Fixed
|
|
41
|
+
|
|
42
|
+
- Text editing keeps its focus, toolbar controls, and selection outline consistent
|
|
43
|
+
|
|
6
44
|
## 2026-09-16
|
|
7
45
|
|
|
8
46
|
### Fixed
|
|
@@ -214,7 +214,11 @@ export function SlideContextToolbar({
|
|
|
214
214
|
const baseFontFamilyOptions = sortFontFamilyOptions(
|
|
215
215
|
FONT_FAMILY_OPTIONS.map((option) => ({
|
|
216
216
|
value: option.value,
|
|
217
|
-
label:
|
|
217
|
+
label:
|
|
218
|
+
option.label ??
|
|
219
|
+
(option.key
|
|
220
|
+
? t(`styleInspector.fontFamilies.${option.key}`)
|
|
221
|
+
: displayFontFamilyName(option.value)),
|
|
218
222
|
})),
|
|
219
223
|
);
|
|
220
224
|
const inlineEditSurfaceProps = {
|
|
@@ -186,8 +186,8 @@ export declare function createAutomationCallbackHandler(runtime: AutomationRunti
|
|
|
186
186
|
duplicate?: undefined;
|
|
187
187
|
eventId?: undefined;
|
|
188
188
|
} | {
|
|
189
|
+
error?: undefined;
|
|
189
190
|
accepted: boolean;
|
|
190
191
|
duplicate: boolean;
|
|
191
192
|
eventId: string;
|
|
192
|
-
error?: undefined;
|
|
193
193
|
}>;
|
|
@@ -22,7 +22,7 @@ import { DEFAULT_MEMBER_SEARCH_DEBOUNCE_MS, useShareOrgMemberSearch, } from "../
|
|
|
22
22
|
import { useActionMutation, useActionQuery } from "../use-action.js";
|
|
23
23
|
import { cn } from "../utils.js";
|
|
24
24
|
import { useOrg, useOrgMembers, useOrgInvitations, useCreateOrg, useUpdateOrg, useBulkInviteMembers, useChangeMemberRole, useAcceptInvitation, useRemoveMember, useDeleteOrg, useSwitchOrg, useSetOrgDomain, useSetWorkspaceAppDefaultVisibility, useWorkspaceAppAccess, useSetWorkspaceAppAccess, useSetOrgWorkspaceUrl, useSetOrgAuthProvider, useRevealA2ASecret, useSetA2ASecret, useSyncA2ASecret, useJoinByDomain, useAppRoles, useSetAppMemberRoles, useOrgSsoProviders, useCreateOrgSsoProvider, useVerifyOrgSsoProvider, useDeleteOrgSsoProvider, useOrgScim, useCreateOrgScimConnection, useDeleteOrgScimConnection, ORG_MEMBER_PAGE_SIZE, } from "./hooks.js";
|
|
25
|
-
const Button = forwardRef(({ className, ...props }, ref) => (_jsx(ToolkitButton, { ref: ref, variant: "ghost", className: cn("h-auto p-0 hover:bg-transparent
|
|
25
|
+
const Button = forwardRef(({ className, ...props }, ref) => (_jsx(ToolkitButton, { ref: ref, variant: "ghost", className: cn("h-auto p-0 hover:bg-transparent active:scale-100 [&_svg]:!size-auto", props.emphasis === "solid" ? null : "hover:text-inherit", className), ...props })));
|
|
26
26
|
Button.displayName = "TeamPrimitiveButton";
|
|
27
27
|
function RoleIcon({ role }) {
|
|
28
28
|
if (role === "owner")
|
|
@@ -18,7 +18,7 @@ import { KeyProviderTile } from "./KeyProviderTile.js";
|
|
|
18
18
|
import { NewKeyMenu, normalizeKeyName } from "./NewKeyMenu.js";
|
|
19
19
|
import { SettingsCrossLinkHint } from "./SettingsCrossLinkHint.js";
|
|
20
20
|
import { SettingsSkeleton } from "./SettingsSkeleton.js";
|
|
21
|
-
const Button = React.forwardRef(({ className, ...props }, ref) => (_jsx(ToolkitButton, { ref: ref, variant: "ghost", className: cn("h-auto p-0 hover:bg-transparent
|
|
21
|
+
const Button = React.forwardRef(({ className, ...props }, ref) => (_jsx(ToolkitButton, { ref: ref, variant: "ghost", className: cn("h-auto p-0 hover:bg-transparent active:scale-100 [&_svg]:!size-auto", props.emphasis === "solid" ? null : "hover:text-inherit", className), ...props })));
|
|
22
22
|
Button.displayName = "SecretsPrimitiveButton";
|
|
23
23
|
const SOURCE_LABEL_KEY = {
|
|
24
24
|
vault: "secrets.sourceVault",
|
|
@@ -40,7 +40,7 @@ import { UsageSection } from "./UsageSection.js";
|
|
|
40
40
|
import { useBuilderConnectFlow, useBuilderStatus, } from "./useBuilderStatus.js";
|
|
41
41
|
import { settingsSectionDomId, useSettingsPanelController, } from "./useSettingsPanelController.js";
|
|
42
42
|
import { VoiceTranscriptionSection } from "./VoiceTranscriptionSection.js";
|
|
43
|
-
const Button = React.forwardRef(({ className, ...props }, ref) => (_jsx(ToolkitButton, { ref: ref, variant: "ghost", className: cn("h-auto p-0 hover:bg-transparent
|
|
43
|
+
const Button = React.forwardRef(({ className, ...props }, ref) => (_jsx(ToolkitButton, { ref: ref, variant: "ghost", className: cn("h-auto p-0 hover:bg-transparent active:scale-100 [&_svg]:!size-auto", props.emphasis === "solid" ? null : "hover:text-inherit", className), ...props })));
|
|
44
44
|
Button.displayName = "SettingsPrimitiveButton";
|
|
45
45
|
const ManageButton = React.forwardRef(({ children = "Manage", className, ...props }, ref) => (_jsx(ToolkitButton, { ref: ref, type: "button", variant: "ghost", intent: "neutral", emphasis: "outline", className: cn("inline-flex h-9 min-h-9 items-center justify-center gap-1 rounded-md border border-border px-3 text-sm font-medium leading-none text-foreground hover:bg-accent/40 active:scale-100 [&_svg]:!size-4", className), ...props, children: children })));
|
|
46
46
|
ManageButton.displayName = "SettingsManageButton";
|
|
@@ -62,11 +62,11 @@ export declare const postAwareness: import("h3").EventHandlerWithFetch<import("h
|
|
|
62
62
|
error: string;
|
|
63
63
|
states?: undefined;
|
|
64
64
|
} | {
|
|
65
|
-
error?: undefined;
|
|
66
65
|
states: {
|
|
67
66
|
clientId: number;
|
|
68
67
|
state: string;
|
|
69
68
|
}[];
|
|
69
|
+
error?: undefined;
|
|
70
70
|
}>>;
|
|
71
71
|
/**
|
|
72
72
|
* GET /_agent-native/collab/:docId/users
|
|
@@ -77,9 +77,9 @@ export declare const getActiveUsers: import("h3").EventHandlerWithFetch<import("
|
|
|
77
77
|
error: string;
|
|
78
78
|
users?: undefined;
|
|
79
79
|
} | {
|
|
80
|
-
error?: undefined;
|
|
81
80
|
users: {
|
|
82
81
|
clientId: number;
|
|
83
82
|
lastSeen: number;
|
|
84
83
|
}[];
|
|
84
|
+
error?: undefined;
|
|
85
85
|
}>>;
|
package/dist/collab/routes.d.ts
CHANGED
|
@@ -26,8 +26,8 @@ export declare const getCollabState: import("h3").EventHandlerWithFetch<import("
|
|
|
26
26
|
* Body: { update: string (base64), requestSource?: string }
|
|
27
27
|
*/
|
|
28
28
|
export declare const postCollabUpdate: import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<{
|
|
29
|
-
ok?: undefined;
|
|
30
29
|
error: string;
|
|
30
|
+
ok?: undefined;
|
|
31
31
|
} | {
|
|
32
32
|
error?: undefined;
|
|
33
33
|
ok: boolean;
|
|
@@ -13,8 +13,8 @@
|
|
|
13
13
|
* Body: { json: any, fieldName?: string, type?: "map"|"array", requestSource?: string }
|
|
14
14
|
*/
|
|
15
15
|
export declare const postCollabJson: import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<{
|
|
16
|
-
ok?: undefined;
|
|
17
16
|
error: string;
|
|
17
|
+
ok?: undefined;
|
|
18
18
|
} | {
|
|
19
19
|
error?: undefined;
|
|
20
20
|
ok: boolean;
|
|
@@ -34,12 +34,12 @@ declare const _default: import("../../action.js").ActionDefinition<{
|
|
|
34
34
|
idempotencyKey: string;
|
|
35
35
|
deleted: boolean;
|
|
36
36
|
} | {
|
|
37
|
-
id?: undefined;
|
|
38
37
|
error: string;
|
|
39
38
|
url?: undefined;
|
|
40
39
|
provider?: undefined;
|
|
41
40
|
configured?: undefined;
|
|
42
41
|
connectPath?: undefined;
|
|
42
|
+
id?: undefined;
|
|
43
43
|
} | {
|
|
44
44
|
url: string;
|
|
45
45
|
id: string;
|
|
@@ -48,11 +48,11 @@ declare const _default: import("../../action.js").ActionDefinition<{
|
|
|
48
48
|
connectPath?: undefined;
|
|
49
49
|
error?: undefined;
|
|
50
50
|
} | {
|
|
51
|
-
id?: undefined;
|
|
52
51
|
url?: undefined;
|
|
53
52
|
provider?: undefined;
|
|
54
53
|
error: string;
|
|
55
54
|
configured: boolean;
|
|
56
55
|
connectPath: string;
|
|
56
|
+
id?: undefined;
|
|
57
57
|
}>;
|
|
58
58
|
export default _default;
|
|
@@ -41,9 +41,9 @@ export declare function createObservabilityHandler(): import("h3").EventHandlerW
|
|
|
41
41
|
thumbsUpRate: number;
|
|
42
42
|
avgEvalScore: number;
|
|
43
43
|
} | {
|
|
44
|
-
id?: undefined;
|
|
45
44
|
summary: import("./types.js").TraceSummary;
|
|
46
45
|
spans: import("./types.js").TraceSpan[];
|
|
46
|
+
id?: undefined;
|
|
47
47
|
error?: undefined;
|
|
48
48
|
ok?: undefined;
|
|
49
49
|
} | {
|
|
@@ -53,15 +53,15 @@ export declare function createObservabilityHandler(): import("h3").EventHandlerW
|
|
|
53
53
|
error?: undefined;
|
|
54
54
|
ok?: undefined;
|
|
55
55
|
} | {
|
|
56
|
-
id?: undefined;
|
|
57
56
|
summary?: undefined;
|
|
58
57
|
spans?: undefined;
|
|
58
|
+
id?: undefined;
|
|
59
59
|
error: any;
|
|
60
60
|
ok?: undefined;
|
|
61
61
|
} | {
|
|
62
|
-
id?: undefined;
|
|
63
62
|
summary?: undefined;
|
|
64
63
|
spans?: undefined;
|
|
65
|
-
|
|
64
|
+
id?: undefined;
|
|
66
65
|
error?: undefined;
|
|
66
|
+
ok: boolean;
|
|
67
67
|
}>>;
|
package/dist/org/handlers.d.ts
CHANGED
|
@@ -53,9 +53,9 @@ export declare const setWorkspaceAppDefaultVisibilityHandler: import("h3").Event
|
|
|
53
53
|
/** POST /_agent-native/org — create a new organization */
|
|
54
54
|
export declare const createOrgHandler: import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<{
|
|
55
55
|
success: boolean;
|
|
56
|
-
id?: undefined;
|
|
57
56
|
name?: undefined;
|
|
58
57
|
role?: undefined;
|
|
58
|
+
id?: undefined;
|
|
59
59
|
} | {
|
|
60
60
|
success?: undefined;
|
|
61
61
|
id: string;
|
|
@@ -48,8 +48,8 @@ export declare function handleUpdateResource(event: any): Promise<import("./stor
|
|
|
48
48
|
}>;
|
|
49
49
|
/** DELETE /_agent-native/resources/:id — delete a resource */
|
|
50
50
|
export declare function handleDeleteResource(event: any): Promise<{
|
|
51
|
-
error: string;
|
|
52
51
|
ok?: undefined;
|
|
52
|
+
error: string;
|
|
53
53
|
} | {
|
|
54
54
|
error?: undefined;
|
|
55
55
|
ok: boolean;
|
|
@@ -46,20 +46,20 @@ export declare function createAgentEngineApiKeyHandler(): import("h3").EventHand
|
|
|
46
46
|
scope?: undefined;
|
|
47
47
|
ok?: undefined;
|
|
48
48
|
} | {
|
|
49
|
-
error?: undefined;
|
|
50
49
|
ok: boolean;
|
|
51
50
|
key: string;
|
|
52
51
|
scope: string;
|
|
52
|
+
error?: undefined;
|
|
53
53
|
} | {
|
|
54
54
|
key?: undefined;
|
|
55
55
|
scope?: undefined;
|
|
56
56
|
ok: boolean;
|
|
57
57
|
error: string;
|
|
58
58
|
} | {
|
|
59
|
-
error?: undefined;
|
|
60
59
|
ok: boolean;
|
|
61
60
|
key: string;
|
|
62
61
|
baseUrlKey?: string;
|
|
63
62
|
scope: AgentEngineApiKeyScope;
|
|
63
|
+
error?: undefined;
|
|
64
64
|
}>>;
|
|
65
65
|
export {};
|
|
@@ -408,8 +408,8 @@ export declare function createPublicRemoteAgentsHandler(discover?: PublicAgentDi
|
|
|
408
408
|
error: string;
|
|
409
409
|
agents?: undefined;
|
|
410
410
|
} | {
|
|
411
|
-
error?: undefined;
|
|
412
411
|
agents: Omit<import("./agent-discovery.js").DiscoveredAgent, "auth" | "kind">[];
|
|
412
|
+
error?: undefined;
|
|
413
413
|
}>>;
|
|
414
414
|
export declare function getBuilderConnectErrorDisposition(error: unknown, connectAttemptId: string | null): "correlated" | "legacy" | null;
|
|
415
415
|
export declare function getBuilderConnectErrorKey(ownerEmail: string, connectAttemptId?: string | null): string;
|
|
@@ -59,8 +59,8 @@ export declare function createRealtimeTokenHandler(): import("h3").EventHandlerW
|
|
|
59
59
|
expiresAt?: undefined;
|
|
60
60
|
ttlSeconds?: undefined;
|
|
61
61
|
} | {
|
|
62
|
-
error?: undefined;
|
|
63
62
|
token: string;
|
|
64
63
|
expiresAt: string;
|
|
65
64
|
ttlSeconds: number;
|
|
65
|
+
error?: undefined;
|
|
66
66
|
}>>;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agent-native/core",
|
|
3
|
-
"version": "0.183.0
|
|
3
|
+
"version": "0.183.0",
|
|
4
4
|
"description": "The agentic application framework for building autonomous agents with intuitive UIs",
|
|
5
5
|
"homepage": "https://github.com/BuilderIO/agent-native#readme",
|
|
6
6
|
"bugs": {
|
|
@@ -488,9 +488,9 @@
|
|
|
488
488
|
"yaml": "^2.9.0",
|
|
489
489
|
"yjs": "^13.6.32",
|
|
490
490
|
"zod": "^4.3.6",
|
|
491
|
-
"@agent-native/
|
|
492
|
-
"@agent-native/
|
|
493
|
-
"@agent-native/
|
|
491
|
+
"@agent-native/recap-cli": "0.5.35",
|
|
492
|
+
"@agent-native/toolkit": "^0.20.5",
|
|
493
|
+
"@agent-native/agentkit": "^0.2.5"
|
|
494
494
|
},
|
|
495
495
|
"devDependencies": {
|
|
496
496
|
"@ai-sdk/anthropic": "^3.0.71",
|