@agent-native/core 0.179.0-nightly-20260914204702 → 0.179.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/CHANGELOG.md +32 -0
- package/corpus/templates/clips/CHANGELOG.md +19 -0
- package/corpus/templates/content/CHANGELOG.md +20 -0
- package/corpus/templates/content/app/components/editor/CommentsSidebar.tsx +16 -7
- package/corpus/templates/content/app/components/editor/DocumentEditor.tsx +70 -55
- package/corpus/templates/content/app/components/editor/suggestions/draft-session.ts +3 -3
- package/corpus/templates/content/app/components/editor/suggestions/markdown-operation.ts +98 -2
- package/corpus/templates/content/changelog/2026-09-13-suggestion-previews-and-highlights-stay-aligned-near-lists.md +5 -0
- package/corpus/templates/content/shared/suggestion-text.ts +20 -4
- package/corpus/templates/design/.generated/bridge/editor-chrome.generated.ts +17 -5
- package/corpus/templates/design/.generated/bridge/source-location.generated.ts +17 -5
- package/corpus/templates/design/CHANGELOG.md +60 -0
- package/corpus/templates/design/app/components/design/bridge/editor-chrome.bridge.ts +39 -7
- package/corpus/templates/design/app/components/design/bridge/source-location.bridge.ts +37 -4
- package/corpus/templates/design/app/pages/design-editor/source-location.ts +18 -0
- package/corpus/templates/design/changelog/2026-09-14-selecting-an-element-in-a-live-react-app-now-anchors-to-the-.md +6 -0
- package/corpus/templates/factory/CHANGELOG.md +19 -0
- package/corpus/templates/forms/CHANGELOG.md +6 -0
- package/corpus/templates/mail/CHANGELOG.md +34 -0
- package/corpus/templates/slides/CHANGELOG.md +15 -0
- 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/realtime-token.d.ts +1 -1
- package/package.json +4 -4
package/corpus/README.md
CHANGED
|
@@ -3,6 +3,38 @@
|
|
|
3
3
|
All notable user-facing changes to Agent-Native Analytics 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-12
|
|
7
|
+
|
|
8
|
+
### Improved
|
|
9
|
+
|
|
10
|
+
- Sign-in panel moves clear of product content
|
|
11
|
+
|
|
12
|
+
### Fixed
|
|
13
|
+
|
|
14
|
+
- Dashboard pages no longer refresh after unrelated agent actions
|
|
15
|
+
|
|
16
|
+
## 2026-09-11
|
|
17
|
+
|
|
18
|
+
### Fixed
|
|
19
|
+
|
|
20
|
+
- Analytics keeps event receipts until BigQuery confirms delivery
|
|
21
|
+
- Ask sidebar controls stay on one row for a cleaner chat list.
|
|
22
|
+
- Coverage-sensitive searches now return a clearly labeled partial answer when a full provider scan cannot finish
|
|
23
|
+
- Retention metrics stay visibly unknown when analytics event coverage has gaps
|
|
24
|
+
- The 7d Rolling Signed-In Return Rate chart no longer drops to 0% for the newest days; a point appears only once its 7-day or 14-day return window has fully elapsed, and the 7d/30d ranges now render.
|
|
25
|
+
|
|
26
|
+
## 2026-09-10
|
|
27
|
+
|
|
28
|
+
### Improved
|
|
29
|
+
|
|
30
|
+
- Dashboards render charts immediately instead of animating every panel on load, so large dashboards no longer freeze the page.
|
|
31
|
+
|
|
32
|
+
### Fixed
|
|
33
|
+
|
|
34
|
+
- Agent context links for dashboards, analyses, and session replays now work for external agents instead of returning Unauthorized
|
|
35
|
+
- BigQuery active-user charts now show the full selected history.
|
|
36
|
+
- Chart editor dialogs stay centered when editing dashboards
|
|
37
|
+
|
|
6
38
|
## 2026-09-09
|
|
7
39
|
|
|
8
40
|
### Improved
|
|
@@ -3,10 +3,29 @@
|
|
|
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-13
|
|
7
|
+
|
|
8
|
+
### Improved
|
|
9
|
+
|
|
10
|
+
- Members now know to ask an organization admin to create the first Space.
|
|
11
|
+
|
|
12
|
+
### Fixed
|
|
13
|
+
|
|
14
|
+
- Dictate stays available by default in Clips Desktop.
|
|
15
|
+
|
|
16
|
+
## 2026-09-12
|
|
17
|
+
|
|
18
|
+
### Fixed
|
|
19
|
+
|
|
20
|
+
- Archive and Move to trash actions now use clear, action-specific labels in Clips.
|
|
21
|
+
- Clips sign-in prompts now focus on the requested action with larger buttons.
|
|
22
|
+
- Meetings stays in navigation and links to its Labs setting when unavailable.
|
|
23
|
+
|
|
6
24
|
## 2026-09-10
|
|
7
25
|
|
|
8
26
|
### Fixed
|
|
9
27
|
|
|
28
|
+
- Deleting your only organization no longer leaves a "Couldn't load organization branding" error above the create-an-organization card in Settings.
|
|
10
29
|
- Builder and other MCP connections now open setup in a new tab.
|
|
11
30
|
|
|
12
31
|
## 2026-09-09
|
|
@@ -3,6 +3,26 @@
|
|
|
3
3
|
All notable user-facing changes to Agent-Native Content are documented here. Open it any
|
|
4
4
|
time from the command menu (Cmd+K → "What's new").
|
|
5
5
|
|
|
6
|
+
## 2026-09-14
|
|
7
|
+
|
|
8
|
+
### Fixed
|
|
9
|
+
|
|
10
|
+
- The block drag-handle menu now always opens beside the block instead of sometimes jumping to the corner of the window.
|
|
11
|
+
- Pasting a checklist from Notion or GitHub now keeps its checkboxes instead of turning into plain bullets.
|
|
12
|
+
- Asking the agent about Notion no longer reports "not connected" when the Notion MCP server is connected — it now names the account connection and the MCP connection separately.
|
|
13
|
+
|
|
14
|
+
## 2026-09-12
|
|
15
|
+
|
|
16
|
+
### Changed
|
|
17
|
+
|
|
18
|
+
- Databases are now called **collections** across Content — same pages, views, and sharing, friendlier name.
|
|
19
|
+
|
|
20
|
+
## 2026-09-10
|
|
21
|
+
|
|
22
|
+
### Fixed
|
|
23
|
+
|
|
24
|
+
- Opening a page link your account cannot read now lands on a page you can open and says why, instead of stopping on a "Document unavailable" screen — so a brand-new account no longer arrives on a dead end after signing up.
|
|
25
|
+
|
|
6
26
|
## 2026-09-09
|
|
7
27
|
|
|
8
28
|
### Added
|
|
@@ -248,6 +248,12 @@ export function findPendingCommentOffset(
|
|
|
248
248
|
|
|
249
249
|
type ThreadLayoutIdentity = { threadId: string; comments: readonly unknown[] };
|
|
250
250
|
|
|
251
|
+
// Stable identities: a fresh `[]` default re-keys every downstream useMemo,
|
|
252
|
+
// which rebuilds the anchor observers on every render.
|
|
253
|
+
const NO_THREADS: CommentThread[] = [];
|
|
254
|
+
const NO_SUGGESTIONS: ResourceSuggestion[] = [];
|
|
255
|
+
const NO_DRAFT_SUGGESTIONS: DraftSuggestion[] = [];
|
|
256
|
+
|
|
251
257
|
export function estimateThreadCardHeight(thread: ThreadLayoutIdentity) {
|
|
252
258
|
return 80 + Math.max(0, thread.comments.length - 1) * 44;
|
|
253
259
|
}
|
|
@@ -677,7 +683,7 @@ export function CommentsSidebar({
|
|
|
677
683
|
compact = false,
|
|
678
684
|
replyDrafts,
|
|
679
685
|
documentId,
|
|
680
|
-
threads =
|
|
686
|
+
threads = NO_THREADS,
|
|
681
687
|
isLoading = false,
|
|
682
688
|
pendingComment,
|
|
683
689
|
pendingTargetValid = true,
|
|
@@ -700,8 +706,8 @@ export function CommentsSidebar({
|
|
|
700
706
|
canResolve = false,
|
|
701
707
|
alignToAnchors = true,
|
|
702
708
|
forceVisible = false,
|
|
703
|
-
suggestions =
|
|
704
|
-
draftSuggestions =
|
|
709
|
+
suggestions = NO_SUGGESTIONS,
|
|
710
|
+
draftSuggestions = NO_DRAFT_SUGGESTIONS,
|
|
705
711
|
onMaterializeDraft,
|
|
706
712
|
canDecideSuggestions = false,
|
|
707
713
|
decidingSuggestion = false,
|
|
@@ -1105,13 +1111,16 @@ export function CommentsSidebar({
|
|
|
1105
1111
|
[],
|
|
1106
1112
|
);
|
|
1107
1113
|
|
|
1114
|
+
// Only the presence of a pending comment moves the lane; its draft text
|
|
1115
|
+
// changes on every keystroke and must not re-key the anchor observers.
|
|
1116
|
+
const hasPendingComment = !!pendingComment;
|
|
1108
1117
|
const recomputeOffsets = useCallback(() => {
|
|
1109
1118
|
const container = scrollContainerRef?.current ?? null;
|
|
1110
1119
|
if (!container || inlineThreads.length === 0) {
|
|
1111
1120
|
setThreadPositions((prev) => (prev.size === 0 ? prev : new Map()));
|
|
1112
1121
|
setPendingOffset((prev) => {
|
|
1113
1122
|
const next =
|
|
1114
|
-
|
|
1123
|
+
hasPendingComment && alignToAnchors
|
|
1115
1124
|
? findPendingCommentOffset(container, sidebarRef.current)
|
|
1116
1125
|
: null;
|
|
1117
1126
|
return prev === next ? prev : next;
|
|
@@ -1131,7 +1140,7 @@ export function CommentsSidebar({
|
|
|
1131
1140
|
if (position) positions.set(thread.threadId, position);
|
|
1132
1141
|
}
|
|
1133
1142
|
const nextPendingOffset =
|
|
1134
|
-
|
|
1143
|
+
hasPendingComment && alignToAnchors
|
|
1135
1144
|
? findPendingCommentOffset(container, layoutContainer)
|
|
1136
1145
|
: null;
|
|
1137
1146
|
setThreadPositions((prev) => {
|
|
@@ -1152,7 +1161,7 @@ export function CommentsSidebar({
|
|
|
1152
1161
|
setPendingOffset((prev) =>
|
|
1153
1162
|
prev === nextPendingOffset ? prev : nextPendingOffset,
|
|
1154
1163
|
);
|
|
1155
|
-
}, [alignToAnchors, inlineThreads,
|
|
1164
|
+
}, [alignToAnchors, inlineThreads, hasPendingComment, scrollContainerRef]);
|
|
1156
1165
|
|
|
1157
1166
|
useEffect(() => {
|
|
1158
1167
|
const container = scrollContainerRef?.current ?? null;
|
|
@@ -1186,7 +1195,7 @@ export function CommentsSidebar({
|
|
|
1186
1195
|
window.removeEventListener("resize", schedule);
|
|
1187
1196
|
};
|
|
1188
1197
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
1189
|
-
}, [openThreadKey,
|
|
1198
|
+
}, [openThreadKey, hasPendingComment, recomputeOffsets]);
|
|
1190
1199
|
|
|
1191
1200
|
useEffect(() => {
|
|
1192
1201
|
const openIds = new Set(inlineThreads.map((thread) => thread.threadId));
|
|
@@ -26,6 +26,7 @@ import {
|
|
|
26
26
|
import type { ResourceSuggestion } from "@agent-native/core/review";
|
|
27
27
|
import { normalizeDocumentTitle } from "@agent-native/core/shared";
|
|
28
28
|
import type { Document, DocumentSyncStatus } from "@shared/api";
|
|
29
|
+
import { canonicalizeNfm } from "@shared/nfm";
|
|
29
30
|
import {
|
|
30
31
|
SuggestionFormattingMappingError,
|
|
31
32
|
suggestionMarkedSourceRanges,
|
|
@@ -76,7 +77,7 @@ import {
|
|
|
76
77
|
SheetTitle,
|
|
77
78
|
} from "@/components/ui/sheet";
|
|
78
79
|
import { flushDocumentPropertyWrites } from "@/hooks/document-property-persistence";
|
|
79
|
-
import { useComments } from "@/hooks/use-comments";
|
|
80
|
+
import { useComments, type CommentThread } from "@/hooks/use-comments";
|
|
80
81
|
import {
|
|
81
82
|
useCreateContentDatabase,
|
|
82
83
|
useDeleteContentDatabase,
|
|
@@ -207,6 +208,14 @@ import type {
|
|
|
207
208
|
VisualEditorPersistenceController,
|
|
208
209
|
} from "./VisualEditor";
|
|
209
210
|
|
|
211
|
+
const NO_COMMENT_THREADS: CommentThread[] = [];
|
|
212
|
+
|
|
213
|
+
export function documentEditorCommentThreads(
|
|
214
|
+
threads: CommentThread[] | null | undefined,
|
|
215
|
+
) {
|
|
216
|
+
return threads ?? NO_COMMENT_THREADS;
|
|
217
|
+
}
|
|
218
|
+
|
|
210
219
|
const TAB_ID = generateTabId();
|
|
211
220
|
|
|
212
221
|
export function applyHistoryToDocumentBody(
|
|
@@ -325,6 +334,19 @@ export function suggestionPresentation(
|
|
|
325
334
|
}
|
|
326
335
|
const range = resolveMarkdownSuggestionRange(currentMarkdown, operation);
|
|
327
336
|
if (!range) return null;
|
|
337
|
+
const editorMarkdown = canonicalizeNfm(currentMarkdown);
|
|
338
|
+
const currentText = currentMarkdown.slice(range.from, range.to);
|
|
339
|
+
const editorRange = resolveMarkdownSuggestionRange(editorMarkdown, {
|
|
340
|
+
before: { markdown: currentMarkdown, changedText: currentText },
|
|
341
|
+
after: { markdown: currentMarkdown, changedText: currentText },
|
|
342
|
+
anchor: {
|
|
343
|
+
from: range.from,
|
|
344
|
+
to: range.to,
|
|
345
|
+
prefix: currentMarkdown.slice(Math.max(0, range.from - 32), range.from),
|
|
346
|
+
suffix: currentMarkdown.slice(range.to, range.to + 32),
|
|
347
|
+
},
|
|
348
|
+
});
|
|
349
|
+
if (!editorRange) return null;
|
|
328
350
|
return {
|
|
329
351
|
id: suggestion.id,
|
|
330
352
|
kind: operation.kind as VisualEditorSuggestion["kind"],
|
|
@@ -341,9 +363,12 @@ export function suggestionPresentation(
|
|
|
341
363
|
to: operationAnchor.from + after.changedText.length,
|
|
342
364
|
},
|
|
343
365
|
anchor: {
|
|
344
|
-
from:
|
|
345
|
-
prefix:
|
|
346
|
-
|
|
366
|
+
from: editorRange.from,
|
|
367
|
+
prefix: editorMarkdown.slice(
|
|
368
|
+
Math.max(0, editorRange.from - 32),
|
|
369
|
+
editorRange.from,
|
|
370
|
+
),
|
|
371
|
+
suffix: editorMarkdown.slice(editorRange.to, editorRange.to + 32),
|
|
347
372
|
},
|
|
348
373
|
presentation: "canonical",
|
|
349
374
|
};
|
|
@@ -1103,6 +1128,27 @@ export function positionAnchoredCommentCard({
|
|
|
1103
1128
|
};
|
|
1104
1129
|
}
|
|
1105
1130
|
|
|
1131
|
+
export type AnchoredCommentPosition = {
|
|
1132
|
+
left: number;
|
|
1133
|
+
top: number;
|
|
1134
|
+
width: number;
|
|
1135
|
+
placement: "above" | "below";
|
|
1136
|
+
};
|
|
1137
|
+
|
|
1138
|
+
export function sameAnchoredCommentPosition(
|
|
1139
|
+
left: AnchoredCommentPosition | null,
|
|
1140
|
+
right: AnchoredCommentPosition | null,
|
|
1141
|
+
) {
|
|
1142
|
+
if (left === right) return true;
|
|
1143
|
+
if (!left || !right) return false;
|
|
1144
|
+
return (
|
|
1145
|
+
left.left === right.left &&
|
|
1146
|
+
left.top === right.top &&
|
|
1147
|
+
left.width === right.width &&
|
|
1148
|
+
left.placement === right.placement
|
|
1149
|
+
);
|
|
1150
|
+
}
|
|
1151
|
+
|
|
1106
1152
|
export function pendingCommentTargetMatches(
|
|
1107
1153
|
marked: Iterable<Pick<Element, "textContent">>,
|
|
1108
1154
|
quotedText: string,
|
|
@@ -3758,8 +3804,13 @@ function PageEditorSessionBody({
|
|
|
3758
3804
|
const replyDrafts = useCommentReplyDrafts(documentId, session?.email);
|
|
3759
3805
|
const [pendingCommentTargetValid, setPendingCommentTargetValid] =
|
|
3760
3806
|
useState(true);
|
|
3807
|
+
// Keyed by the selection, never by the draft text: re-running this on each
|
|
3808
|
+
// keystroke blanks the target back to invalid for a frame, which shows the
|
|
3809
|
+
// "select text" alert and disables Submit inside the open composer.
|
|
3810
|
+
const pendingCommentTargetId = pendingComment?.id ?? null;
|
|
3811
|
+
const pendingCommentQuotedText = pendingComment?.quotedText ?? null;
|
|
3761
3812
|
useLayoutEffect(() => {
|
|
3762
|
-
if (!
|
|
3813
|
+
if (!pendingCommentTargetId || pendingCommentQuotedText === null) {
|
|
3763
3814
|
setPendingCommentTargetValid(true);
|
|
3764
3815
|
return;
|
|
3765
3816
|
}
|
|
@@ -3777,7 +3828,7 @@ function PageEditorSessionBody({
|
|
|
3777
3828
|
".comment-highlight--pending",
|
|
3778
3829
|
);
|
|
3779
3830
|
setPendingCommentTargetValid(
|
|
3780
|
-
pendingCommentTargetMatches(marked,
|
|
3831
|
+
pendingCommentTargetMatches(marked, pendingCommentQuotedText),
|
|
3781
3832
|
);
|
|
3782
3833
|
});
|
|
3783
3834
|
};
|
|
@@ -3795,7 +3846,7 @@ function PageEditorSessionBody({
|
|
|
3795
3846
|
cancelAnimationFrame(frame);
|
|
3796
3847
|
observer.disconnect();
|
|
3797
3848
|
};
|
|
3798
|
-
}, [
|
|
3849
|
+
}, [pendingCommentTargetId, pendingCommentQuotedText]);
|
|
3799
3850
|
const [focusSuggestionId, setFocusSuggestionId] = useState<string | null>(
|
|
3800
3851
|
null,
|
|
3801
3852
|
);
|
|
@@ -3806,12 +3857,8 @@ function PageEditorSessionBody({
|
|
|
3806
3857
|
const documentLayoutRef = useRef<HTMLDivElement>(null);
|
|
3807
3858
|
const commentLaneRef = useRef<HTMLElement>(null);
|
|
3808
3859
|
const anchoredCommentRef = useRef<HTMLElement>(null);
|
|
3809
|
-
const [anchoredCommentPosition, setAnchoredCommentPosition] =
|
|
3810
|
-
|
|
3811
|
-
top: number;
|
|
3812
|
-
width: number;
|
|
3813
|
-
placement: "above" | "below";
|
|
3814
|
-
} | null>(null);
|
|
3860
|
+
const [anchoredCommentPosition, setAnchoredCommentPosition] =
|
|
3861
|
+
useState<AnchoredCommentPosition | null>(null);
|
|
3815
3862
|
const [commentLaneOffset, setCommentLaneOffset] = useState(0);
|
|
3816
3863
|
const hasUtilityRailSpace = useElementMinWidth(documentLayoutRef, 960);
|
|
3817
3864
|
const hasInlineCommentSpace = useElementMinWidth(documentLayoutRef, 1088);
|
|
@@ -3852,45 +3899,6 @@ function PageEditorSessionBody({
|
|
|
3852
3899
|
const hasFocusedCommentReply =
|
|
3853
3900
|
replyDrafts.focus.current?.documentId === documentId;
|
|
3854
3901
|
|
|
3855
|
-
useLayoutEffect(() => {
|
|
3856
|
-
if (!pendingComment) {
|
|
3857
|
-
setPendingCommentTargetValid(true);
|
|
3858
|
-
return;
|
|
3859
|
-
}
|
|
3860
|
-
const scrollContainer = scrollContainerRef.current;
|
|
3861
|
-
if (!scrollContainer) {
|
|
3862
|
-
setPendingCommentTargetValid(false);
|
|
3863
|
-
return;
|
|
3864
|
-
}
|
|
3865
|
-
|
|
3866
|
-
let frame = 0;
|
|
3867
|
-
const update = () => {
|
|
3868
|
-
cancelAnimationFrame(frame);
|
|
3869
|
-
frame = requestAnimationFrame(() => {
|
|
3870
|
-
const marked = scrollContainer.querySelectorAll(
|
|
3871
|
-
".comment-highlight--pending",
|
|
3872
|
-
);
|
|
3873
|
-
setPendingCommentTargetValid(
|
|
3874
|
-
pendingCommentTargetMatches(marked, pendingComment.quotedText),
|
|
3875
|
-
);
|
|
3876
|
-
});
|
|
3877
|
-
};
|
|
3878
|
-
setPendingCommentTargetValid(false);
|
|
3879
|
-
update();
|
|
3880
|
-
const observer = new MutationObserver(update);
|
|
3881
|
-
observer.observe(scrollContainer, {
|
|
3882
|
-
childList: true,
|
|
3883
|
-
subtree: true,
|
|
3884
|
-
characterData: true,
|
|
3885
|
-
attributes: true,
|
|
3886
|
-
attributeFilter: ["class"],
|
|
3887
|
-
});
|
|
3888
|
-
return () => {
|
|
3889
|
-
cancelAnimationFrame(frame);
|
|
3890
|
-
observer.disconnect();
|
|
3891
|
-
};
|
|
3892
|
-
}, [pendingComment]);
|
|
3893
|
-
|
|
3894
3902
|
useEffect(() => {
|
|
3895
3903
|
if (utilityPanel) setLastUtilityPanel(utilityPanel);
|
|
3896
3904
|
}, [utilityPanel]);
|
|
@@ -4102,6 +4110,13 @@ function PageEditorSessionBody({
|
|
|
4102
4110
|
) as HTMLElement | null;
|
|
4103
4111
|
if (!scrollContainer || !scrollContent) return;
|
|
4104
4112
|
let frame = 0;
|
|
4113
|
+
// The observers below watch the card itself, and the placement is derived
|
|
4114
|
+
// from the card's own measured height. Committing an unchanged position
|
|
4115
|
+
// would feed that measurement back in as a fresh re-render every frame.
|
|
4116
|
+
const commit = (next: AnchoredCommentPosition) =>
|
|
4117
|
+
setAnchoredCommentPosition((previous) =>
|
|
4118
|
+
sameAnchoredCommentPosition(previous, next) ? previous : next,
|
|
4119
|
+
);
|
|
4105
4120
|
const update = () => {
|
|
4106
4121
|
cancelAnimationFrame(frame);
|
|
4107
4122
|
frame = requestAnimationFrame(() => {
|
|
@@ -4123,7 +4138,7 @@ function PageEditorSessionBody({
|
|
|
4123
4138
|
: ".comment-highlight--pending",
|
|
4124
4139
|
) as HTMLElement | null;
|
|
4125
4140
|
if (!marked) {
|
|
4126
|
-
|
|
4141
|
+
commit(
|
|
4127
4142
|
positionUnanchoredCommentCard({
|
|
4128
4143
|
containerRect: scrollContent.getBoundingClientRect(),
|
|
4129
4144
|
boundaryRect: scrollContainer.getBoundingClientRect(),
|
|
@@ -4139,7 +4154,7 @@ function PageEditorSessionBody({
|
|
|
4139
4154
|
const boundaryRect = scrollContainer.getBoundingClientRect();
|
|
4140
4155
|
const cardHeight =
|
|
4141
4156
|
anchoredCommentRef.current?.getBoundingClientRect().height ?? 180;
|
|
4142
|
-
|
|
4157
|
+
commit(
|
|
4143
4158
|
positionAnchoredCommentCard({
|
|
4144
4159
|
anchorRect,
|
|
4145
4160
|
containerRect,
|
|
@@ -4431,7 +4446,7 @@ function PageEditorSessionBody({
|
|
|
4431
4446
|
compact={!hasInlineCommentSpace}
|
|
4432
4447
|
replyDrafts={replyDrafts}
|
|
4433
4448
|
documentId={documentId}
|
|
4434
|
-
threads={threads
|
|
4449
|
+
threads={documentEditorCommentThreads(threads)}
|
|
4435
4450
|
isLoading={commentsLoading}
|
|
4436
4451
|
pendingComment={pendingComment}
|
|
4437
4452
|
pendingTargetValid={pendingCommentTargetValid}
|
|
@@ -6,7 +6,7 @@ import { SuggestionFormattingMappingError } from "@shared/suggestion-formatting"
|
|
|
6
6
|
|
|
7
7
|
import {
|
|
8
8
|
draftSuggestionAnchors,
|
|
9
|
-
|
|
9
|
+
markdownSuggestionOperationsForEditorRevision,
|
|
10
10
|
} from "./markdown-operation";
|
|
11
11
|
|
|
12
12
|
export function canonicalSuggestionRevision(
|
|
@@ -190,14 +190,14 @@ export function suggestionDraftOperations(
|
|
|
190
190
|
session: SuggestionDraftSession,
|
|
191
191
|
draftContent: string,
|
|
192
192
|
) {
|
|
193
|
-
const operations =
|
|
193
|
+
const operations = markdownSuggestionOperationsForEditorRevision({
|
|
194
194
|
before: session.baseContent,
|
|
195
195
|
after: draftContent,
|
|
196
196
|
replacements: session.replacementIntents ?? [],
|
|
197
197
|
});
|
|
198
198
|
if (!session.existingSuggestion || operations.length < 2) return operations;
|
|
199
199
|
// An amendment retains the saved proposal's single-operation identity.
|
|
200
|
-
return
|
|
200
|
+
return markdownSuggestionOperationsForEditorRevision({
|
|
201
201
|
before: session.baseContent,
|
|
202
202
|
after: draftContent,
|
|
203
203
|
replacements: [
|
|
@@ -1,8 +1,10 @@
|
|
|
1
|
+
import { canonicalizeNfm } from "@shared/nfm";
|
|
1
2
|
import {
|
|
2
3
|
suggestionFormattingChanges,
|
|
3
4
|
suggestionMarkedSourceRanges,
|
|
4
5
|
SuggestionFormattingMappingError,
|
|
5
6
|
} from "@shared/suggestion-formatting";
|
|
7
|
+
import { resolveMarkdownSuggestionRange } from "@shared/suggestion-rebase";
|
|
6
8
|
|
|
7
9
|
export type MarkdownSuggestionOperation = {
|
|
8
10
|
ordinal: number;
|
|
@@ -551,14 +553,108 @@ export function markdownSuggestionOperationsForReplacements(input: {
|
|
|
551
553
|
return result;
|
|
552
554
|
}
|
|
553
555
|
|
|
556
|
+
export function markdownSuggestionOperationsForEditorRevision(input: {
|
|
557
|
+
before: string;
|
|
558
|
+
after: string;
|
|
559
|
+
replacements: ReadonlyArray<{ from: number; to: number }>;
|
|
560
|
+
}): MarkdownSuggestionOperation[] {
|
|
561
|
+
const editorBefore = canonicalizeNfm(input.before);
|
|
562
|
+
const replacements = input.replacements.map(({ from, to }) => {
|
|
563
|
+
const changedText = input.before.slice(from, to);
|
|
564
|
+
const range = resolveMarkdownSuggestionRange(editorBefore, {
|
|
565
|
+
before: { markdown: input.before, changedText },
|
|
566
|
+
after: { markdown: input.before, changedText },
|
|
567
|
+
anchor: {
|
|
568
|
+
from,
|
|
569
|
+
to,
|
|
570
|
+
prefix: input.before.slice(Math.max(0, from - 32), from),
|
|
571
|
+
suffix: input.before.slice(to, to + 32),
|
|
572
|
+
},
|
|
573
|
+
});
|
|
574
|
+
if (!range) throw new SuggestionFormattingMappingError();
|
|
575
|
+
return range;
|
|
576
|
+
});
|
|
577
|
+
return markdownSuggestionOperationsForReplacements({
|
|
578
|
+
before: editorBefore,
|
|
579
|
+
after: input.after,
|
|
580
|
+
replacements,
|
|
581
|
+
}).map((operation, ordinal) => {
|
|
582
|
+
const range = resolveMarkdownSuggestionRange(input.before, operation);
|
|
583
|
+
if (!range) throw new SuggestionFormattingMappingError();
|
|
584
|
+
return operationForChange(
|
|
585
|
+
input.before,
|
|
586
|
+
range.from,
|
|
587
|
+
range.to,
|
|
588
|
+
operation.after.changedText,
|
|
589
|
+
ordinal,
|
|
590
|
+
);
|
|
591
|
+
});
|
|
592
|
+
}
|
|
593
|
+
|
|
554
594
|
export function draftSuggestionAnchors(
|
|
555
595
|
operations: readonly MarkdownSuggestionOperation[],
|
|
556
596
|
draft: string,
|
|
557
597
|
): MarkdownSuggestionOperation["anchor"][] {
|
|
598
|
+
const canonical = operations.every(
|
|
599
|
+
(operation) =>
|
|
600
|
+
canonicalizeNfm(operation.after.markdown) === operation.after.markdown,
|
|
601
|
+
);
|
|
602
|
+
let proposedRaw = operations[0]?.before.markdown ?? draft;
|
|
603
|
+
for (const operation of [...operations].reverse()) {
|
|
604
|
+
proposedRaw =
|
|
605
|
+
proposedRaw.slice(0, operation.anchor.from) +
|
|
606
|
+
operation.after.changedText +
|
|
607
|
+
proposedRaw.slice(operation.anchor.to);
|
|
608
|
+
}
|
|
609
|
+
const parts = canonical ? null : diffParts(proposedRaw, draft);
|
|
610
|
+
if (!canonical && !parts) throw new SuggestionFormattingMappingError();
|
|
611
|
+
const boundaryMap = new Array<number>(proposedRaw.length + 1);
|
|
612
|
+
let rawOffset = 0;
|
|
613
|
+
let draftOffset = 0;
|
|
614
|
+
boundaryMap[0] = 0;
|
|
615
|
+
for (const part of parts ?? []) {
|
|
616
|
+
if (part.type === "insert") {
|
|
617
|
+
draftOffset += part.text.length;
|
|
618
|
+
boundaryMap[rawOffset] = draftOffset;
|
|
619
|
+
continue;
|
|
620
|
+
}
|
|
621
|
+
for (let index = 0; index < part.text.length; index += 1) {
|
|
622
|
+
rawOffset += 1;
|
|
623
|
+
if (part.type === "equal") draftOffset += 1;
|
|
624
|
+
boundaryMap[rawOffset] = draftOffset;
|
|
625
|
+
}
|
|
626
|
+
}
|
|
627
|
+
if (
|
|
628
|
+
!canonical &&
|
|
629
|
+
(rawOffset !== proposedRaw.length || draftOffset !== draft.length)
|
|
630
|
+
)
|
|
631
|
+
throw new SuggestionFormattingMappingError();
|
|
632
|
+
|
|
558
633
|
let delta = 0;
|
|
559
634
|
return operations.map((operation) => {
|
|
560
|
-
|
|
561
|
-
|
|
635
|
+
if (canonical) {
|
|
636
|
+
const from = operation.anchor.from + delta;
|
|
637
|
+
const to = from + operation.after.changedText.length;
|
|
638
|
+
delta +=
|
|
639
|
+
operation.after.changedText.length -
|
|
640
|
+
operation.before.changedText.length;
|
|
641
|
+
return {
|
|
642
|
+
from,
|
|
643
|
+
to,
|
|
644
|
+
prefix: draft.slice(Math.max(0, from - 32), from),
|
|
645
|
+
suffix: draft.slice(to, to + 32),
|
|
646
|
+
};
|
|
647
|
+
}
|
|
648
|
+
const rawFrom = operation.anchor.from + delta;
|
|
649
|
+
const rawTo = rawFrom + operation.after.changedText.length;
|
|
650
|
+
const from = boundaryMap[rawFrom];
|
|
651
|
+
const to = boundaryMap[rawTo];
|
|
652
|
+
if (
|
|
653
|
+
from === undefined ||
|
|
654
|
+
to === undefined ||
|
|
655
|
+
draft.slice(from, to) !== operation.after.changedText
|
|
656
|
+
)
|
|
657
|
+
throw new SuggestionFormattingMappingError();
|
|
562
658
|
delta +=
|
|
563
659
|
operation.after.changedText.length - operation.before.changedText.length;
|
|
564
660
|
return {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import { docToNfm, nfmToDoc, type PMNode } from "./nfm";
|
|
1
|
+
import { canonicalizeNfm, docToNfm, nfmToDoc, type PMNode } from "./nfm";
|
|
2
2
|
import { suggestionFormattingSourceSlice } from "./suggestion-formatting";
|
|
3
|
+
import { resolveMarkdownSuggestionRange } from "./suggestion-rebase";
|
|
3
4
|
|
|
4
5
|
export type SuggestionPresentationContext = {
|
|
5
6
|
source: string;
|
|
@@ -177,10 +178,25 @@ export function suggestionTextPresentationForSource(
|
|
|
177
178
|
): SuggestionPresentationNode[] | null {
|
|
178
179
|
if (context.source.slice(context.from, context.to) !== content) return null;
|
|
179
180
|
if (content.length === 0 && context.from === context.to) return [];
|
|
181
|
+
const canonicalSource = canonicalizeNfm(context.source);
|
|
182
|
+
const canonicalRange = resolveMarkdownSuggestionRange(canonicalSource, {
|
|
183
|
+
before: { markdown: context.source, changedText: content },
|
|
184
|
+
after: { markdown: context.source, changedText: content },
|
|
185
|
+
anchor: {
|
|
186
|
+
from: context.from,
|
|
187
|
+
to: context.to,
|
|
188
|
+
prefix: context.source.slice(
|
|
189
|
+
Math.max(0, context.from - 32),
|
|
190
|
+
context.from,
|
|
191
|
+
),
|
|
192
|
+
suffix: context.source.slice(context.to, context.to + 32),
|
|
193
|
+
},
|
|
194
|
+
});
|
|
195
|
+
if (!canonicalRange) return null;
|
|
180
196
|
const parts = suggestionFormattingSourceSlice(
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
197
|
+
canonicalSource,
|
|
198
|
+
canonicalRange.from,
|
|
199
|
+
canonicalRange.to,
|
|
184
200
|
);
|
|
185
201
|
if (!parts) return null;
|
|
186
202
|
const whitespaceOnly =
|
|
@@ -1259,13 +1259,25 @@ export const editorChromeBridgeScript: string = `"use strict";
|
|
|
1259
1259
|
dist: true,
|
|
1260
1260
|
build: true,
|
|
1261
1261
|
".next": true,
|
|
1262
|
-
public: true
|
|
1262
|
+
public: true,
|
|
1263
|
+
".vite": true
|
|
1263
1264
|
};
|
|
1264
|
-
|
|
1265
|
+
var PROVENANCE_REACT_RUNTIME_MODULE_RE = /^(?:react|(?:react[-_])?jsx(?:-dev)?-runtime)(?:\\.development|\\.production(?:\\.min)?)?\\.(?:m?js|cjs)$/;
|
|
1266
|
+
var PROVENANCE_VITE_DEPS_SEGMENT_RE = /^deps(?:_|$)/;
|
|
1267
|
+
function isProvenanceNoisePath(path, localServedOutput) {
|
|
1265
1268
|
var segments = path.split("/");
|
|
1269
|
+
for (var i = 0; i < segments.length - 1; i += 1) {
|
|
1270
|
+
if (PROVENANCE_VITE_DEPS_SEGMENT_RE.test(segments[i]) && PROVENANCE_REACT_RUNTIME_MODULE_RE.test(segments[i + 1])) {
|
|
1271
|
+
return true;
|
|
1272
|
+
}
|
|
1273
|
+
}
|
|
1266
1274
|
for (var i = 0; i < segments.length; i += 1) {
|
|
1267
|
-
|
|
1268
|
-
if (
|
|
1275
|
+
var segment = segments[i];
|
|
1276
|
+
if (localServedOutput && (segment === "dist" || segment === "build")) {
|
|
1277
|
+
continue;
|
|
1278
|
+
}
|
|
1279
|
+
if (PROVENANCE_NOISE_SEGMENTS[segment]) return true;
|
|
1280
|
+
if (segment === "_next" && segments[i + 1] === "static") return true;
|
|
1269
1281
|
}
|
|
1270
1282
|
return false;
|
|
1271
1283
|
}
|
|
@@ -1299,7 +1311,7 @@ export const editorChromeBridgeScript: string = `"use strict";
|
|
|
1299
1311
|
if (!match) return null;
|
|
1300
1312
|
var resolved = resolveProvenanceFrameUrl(match[2]);
|
|
1301
1313
|
if (!resolved) return null;
|
|
1302
|
-
if (
|
|
1314
|
+
if (isProvenanceNoisePath(resolved.sourceFile, resolved.localServedOutput)) {
|
|
1303
1315
|
return null;
|
|
1304
1316
|
}
|
|
1305
1317
|
var line = parseInt(match[3], 10);
|
|
@@ -17,13 +17,25 @@ export const sourceLocationBridgeScript: string = `"use strict";
|
|
|
17
17
|
dist: true,
|
|
18
18
|
build: true,
|
|
19
19
|
".next": true,
|
|
20
|
-
public: true
|
|
20
|
+
public: true,
|
|
21
|
+
".vite": true
|
|
21
22
|
};
|
|
22
|
-
|
|
23
|
+
var REACT_RUNTIME_MODULE_RE = /^(?:react|(?:react[-_])?jsx(?:-dev)?-runtime)(?:\\.development|\\.production(?:\\.min)?)?\\.(?:m?js|cjs)$/;
|
|
24
|
+
var VITE_DEPS_SEGMENT_RE = /^deps(?:_|$)/;
|
|
25
|
+
function isNoisePath(path, localServedOutput) {
|
|
23
26
|
var segments = path.split("/");
|
|
27
|
+
for (var i = 0; i < segments.length - 1; i += 1) {
|
|
28
|
+
if (VITE_DEPS_SEGMENT_RE.test(segments[i]) && REACT_RUNTIME_MODULE_RE.test(segments[i + 1])) {
|
|
29
|
+
return true;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
24
32
|
for (var i = 0; i < segments.length; i += 1) {
|
|
25
|
-
|
|
26
|
-
if (
|
|
33
|
+
var segment = segments[i];
|
|
34
|
+
if (localServedOutput && (segment === "dist" || segment === "build")) {
|
|
35
|
+
continue;
|
|
36
|
+
}
|
|
37
|
+
if (NOISE_SEGMENTS[segment]) return true;
|
|
38
|
+
if (segment === "_next" && segments[i + 1] === "static") return true;
|
|
27
39
|
}
|
|
28
40
|
return false;
|
|
29
41
|
}
|
|
@@ -54,7 +66,7 @@ export const sourceLocationBridgeScript: string = `"use strict";
|
|
|
54
66
|
var rawUrl = match[2];
|
|
55
67
|
var resolved = resolveFrameUrl(rawUrl);
|
|
56
68
|
if (!resolved) return null;
|
|
57
|
-
if (
|
|
69
|
+
if (isNoisePath(resolved.sourceFile, resolved.localServedOutput)) {
|
|
58
70
|
return null;
|
|
59
71
|
}
|
|
60
72
|
var lineNumber = Number(match[3]);
|
|
@@ -3,6 +3,58 @@
|
|
|
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-16
|
|
7
|
+
|
|
8
|
+
### Fixed
|
|
9
|
+
|
|
10
|
+
- A message that only mentions a design, or pastes Design's own guidance back into chat, no longer ends with a false "couldn't confirm that a Design artifact was saved" reply.
|
|
11
|
+
|
|
12
|
+
## 2026-09-13
|
|
13
|
+
|
|
14
|
+
### Fixed
|
|
15
|
+
|
|
16
|
+
- Connect Code imports explain when a design-system name is already in use
|
|
17
|
+
- Design captures are visible only to editors
|
|
18
|
+
- Design generation no longer stays active when a completion event is lost
|
|
19
|
+
- Design MCP servers now render screen exports as PNGs
|
|
20
|
+
- Design screen exports work without system-installed Chrome
|
|
21
|
+
- Fixed screen exports for designs shared with viewers
|
|
22
|
+
- Source-file actions return not-found when a design is missing or inaccessible
|
|
23
|
+
- Undo works immediately after editing a size field in the inspector
|
|
24
|
+
|
|
25
|
+
### Security
|
|
26
|
+
|
|
27
|
+
- Public Design links no longer expose captured state data
|
|
28
|
+
|
|
29
|
+
## 2026-09-12
|
|
30
|
+
|
|
31
|
+
### Fixed
|
|
32
|
+
|
|
33
|
+
- A generated design now gets a prompt-based title when no title comes back.
|
|
34
|
+
- Canvas panning stays proportional at every zoom level.
|
|
35
|
+
- Closed shapes and pen paths support inside, center, and outside stroke alignment.
|
|
36
|
+
- Custom breakpoints can be added after Tablet, and Enter applies their width.
|
|
37
|
+
- Deselecting a text layer clears the leftover text highlight.
|
|
38
|
+
- Design generation no longer stays stuck when an agent run ends without its final chat update.
|
|
39
|
+
- Edit with AI shows the selected layer name and uses the current theme for its hover highlight.
|
|
40
|
+
- Gradient fills stay gradients when you edit them.
|
|
41
|
+
- Grouped layers are easier to edit: click selects the group, double-click opens a child, and paste preserves group placement.
|
|
42
|
+
- Interact mode keeps its toolbar and height controls within the available canvas.
|
|
43
|
+
- The inspector's export Preview now renders a thumbnail when a whole screen or
|
|
44
|
+
frame is selected, instead of always reporting "Preview unavailable" next to a
|
|
45
|
+
working Export button.
|
|
46
|
+
- The Questions panel no longer overlaps the navigation rail.
|
|
47
|
+
|
|
48
|
+
## 2026-09-11
|
|
49
|
+
|
|
50
|
+
### Fixed
|
|
51
|
+
|
|
52
|
+
- Moving the pointer over the canvas no longer takes focus away from a composer
|
|
53
|
+
or comment box that just opened, so you can type into it straight away.
|
|
54
|
+
- Screens no longer creep taller each time a selection handle reaches the bottom
|
|
55
|
+
edge of a frame — the editor's own overlays are no longer measured as page
|
|
56
|
+
content.
|
|
57
|
+
|
|
6
58
|
## 2026-09-10
|
|
7
59
|
|
|
8
60
|
### Added
|
|
@@ -15,6 +67,14 @@ from the command menu (Cmd+K → "What's new") or from Settings.
|
|
|
15
67
|
- Opening or closing the left sidebar (or toggling minimal mode) no longer shifts designs on the canvas.
|
|
16
68
|
- Breakpoint add control keeps a clear gap from the screen edge and shows immediate feedback while adding.
|
|
17
69
|
|
|
70
|
+
### Fixed
|
|
71
|
+
|
|
72
|
+
- Clicking a button or other painted text element on the canvas selects that
|
|
73
|
+
element, not the wrapper the editor puts around its text — so its corner
|
|
74
|
+
radius, fill and component props are editable again.
|
|
75
|
+
- Exported SVGs no longer break on designs that use Alpine list or conditional blocks.
|
|
76
|
+
- PNG and PDF exports now render text with the design's own webfonts, so highlights, gradients and underlines stay aligned with the text.
|
|
77
|
+
|
|
18
78
|
### Changed
|
|
19
79
|
|
|
20
80
|
- Design canvas chrome now overlays instead of resizing the board, so opening or closing the left panel and minimal mode no longer shifts screens on the canvas.
|
|
@@ -642,13 +642,45 @@ declare var __INITIAL_SOURCE_HEAD__: string;
|
|
|
642
642
|
build: true,
|
|
643
643
|
".next": true,
|
|
644
644
|
public: true,
|
|
645
|
+
".vite": true,
|
|
645
646
|
};
|
|
646
647
|
|
|
647
|
-
|
|
648
|
+
// React 19 captures _debugStack inside the JSX runtime itself, so its
|
|
649
|
+
// module is always the top frame; recognised by a runtime module name
|
|
650
|
+
// INSIDE a Vite optimizer deps directory (`deps`/`deps_ssr`/`deps_temp_*`
|
|
651
|
+
// — the optimizer always writes pre-bundles there, even under a custom
|
|
652
|
+
// cacheDir with no node_modules segment) — never by basename alone, since
|
|
653
|
+
// an authored file that happens to be named react.js or jsx-runtime.js
|
|
654
|
+
// outside a deps directory is a real local file.
|
|
655
|
+
var PROVENANCE_REACT_RUNTIME_MODULE_RE =
|
|
656
|
+
/^(?:react|(?:react[-_])?jsx(?:-dev)?-runtime)(?:\.development|\.production(?:\.min)?)?\.(?:m?js|cjs)$/;
|
|
657
|
+
var PROVENANCE_VITE_DEPS_SEGMENT_RE = /^deps(?:_|$)/;
|
|
658
|
+
|
|
659
|
+
// localServedOutput (a /@fs/ frame) exempts dist/build only — a locally
|
|
660
|
+
// built package is a real local file. node_modules stays noise even
|
|
661
|
+
// through /@fs/: Vite resolves symlinks, so a linked workspace package
|
|
662
|
+
// never carries a node_modules segment, and third-party code always
|
|
663
|
+
// arrives here by its real path.
|
|
664
|
+
function isProvenanceNoisePath(
|
|
665
|
+
path: string,
|
|
666
|
+
localServedOutput: boolean,
|
|
667
|
+
): boolean {
|
|
648
668
|
var segments = path.split("/");
|
|
669
|
+
for (var i = 0; i < segments.length - 1; i += 1) {
|
|
670
|
+
if (
|
|
671
|
+
PROVENANCE_VITE_DEPS_SEGMENT_RE.test(segments[i]!) &&
|
|
672
|
+
PROVENANCE_REACT_RUNTIME_MODULE_RE.test(segments[i + 1]!)
|
|
673
|
+
) {
|
|
674
|
+
return true;
|
|
675
|
+
}
|
|
676
|
+
}
|
|
649
677
|
for (var i = 0; i < segments.length; i += 1) {
|
|
650
|
-
|
|
651
|
-
if (
|
|
678
|
+
var segment = segments[i]!;
|
|
679
|
+
if (localServedOutput && (segment === "dist" || segment === "build")) {
|
|
680
|
+
continue;
|
|
681
|
+
}
|
|
682
|
+
if (PROVENANCE_NOISE_SEGMENTS[segment]) return true;
|
|
683
|
+
if (segment === "_next" && segments[i + 1] === "static") return true;
|
|
652
684
|
}
|
|
653
685
|
return false;
|
|
654
686
|
}
|
|
@@ -710,11 +742,11 @@ declare var __INITIAL_SOURCE_HEAD__: string;
|
|
|
710
742
|
var resolved = resolveProvenanceFrameUrl(match[2]!);
|
|
711
743
|
if (!resolved) return null;
|
|
712
744
|
// A Vite /@fs/ frame is a real local file even when its path contains
|
|
713
|
-
// dist/ or node_modules
|
|
714
|
-
//
|
|
745
|
+
// dist/ or build/ — but node_modules stays noise even through /@fs/, so a
|
|
746
|
+
// classic-transform createElement frame served from a symlinked
|
|
747
|
+
// dependency (react.development.js) never becomes element provenance.
|
|
715
748
|
if (
|
|
716
|
-
|
|
717
|
-
isProvenanceNoisePath(resolved.sourceFile)
|
|
749
|
+
isProvenanceNoisePath(resolved.sourceFile, resolved.localServedOutput)
|
|
718
750
|
) {
|
|
719
751
|
return null;
|
|
720
752
|
}
|
|
@@ -76,13 +76,42 @@
|
|
|
76
76
|
build: true,
|
|
77
77
|
".next": true,
|
|
78
78
|
public: true,
|
|
79
|
+
".vite": true,
|
|
79
80
|
};
|
|
80
81
|
|
|
81
|
-
|
|
82
|
+
// React 19 captures _debugStack inside the JSX runtime itself, so its
|
|
83
|
+
// module is always the top frame; recognised by a runtime module name
|
|
84
|
+
// INSIDE a Vite optimizer deps directory (`deps`/`deps_ssr`/`deps_temp_*`
|
|
85
|
+
// — the optimizer always writes pre-bundles there, even under a custom
|
|
86
|
+
// cacheDir with no node_modules segment) — never by basename alone, since
|
|
87
|
+
// an authored file that happens to be named react.js or jsx-runtime.js
|
|
88
|
+
// outside a deps directory is a real local file.
|
|
89
|
+
var REACT_RUNTIME_MODULE_RE =
|
|
90
|
+
/^(?:react|(?:react[-_])?jsx(?:-dev)?-runtime)(?:\.development|\.production(?:\.min)?)?\.(?:m?js|cjs)$/;
|
|
91
|
+
var VITE_DEPS_SEGMENT_RE = /^deps(?:_|$)/;
|
|
92
|
+
|
|
93
|
+
// localServedOutput (a /@fs/ frame) exempts dist/build only — a locally
|
|
94
|
+
// built package is a real local file. node_modules stays noise even
|
|
95
|
+
// through /@fs/: Vite resolves symlinks, so a linked workspace package
|
|
96
|
+
// never carries a node_modules segment, and third-party code always
|
|
97
|
+
// arrives here by its real path.
|
|
98
|
+
function isNoisePath(path: string, localServedOutput: boolean): boolean {
|
|
82
99
|
var segments = path.split("/");
|
|
100
|
+
for (var i = 0; i < segments.length - 1; i += 1) {
|
|
101
|
+
if (
|
|
102
|
+
VITE_DEPS_SEGMENT_RE.test(segments[i]!) &&
|
|
103
|
+
REACT_RUNTIME_MODULE_RE.test(segments[i + 1]!)
|
|
104
|
+
) {
|
|
105
|
+
return true;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
83
108
|
for (var i = 0; i < segments.length; i += 1) {
|
|
84
|
-
|
|
85
|
-
if (
|
|
109
|
+
var segment = segments[i]!;
|
|
110
|
+
if (localServedOutput && (segment === "dist" || segment === "build")) {
|
|
111
|
+
continue;
|
|
112
|
+
}
|
|
113
|
+
if (NOISE_SEGMENTS[segment]) return true;
|
|
114
|
+
if (segment === "_next" && segments[i + 1] === "static") return true;
|
|
86
115
|
}
|
|
87
116
|
return false;
|
|
88
117
|
}
|
|
@@ -130,7 +159,11 @@
|
|
|
130
159
|
var rawUrl = match[2]!;
|
|
131
160
|
var resolved = resolveFrameUrl(rawUrl);
|
|
132
161
|
if (!resolved) return null;
|
|
133
|
-
|
|
162
|
+
// A Vite /@fs/ frame is a real local file even when its path contains
|
|
163
|
+
// dist/ or build/ — but node_modules stays noise even through /@fs/, so a
|
|
164
|
+
// classic-transform createElement frame served from a symlinked
|
|
165
|
+
// dependency (react.development.js) never becomes element provenance.
|
|
166
|
+
if (isNoisePath(resolved.sourceFile, resolved.localServedOutput)) {
|
|
134
167
|
return null;
|
|
135
168
|
}
|
|
136
169
|
var lineNumber = Number(match[3]);
|
|
@@ -34,14 +34,32 @@ const NOISE_SEGMENTS = new Set([
|
|
|
34
34
|
"build",
|
|
35
35
|
".next",
|
|
36
36
|
"public",
|
|
37
|
+
".vite",
|
|
37
38
|
]);
|
|
38
39
|
|
|
39
40
|
// V8 stack frame: "at Name (url:line:col)" or the anonymous "at url:line:col".
|
|
40
41
|
const STACK_FRAME_RE =
|
|
41
42
|
/^\s*at\s+(?:([^\s(]+)\s+\()?([^()\s][^()]*?):(\d+):(\d+)\)?\s*$/;
|
|
42
43
|
|
|
44
|
+
// React 19 captures _debugStack inside the JSX runtime itself, so its
|
|
45
|
+
// module is always the top frame; recognised by a runtime module name
|
|
46
|
+
// INSIDE a Vite optimizer deps directory (`deps`/`deps_ssr`/`deps_temp_*` —
|
|
47
|
+
// the optimizer always writes pre-bundles there, even under a custom
|
|
48
|
+
// cacheDir with no node_modules segment) — never by basename alone, since an
|
|
49
|
+
// authored file that happens to be named react.js or jsx-runtime.js outside
|
|
50
|
+
// a deps directory is a real local file.
|
|
51
|
+
const REACT_RUNTIME_MODULE_RE =
|
|
52
|
+
/^(?:react|(?:react[-_])?jsx(?:-dev)?-runtime)(?:\.development|\.production(?:\.min)?)?\.(?:m?js|cjs)$/;
|
|
53
|
+
const VITE_DEPS_SEGMENT_RE = /^deps(?:_|$)/;
|
|
54
|
+
|
|
43
55
|
function isNoisePath(path: string): boolean {
|
|
44
56
|
const segments = path.split("/");
|
|
57
|
+
const runtimeInsideDepsDir = segments.some(
|
|
58
|
+
(segment, index) =>
|
|
59
|
+
VITE_DEPS_SEGMENT_RE.test(segment) &&
|
|
60
|
+
REACT_RUNTIME_MODULE_RE.test(segments[index + 1] ?? ""),
|
|
61
|
+
);
|
|
62
|
+
if (runtimeInsideDepsDir) return true;
|
|
45
63
|
if (segments.some((segment) => NOISE_SEGMENTS.has(segment))) return true;
|
|
46
64
|
const nextIndex = segments.indexOf("_next");
|
|
47
65
|
return nextIndex >= 0 && segments[nextIndex + 1] === "static";
|
|
@@ -3,20 +3,39 @@
|
|
|
3
3
|
All notable user-facing changes to Chat are documented here. Open it any
|
|
4
4
|
time from the command menu (Cmd+K → "What's new").
|
|
5
5
|
|
|
6
|
+
## 2026-09-11
|
|
7
|
+
|
|
8
|
+
### Improved
|
|
9
|
+
|
|
10
|
+
- The Audit tab shows pull request numbers beside GitHub item titles, collapses long summaries with view more, and uses a clear Open thread button.
|
|
11
|
+
- GitHub inbox rows now show source, pull request number, and author on one line; Slack list rows resolve stored member names instead of raw ids, and Slack detail drops the duplicate heading once the thread loads.
|
|
12
|
+
- The Inbox list now shows separate Routing and Automation columns so babysit and Slack workflow progress is visible beside escalation status.
|
|
13
|
+
- PR babysitting now follows unresolved bot review feedback, defers while Builder is active, and shows recommendation, decision, and because on the Audit tab.
|
|
14
|
+
|
|
6
15
|
## 2026-09-10
|
|
7
16
|
|
|
8
17
|
### Improved
|
|
9
18
|
|
|
10
19
|
- Slack feedback marks clear bugs with 👀 again and requires that reaction before dispatching Builder.
|
|
11
20
|
|
|
21
|
+
### Fixed
|
|
22
|
+
|
|
23
|
+
- Factory automations keep display names, Slack channels, and author filters after poll runs and metadata repair.
|
|
24
|
+
- Factory PR babysitting now nudges Builder on Clips, Design, and Content pull requests instead of parking them as owner-managed.
|
|
25
|
+
|
|
12
26
|
## 2026-09-09
|
|
13
27
|
|
|
28
|
+
### Added
|
|
29
|
+
|
|
30
|
+
- A pull request that another request cannot unblock is now parked as stuck for a human to look at, rather than asked again on the next round.
|
|
31
|
+
|
|
14
32
|
### Improved
|
|
15
33
|
|
|
16
34
|
- Slack feedback jobs skip threads whose parent already has 👀 or 🤖, instead of tagging Builder again.
|
|
17
35
|
|
|
18
36
|
### Fixed
|
|
19
37
|
|
|
38
|
+
- Factory asks Builder to fix pull-request feedback once per round of work, instead of repeating the request each time GitHub recalculates whether the branch merges cleanly.
|
|
20
39
|
- Slack feedback keeps a claimed 👀/🤖 marker after polling, so those threads stay out of the review queue.
|
|
21
40
|
- Slack feedback no longer moves an already-started thread to needs-manual just because the parent already has 👀 or 🤖.
|
|
22
41
|
|
|
@@ -3,6 +3,12 @@
|
|
|
3
3
|
All notable user-facing changes to Agent-Native Forms are documented here. Open it any
|
|
4
4
|
time from the command menu (Cmd+K → "What's new").
|
|
5
5
|
|
|
6
|
+
## 2026-09-12
|
|
7
|
+
|
|
8
|
+
### Fixed
|
|
9
|
+
|
|
10
|
+
- Creating a form no longer dead-ends when a field is missing its type; the agent is told exactly which field and which property to correct.
|
|
11
|
+
|
|
6
12
|
## 2026-08-25
|
|
7
13
|
|
|
8
14
|
### Fixed
|
|
@@ -3,6 +3,40 @@
|
|
|
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-12
|
|
7
|
+
|
|
8
|
+
### Improved
|
|
9
|
+
|
|
10
|
+
- Inbox tabs are easier to distinguish
|
|
11
|
+
- New-message compose opens in the main workspace by default
|
|
12
|
+
|
|
13
|
+
### Fixed
|
|
14
|
+
|
|
15
|
+
- Closing a draft preserves its saved account and backend so recovery actions affect the correct mailbox.
|
|
16
|
+
- Harden draft close recovery and keyboard interaction states
|
|
17
|
+
- Keep mail autocomplete accessibility references valid while suggestions close or filter
|
|
18
|
+
- Mail keeps saved drafts on their owning backend and preserves them through Send Undo and close-all.
|
|
19
|
+
- Saved drafts stay in their original mailbox when reopened, autosaved, or discarded.
|
|
20
|
+
- Send status now follows provider results, undo stays available only before dispatch, and draft-save failures are visible. Closing drafts no longer claims a save is complete before persistence, and deleting a draft targets the draft endpoint.
|
|
21
|
+
|
|
22
|
+
## 2026-09-11
|
|
23
|
+
|
|
24
|
+
### Fixed
|
|
25
|
+
|
|
26
|
+
- Inbox tabs now load from a synced local index: counts match the rows shown, custom label and filter tabs only show unarchived mail, and the inbox stays fast under Gmail rate limits.
|
|
27
|
+
- Inline images in Gmail emails display reliably, including embedded image data
|
|
28
|
+
- Nested Gmail labels now render under their parent labels in the label list.
|
|
29
|
+
- On mobile the sidebar shows a close button instead of a pin control that did nothing.
|
|
30
|
+
- Opening a label no longer fails with a 502 while Gmail is rate limiting; the app now reports the brief pause with a retry time.
|
|
31
|
+
- The inbox tab bar stays on one line and scrolls instead of wrapping.
|
|
32
|
+
|
|
33
|
+
## 2026-09-10
|
|
34
|
+
|
|
35
|
+
### Fixed
|
|
36
|
+
|
|
37
|
+
- Attachments use your connected Builder.io storage in hosted Mail
|
|
38
|
+
- Fixed Google sign-in being blocked with an 'unverified password account' error for workspace users provisioned through cross-app SSO.
|
|
39
|
+
|
|
6
40
|
## 2026-09-09
|
|
7
41
|
|
|
8
42
|
### Improved
|
|
@@ -3,6 +3,20 @@
|
|
|
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-12
|
|
7
|
+
|
|
8
|
+
### Fixed
|
|
9
|
+
|
|
10
|
+
- Multi-object rotation keeps translated objects aligned
|
|
11
|
+
- Objects keep their position when ungrouping a rotated group.
|
|
12
|
+
- Preserve scaled child transforms and fractional matrix angles when ungrouping rotated groups
|
|
13
|
+
|
|
14
|
+
## 2026-09-10
|
|
15
|
+
|
|
16
|
+
### Fixed
|
|
17
|
+
|
|
18
|
+
- When Google Slides export falls back to downloading a .pptx, it now says so instead of offering a button that opens an empty Google Slides tab.
|
|
19
|
+
|
|
6
20
|
## 2026-09-09
|
|
7
21
|
|
|
8
22
|
### Improved
|
|
@@ -12,6 +26,7 @@ time from the command menu (Cmd+K → "What's new").
|
|
|
12
26
|
|
|
13
27
|
### Fixed
|
|
14
28
|
|
|
29
|
+
- Google Slides export is now marked unavailable instead of sending you to a broken Google sign-in page when the Google connection cannot be used.
|
|
15
30
|
- When an agent's slide edit does not match the slide, it now says so instead of reporting an internal server error, so the agent can correct the edit rather than retrying the same one.
|
|
16
31
|
|
|
17
32
|
## 2026-09-03
|
|
@@ -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
|
-
ok?: undefined;
|
|
52
51
|
error: string;
|
|
52
|
+
ok?: undefined;
|
|
53
53
|
} | {
|
|
54
54
|
error?: undefined;
|
|
55
55
|
ok: boolean;
|
package/dist/secrets/routes.d.ts
CHANGED
|
@@ -56,17 +56,17 @@ export declare function createWriteSecretHandler(): import("h3").EventHandlerWit
|
|
|
56
56
|
ok?: undefined;
|
|
57
57
|
status?: undefined;
|
|
58
58
|
} | {
|
|
59
|
-
error?: undefined;
|
|
60
59
|
ok: boolean;
|
|
61
60
|
status: string;
|
|
61
|
+
error?: undefined;
|
|
62
62
|
} | {
|
|
63
63
|
ok?: undefined;
|
|
64
64
|
error: string;
|
|
65
65
|
removed?: undefined;
|
|
66
66
|
} | {
|
|
67
|
-
error?: undefined;
|
|
68
67
|
ok: boolean;
|
|
69
68
|
removed: boolean;
|
|
69
|
+
error?: undefined;
|
|
70
70
|
}>>;
|
|
71
71
|
/**
|
|
72
72
|
* POST /_agent-native/secrets/:key/test — validate an optional candidate value
|
|
@@ -77,13 +77,13 @@ export declare function createTestSecretHandler(): import("h3").EventHandlerWith
|
|
|
77
77
|
error: string;
|
|
78
78
|
note?: undefined;
|
|
79
79
|
} | {
|
|
80
|
-
error?: undefined;
|
|
81
80
|
ok: boolean;
|
|
82
81
|
note?: undefined;
|
|
83
|
-
} | {
|
|
84
82
|
error?: undefined;
|
|
83
|
+
} | {
|
|
85
84
|
ok: boolean;
|
|
86
85
|
note: string;
|
|
86
|
+
error?: undefined;
|
|
87
87
|
} | {
|
|
88
88
|
note?: undefined;
|
|
89
89
|
ok: boolean;
|
|
@@ -115,11 +115,11 @@ export interface AdHocSecretPayload {
|
|
|
115
115
|
export declare function createAdHocSecretHandler(): import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<AdHocSecretPayload[] | {
|
|
116
116
|
error: string;
|
|
117
117
|
} | {
|
|
118
|
-
error?: undefined;
|
|
119
118
|
ok: boolean;
|
|
120
119
|
key: string;
|
|
121
|
-
} | {
|
|
122
120
|
error?: undefined;
|
|
121
|
+
} | {
|
|
123
122
|
ok: boolean;
|
|
124
123
|
removed: boolean;
|
|
124
|
+
error?: undefined;
|
|
125
125
|
}>>;
|
|
@@ -34,10 +34,10 @@ export declare function resolveAgentEngineApiKeyWriteTarget(event: H3Event, scop
|
|
|
34
34
|
export declare function createAgentEngineApiKeyHandler(): import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<{
|
|
35
35
|
error: any;
|
|
36
36
|
} | {
|
|
37
|
+
error?: undefined;
|
|
37
38
|
ok: boolean;
|
|
38
39
|
key: string;
|
|
39
40
|
baseUrlKey?: string;
|
|
40
41
|
scope: AgentEngineApiKeyScope;
|
|
41
|
-
error?: undefined;
|
|
42
42
|
}>>;
|
|
43
43
|
export {};
|
|
@@ -59,8 +59,8 @@ export declare function createRealtimeTokenHandler(): import("h3").EventHandlerW
|
|
|
59
59
|
expiresAt?: undefined;
|
|
60
60
|
ttlSeconds?: undefined;
|
|
61
61
|
} | {
|
|
62
|
+
error?: undefined;
|
|
62
63
|
token: string;
|
|
63
64
|
expiresAt: string;
|
|
64
65
|
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.179.0
|
|
3
|
+
"version": "0.179.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": {
|
|
@@ -475,9 +475,9 @@
|
|
|
475
475
|
"yaml": "^2.9.0",
|
|
476
476
|
"yjs": "^13.6.32",
|
|
477
477
|
"zod": "^4.3.6",
|
|
478
|
-
"@agent-native/agentkit": "^0.2.0
|
|
479
|
-
"@agent-native/
|
|
480
|
-
"@agent-native/
|
|
478
|
+
"@agent-native/agentkit": "^0.2.0",
|
|
479
|
+
"@agent-native/toolkit": "^0.20.0",
|
|
480
|
+
"@agent-native/recap-cli": "0.5.30"
|
|
481
481
|
},
|
|
482
482
|
"devDependencies": {
|
|
483
483
|
"@ai-sdk/anthropic": "^3.0.71",
|