@dxos/react-ui-feed 0.12.0-next-73daef452f1464b62efdbca75fb0067c1066f264 → 0.13.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/dist/lib/chunk-src.mjs +162 -79
- package/dist/lib/chunk-src.mjs.map +1 -1
- package/dist/lib/debug.mjs +3 -3
- package/dist/lib/debug.mjs.map +1 -1
- package/dist/lib/testing.mjs +168 -150
- package/dist/lib/testing.mjs.map +1 -1
- package/dist/types/src/components/Block/MarkdownBlock.d.ts +8 -5
- package/dist/types/src/components/Block/MarkdownBlock.d.ts.map +1 -1
- package/dist/types/src/components/Block/diff-range.d.ts +12 -0
- package/dist/types/src/components/Block/diff-range.d.ts.map +1 -0
- package/dist/types/src/components/Block/diff-range.test.d.ts +2 -0
- package/dist/types/src/components/Block/diff-range.test.d.ts.map +1 -0
- package/dist/types/src/components/Block/extensions.d.ts +5 -3
- package/dist/types/src/components/Block/extensions.d.ts.map +1 -1
- package/dist/types/src/components/Block/index.d.ts +5 -5
- package/dist/types/src/components/Block/index.d.ts.map +1 -1
- package/dist/types/src/components/MessageList/MessageList.d.ts +22 -7
- package/dist/types/src/components/MessageList/MessageList.d.ts.map +1 -1
- package/dist/types/src/components/MessageList/MessageList.stories.d.ts +1 -1
- package/dist/types/src/components/MessageList/MessageList.stories.d.ts.map +1 -1
- package/dist/types/src/components/MessageList/index.d.ts +2 -2
- package/dist/types/src/components/MessageList/index.d.ts.map +1 -1
- package/dist/types/src/components/Minimap/Minimap.d.ts +2 -2
- package/dist/types/src/components/Minimap/Minimap.d.ts.map +1 -1
- package/dist/types/src/components/Minimap/index.d.ts +1 -1
- package/dist/types/src/components/Minimap/index.d.ts.map +1 -1
- package/dist/types/src/components/Outline/Outline.d.ts +2 -2
- package/dist/types/src/components/Outline/Outline.d.ts.map +1 -1
- package/dist/types/src/components/Outline/Outline.stories.d.ts +8 -1
- package/dist/types/src/components/Outline/Outline.stories.d.ts.map +1 -1
- package/dist/types/src/components/Outline/index.d.ts +1 -1
- package/dist/types/src/components/Outline/index.d.ts.map +1 -1
- package/dist/types/src/components/index.d.ts +4 -4
- package/dist/types/src/components/index.d.ts.map +1 -1
- package/dist/types/src/debug/Debug.d.ts +2 -2
- package/dist/types/src/debug/Debug.d.ts.map +1 -1
- package/dist/types/src/debug/Stats.d.ts +2 -2
- package/dist/types/src/debug/Stats.d.ts.map +1 -1
- package/dist/types/src/debug/index.d.ts +5 -5
- package/dist/types/src/debug/index.d.ts.map +1 -1
- package/dist/types/src/debug/useDebug.d.ts +1 -1
- package/dist/types/src/debug/useDebug.d.ts.map +1 -1
- package/dist/types/src/hooks/index.d.ts +3 -3
- package/dist/types/src/hooks/index.d.ts.map +1 -1
- package/dist/types/src/hooks/useFeedNavigation.d.ts +1 -1
- package/dist/types/src/hooks/useFeedNavigation.d.ts.map +1 -1
- package/dist/types/src/index.d.ts +3 -3
- package/dist/types/src/index.d.ts.map +1 -1
- package/dist/types/src/model/feed.d.ts +1 -1
- package/dist/types/src/model/feed.d.ts.map +1 -1
- package/dist/types/src/model/index.d.ts +3 -3
- package/dist/types/src/model/index.d.ts.map +1 -1
- package/dist/types/src/model/useListModel.d.ts +1 -1
- package/dist/types/src/model/useListModel.d.ts.map +1 -1
- package/dist/types/src/stories/baseline.stories.d.ts +1 -1
- package/dist/types/src/stories/baseline.stories.d.ts.map +1 -1
- package/dist/types/src/stories/bridge.stories.d.ts +1 -1
- package/dist/types/src/stories/bridge.stories.d.ts.map +1 -1
- package/dist/types/src/stories/construction.stories.d.ts.map +1 -1
- package/dist/types/src/stories/fill.stories.d.ts +1 -1
- package/dist/types/src/stories/fill.stories.d.ts.map +1 -1
- package/dist/types/src/stories/navigation.stories.d.ts +1 -1
- package/dist/types/src/stories/navigation.stories.d.ts.map +1 -1
- package/dist/types/src/stories/streaming.stories.d.ts +1 -1
- package/dist/types/src/stories/streaming.stories.d.ts.map +1 -1
- package/dist/types/src/stories/tail.stories.d.ts +1 -1
- package/dist/types/src/stories/tail.stories.d.ts.map +1 -1
- package/dist/types/src/stories/widget.stories.d.ts +1 -1
- package/dist/types/src/stories/widget.stories.d.ts.map +1 -1
- package/dist/types/src/testing/FeedStory.d.ts +1 -1
- package/dist/types/src/testing/FeedStory.d.ts.map +1 -1
- package/dist/types/src/testing/MessageWindow.d.ts +2 -2
- package/dist/types/src/testing/MessageWindow.d.ts.map +1 -1
- package/dist/types/src/testing/debug/FeedStats.d.ts +3 -3
- package/dist/types/src/testing/debug/FeedStats.d.ts.map +1 -1
- package/dist/types/src/testing/debug/index.d.ts +3 -3
- package/dist/types/src/testing/debug/index.d.ts.map +1 -1
- package/dist/types/src/testing/debug/use-feed-debug.d.ts +1 -1
- package/dist/types/src/testing/debug/use-feed-debug.d.ts.map +1 -1
- package/dist/types/src/testing/index.d.ts +9 -9
- package/dist/types/src/testing/index.d.ts.map +1 -1
- package/dist/types/src/testing/scenarios.d.ts +1 -1
- package/dist/types/src/testing/scenarios.d.ts.map +1 -1
- package/dist/types/src/testing/turn.d.ts +8 -2
- package/dist/types/src/testing/turn.d.ts.map +1 -1
- package/dist/types/src/testing/widgets.d.ts.map +1 -1
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +16 -16
- package/src/components/Block/MarkdownBlock.tsx +36 -17
- package/src/components/Block/block.stories.tsx +4 -4
- package/src/components/Block/diff-range.test.ts +33 -0
- package/src/components/Block/diff-range.ts +29 -0
- package/src/components/Block/extensions.ts +35 -6
- package/src/components/Block/index.ts +5 -5
- package/src/components/MessageList/MessageList.stories.tsx +1 -1
- package/src/components/MessageList/MessageList.tsx +215 -115
- package/src/components/MessageList/index.ts +2 -2
- package/src/components/Minimap/Minimap.stories.tsx +2 -2
- package/src/components/Minimap/Minimap.tsx +4 -4
- package/src/components/Minimap/index.ts +1 -1
- package/src/components/Outline/Outline.stories.tsx +47 -4
- package/src/components/Outline/Outline.tsx +19 -17
- package/src/components/Outline/index.ts +1 -1
- package/src/components/index.ts +4 -4
- package/src/debug/Debug.tsx +6 -6
- package/src/debug/Stats.tsx +4 -4
- package/src/debug/debug-model.test.ts +1 -1
- package/src/debug/frame-meter.test.ts +1 -1
- package/src/debug/index.ts +5 -5
- package/src/debug/useDebug.tsx +1 -1
- package/src/hooks/index.ts +3 -3
- package/src/hooks/useFeedNavigation.ts +1 -1
- package/src/index.ts +3 -3
- package/src/model/feed-model.test.ts +1 -1
- package/src/model/feed.test.ts +18 -1
- package/src/model/feed.ts +8 -1
- package/src/model/index.ts +3 -3
- package/src/model/useListModel.ts +1 -1
- package/src/stories/baseline.stories.tsx +1 -1
- package/src/stories/bridge.stories.tsx +6 -5
- package/src/stories/construction.stories.tsx +7 -3
- package/src/stories/fill.stories.tsx +1 -1
- package/src/stories/horizontal.stories.tsx +2 -2
- package/src/stories/mount.stories.tsx +5 -5
- package/src/stories/navigation.stories.tsx +1 -1
- package/src/stories/streaming.stories.tsx +1 -1
- package/src/stories/tail.stories.tsx +1 -1
- package/src/stories/widget.stories.tsx +1 -1
- package/src/testing/FeedStory.tsx +50 -34
- package/src/testing/MessageWindow.tsx +8 -10
- package/src/testing/controls.tsx +1 -1
- package/src/testing/debug/FeedStats.tsx +11 -10
- package/src/testing/debug/index.ts +3 -3
- package/src/testing/debug/use-feed-debug.ts +2 -2
- package/src/testing/index.ts +9 -9
- package/src/testing/scenarios.tsx +27 -33
- package/src/testing/turn.ts +54 -30
- package/src/testing/widgets.tsx +21 -20
package/dist/lib/chunk-src.mjs
CHANGED
|
@@ -4,11 +4,15 @@ import { EditorState, StateEffect, StateField } from "@codemirror/state";
|
|
|
4
4
|
import { Decoration, EditorView } from "@codemirror/view";
|
|
5
5
|
import DOMPurify from "dompurify";
|
|
6
6
|
import { createPortal } from "react-dom";
|
|
7
|
-
import
|
|
8
|
-
import { createBasicExtensions, createMarkdownExtensions, createThemeExtensions, decorateMarkdown, extendedMarkdown, xmlBlockDecoration, xmlFormatting, xmlTags } from "@dxos/ui-editor";
|
|
9
|
-
import { createContext as createContext$1 } from "@radix-ui/react-context";
|
|
10
|
-
import { ListModel, useFollow, useWindow } from "@dxos/react-ui-virtual";
|
|
7
|
+
import * as Hooks from "@dxos/react-ui/Hooks";
|
|
11
8
|
import { mx } from "@dxos/ui-theme";
|
|
9
|
+
import { createBasicExtensions, createMarkdownExtensions, createThemeExtensions, decorateMarkdown, extendedMarkdown, objectLinks, widgetHost, xmlBlockDecoration, xmlFormatting, xmlTags } from "@dxos/ui-editor";
|
|
10
|
+
import { ListModel, useFollow, useWindow, windowRowProps } from "@dxos/react-ui-virtual";
|
|
11
|
+
import * as Button from "@dxos/react-ui/Button";
|
|
12
|
+
import * as Layout from "@dxos/react-ui/Layout";
|
|
13
|
+
import * as ScrollArea from "@dxos/react-ui/ScrollArea";
|
|
14
|
+
import * as Util from "@dxos/react-ui/Util";
|
|
15
|
+
import * as Popover from "@dxos/react-ui/Popover";
|
|
12
16
|
//#region src/hooks/useDecorations.tsx
|
|
13
17
|
var DecorationsContext = createContext(/* @__PURE__ */ new Map());
|
|
14
18
|
/**
|
|
@@ -255,6 +259,24 @@ var HtmlBlock = memo(({ html }) => {
|
|
|
255
259
|
});
|
|
256
260
|
HtmlBlock.displayName = "HtmlBlock";
|
|
257
261
|
//#endregion
|
|
262
|
+
//#region src/components/Block/diff-range.ts
|
|
263
|
+
/**
|
|
264
|
+
* The smallest single replacement that turns `before` into `after`: the common prefix and suffix
|
|
265
|
+
* are left alone, so a document whose middle changed keeps the decorations either side of it.
|
|
266
|
+
*/
|
|
267
|
+
var diffRange = (before, after) => {
|
|
268
|
+
const limit = Math.min(before.length, after.length);
|
|
269
|
+
let prefix = 0;
|
|
270
|
+
while (prefix < limit && before.charCodeAt(prefix) === after.charCodeAt(prefix)) prefix++;
|
|
271
|
+
let suffix = 0;
|
|
272
|
+
while (suffix < limit - prefix && before.charCodeAt(before.length - 1 - suffix) === after.charCodeAt(after.length - 1 - suffix)) suffix++;
|
|
273
|
+
return {
|
|
274
|
+
from: prefix,
|
|
275
|
+
to: before.length - suffix,
|
|
276
|
+
insert: after.slice(prefix, after.length - suffix)
|
|
277
|
+
};
|
|
278
|
+
};
|
|
279
|
+
//#endregion
|
|
258
280
|
//#region src/components/Block/extensions.ts
|
|
259
281
|
/**
|
|
260
282
|
* The extension set one item's document is built from.
|
|
@@ -269,11 +291,14 @@ HtmlBlock.displayName = "HtmlBlock";
|
|
|
269
291
|
* Extensions are descriptors, not instances: a `StateField` or `ViewPlugin` in a shared array is
|
|
270
292
|
* still instantiated per view, so sharing changes nothing but the identity CodeMirror dedupes on.
|
|
271
293
|
*/
|
|
272
|
-
var createBlockExtensions = ({ registry, editable = false, themeMode = "light", setWidgets } = {}) => [...sharedExtensions(registry, editable, themeMode), ...registry ? [
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
})
|
|
294
|
+
var createBlockExtensions = ({ registry, objectImage, editable = false, themeMode = "light", setWidgets } = {}) => [...sharedExtensions(registry, editable, themeMode), ...registry ? [
|
|
295
|
+
widgetHost({
|
|
296
|
+
setWidgets,
|
|
297
|
+
bookmarks: ["prompt"]
|
|
298
|
+
}),
|
|
299
|
+
xmlTags({ registry }),
|
|
300
|
+
objectLinks({ image: objectImage })
|
|
301
|
+
] : []];
|
|
277
302
|
/** Registries are compared by identity, so a feed's single registry is a single cache scope. */
|
|
278
303
|
var NO_REGISTRY = {};
|
|
279
304
|
var cache = /* @__PURE__ */ new WeakMap();
|
|
@@ -298,17 +323,32 @@ var build = (registry, editable, themeMode) => [
|
|
|
298
323
|
editable,
|
|
299
324
|
lineWrapping: true
|
|
300
325
|
}),
|
|
301
|
-
createThemeExtensions({
|
|
326
|
+
createThemeExtensions({
|
|
327
|
+
themeMode,
|
|
328
|
+
syntaxHighlighting: true
|
|
329
|
+
}),
|
|
302
330
|
registry ? extendedMarkdown({ registry }) : createMarkdownExtensions(),
|
|
303
|
-
registry && xmlFormatting({ skip: ["prompt"] }),
|
|
331
|
+
registry && xmlFormatting({ skip: registry.prompt ? ["prompt"] : [] }),
|
|
304
332
|
decorateMarkdown(),
|
|
305
333
|
registry?.prompt && xmlBlockDecoration({
|
|
306
334
|
tag: "prompt",
|
|
307
335
|
hideTags: true
|
|
308
336
|
}),
|
|
309
337
|
highlights,
|
|
310
|
-
highlightTheme
|
|
338
|
+
highlightTheme,
|
|
339
|
+
blockScrollerTheme
|
|
311
340
|
].filter(Boolean);
|
|
341
|
+
/**
|
|
342
|
+
* An item's editor never scrolls on the block axis: it is auto-height and the feed around it is
|
|
343
|
+
* what scrolls, so its content fits by construction.
|
|
344
|
+
*
|
|
345
|
+
* Left to `auto`, that invariant is broken transiently by any widget that changes height — a tool
|
|
346
|
+
* block's disclosure animating open grows the content every frame while `requestMeasure` lands the
|
|
347
|
+
* editor's new height a frame later, so the scroller is a frame short throughout and paints a
|
|
348
|
+
* scrollbar for the length of the animation. The inline axis keeps `auto`; a wide payload still
|
|
349
|
+
* scrolls.
|
|
350
|
+
*/
|
|
351
|
+
var blockScrollerTheme = EditorView.theme({ ".cm-scroller": { overflowY: "hidden" } });
|
|
312
352
|
//#endregion
|
|
313
353
|
//#region src/components/Block/MarkdownBlock.tsx
|
|
314
354
|
/**
|
|
@@ -317,8 +357,8 @@ var build = (registry, editable, themeMode) => [
|
|
|
317
357
|
* Each item owns a document, so streaming appends and per-message editing stay local — where a
|
|
318
358
|
* single thread-wide document needs a cursor and a range table to know which message it is touching.
|
|
319
359
|
*/
|
|
320
|
-
var MarkdownBlock = memo(({ text, stream, editable = false, registry, hits, onWidgetsChange }) => {
|
|
321
|
-
const
|
|
360
|
+
var MarkdownBlock = memo(({ classNames, text, stream, editable = false, registry, objectImage, hits, onWidgetsChange }) => {
|
|
361
|
+
const themeMode = Hooks.useThemeMode();
|
|
322
362
|
const [view, setView] = useState(null);
|
|
323
363
|
const [widgets, setWidgets] = useState([]);
|
|
324
364
|
const rootRef = useRef(null);
|
|
@@ -327,6 +367,7 @@ var MarkdownBlock = memo(({ text, stream, editable = false, registry, hits, onWi
|
|
|
327
367
|
selectionGroupRef.current = selectionGroup;
|
|
328
368
|
const extensions = useMemo(() => [...createBlockExtensions({
|
|
329
369
|
registry,
|
|
370
|
+
objectImage,
|
|
330
371
|
editable,
|
|
331
372
|
themeMode,
|
|
332
373
|
setWidgets
|
|
@@ -335,19 +376,18 @@ var MarkdownBlock = memo(({ text, stream, editable = false, registry, hits, onWi
|
|
|
335
376
|
})], [
|
|
336
377
|
editable,
|
|
337
378
|
themeMode,
|
|
338
|
-
registry
|
|
379
|
+
registry,
|
|
380
|
+
objectImage
|
|
339
381
|
]);
|
|
340
382
|
const initialTextRef = useRef(text);
|
|
341
383
|
useLayoutEffect(() => {
|
|
342
384
|
const parent = rootRef.current;
|
|
343
385
|
if (!parent) return;
|
|
344
|
-
const instance = new EditorView({
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
})
|
|
350
|
-
});
|
|
386
|
+
const instance = new EditorView({ state: EditorState.create({
|
|
387
|
+
doc: initialTextRef.current,
|
|
388
|
+
extensions
|
|
389
|
+
}) });
|
|
390
|
+
parent.appendChild(instance.dom);
|
|
351
391
|
const unregister = selectionGroupRef.current.register(instance);
|
|
352
392
|
setView(instance);
|
|
353
393
|
return () => {
|
|
@@ -368,11 +408,7 @@ var MarkdownBlock = memo(({ text, stream, editable = false, registry, hits, onWi
|
|
|
368
408
|
cancelAnimationFrame(dripRaf.current);
|
|
369
409
|
dripRaf.current = 0;
|
|
370
410
|
dispatchedRef.current = text;
|
|
371
|
-
view.dispatch({ changes:
|
|
372
|
-
from: 0,
|
|
373
|
-
to: view.state.doc.length,
|
|
374
|
-
insert: text
|
|
375
|
-
} });
|
|
411
|
+
view.dispatch({ changes: diffRange(view.state.doc.toString(), text) });
|
|
376
412
|
return;
|
|
377
413
|
}
|
|
378
414
|
if (!stream) {
|
|
@@ -440,7 +476,10 @@ var MarkdownBlock = memo(({ text, stream, editable = false, registry, hits, onWi
|
|
|
440
476
|
for (const { root } of widgets) observer.observe(root);
|
|
441
477
|
return () => observer.disconnect();
|
|
442
478
|
}, [view, widgets]);
|
|
443
|
-
return /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx("div", {
|
|
479
|
+
return /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx("div", {
|
|
480
|
+
className: mx(classNames),
|
|
481
|
+
ref: rootRef
|
|
482
|
+
}), widgets.map(({ Component, root, id, props }) => /* @__PURE__ */ jsx("div", { children: createPortal(/* @__PURE__ */ jsx(Component, {
|
|
444
483
|
view: view ?? void 0,
|
|
445
484
|
...props
|
|
446
485
|
}), root) }, id))] });
|
|
@@ -833,16 +872,20 @@ var usePositionLog = () => {
|
|
|
833
872
|
};
|
|
834
873
|
//#endregion
|
|
835
874
|
//#region src/components/MessageList/MessageList.tsx
|
|
836
|
-
var
|
|
875
|
+
var MESSAGE_LIST_NAME = "MessageList";
|
|
876
|
+
var [MessageListProvider, useMessageListContext] = Hooks.createContext(MESSAGE_LIST_NAME);
|
|
877
|
+
var [MessageListItemProvider, useMessageListItemContext] = Hooks.createContext(MESSAGE_LIST_NAME);
|
|
837
878
|
/**
|
|
838
879
|
* The list's state and scroll controls, for parts that live outside the viewport — a toolbar's
|
|
839
880
|
* find-next, a statusbar's range readout.
|
|
840
881
|
*/
|
|
841
882
|
var useMessageList = (consumerName = "useMessageList") => {
|
|
842
|
-
const { range, currentIndex, navigation, mountedRows, mountedWidgets, jumps, shifts, breaks, resetShifts, model, scrollToIndex, scrollToBottom } = useMessageListContext(consumerName);
|
|
883
|
+
const { range, currentIndex, atEnd, following, navigation, mountedRows, mountedWidgets, jumps, shifts, breaks, resetShifts, model, scrollToIndex, scrollToBottom } = useMessageListContext(consumerName);
|
|
843
884
|
return {
|
|
844
885
|
range,
|
|
845
886
|
currentIndex,
|
|
887
|
+
atEnd,
|
|
888
|
+
following,
|
|
846
889
|
navigation,
|
|
847
890
|
mountedRows,
|
|
848
891
|
mountedWidgets,
|
|
@@ -864,7 +907,7 @@ var DEFAULT_ESTIMATE = 120;
|
|
|
864
907
|
* Renders no DOM of its own, so a toolbar or statusbar that reads its state can sit outside the
|
|
865
908
|
* scroll container — the scrolling part is `MessageList.Viewport`.
|
|
866
909
|
*/
|
|
867
|
-
var MessageListRoot = ({ children, model, renderer = defaultRenderer, registry, Chrome = DefaultChrome, Custom, debug, estimateSize = DEFAULT_ESTIMATE, stickyBottom = false, tailLines = 0, overscan = 8, onRangeChange, controllerRef }) => {
|
|
910
|
+
var MessageListRoot = ({ children, model, renderer = defaultRenderer, registry, objectImage, Chrome = DefaultChrome, Custom, debug, estimateSize = DEFAULT_ESTIMATE, stickyBottom = false, tailLines = 0, overscan = 8, onRangeChange, controllerRef }) => {
|
|
868
911
|
const widgetStore = useMemo(createWidgetStateStore, []);
|
|
869
912
|
const [viewport, setViewportState] = useState(null);
|
|
870
913
|
const scrollerRef = useRef(null);
|
|
@@ -935,6 +978,11 @@ var MessageListRoot = ({ children, model, renderer = defaultRenderer, registry,
|
|
|
935
978
|
});
|
|
936
979
|
const changedAtRef = useRef(performance.now());
|
|
937
980
|
useEffect(() => model.subscribe(() => changedAtRef.current = performance.now()), [model]);
|
|
981
|
+
const revisions = useRef(/* @__PURE__ */ new Map());
|
|
982
|
+
useEffect(() => model.subscribe(({ updated }) => {
|
|
983
|
+
for (const id of updated ?? []) revisions.current.set(id, (revisions.current.get(id) ?? 0) + 1);
|
|
984
|
+
}), [model]);
|
|
985
|
+
const revisionOf = useCallback((id) => revisions.current.get(id) ?? 0, []);
|
|
938
986
|
const follow = useFollow({
|
|
939
987
|
scrollerRef,
|
|
940
988
|
placement,
|
|
@@ -1031,14 +1079,14 @@ var MessageListRoot = ({ children, model, renderer = defaultRenderer, registry,
|
|
|
1031
1079
|
}, [viewport, navigation]);
|
|
1032
1080
|
const selectionGroup = useMemo(createSelectionGroup, []);
|
|
1033
1081
|
useEffect(() => {
|
|
1034
|
-
setRef(controllerRef, {
|
|
1082
|
+
Hooks.setRef(controllerRef, {
|
|
1035
1083
|
model,
|
|
1036
1084
|
scrollToBottom,
|
|
1037
1085
|
scrollToIndex,
|
|
1038
1086
|
navigation
|
|
1039
1087
|
});
|
|
1040
1088
|
return () => {
|
|
1041
|
-
setRef(controllerRef, null);
|
|
1089
|
+
Hooks.setRef(controllerRef, null);
|
|
1042
1090
|
};
|
|
1043
1091
|
}, [
|
|
1044
1092
|
controllerRef,
|
|
@@ -1055,11 +1103,14 @@ var MessageListRoot = ({ children, model, renderer = defaultRenderer, registry,
|
|
|
1055
1103
|
model,
|
|
1056
1104
|
renderer,
|
|
1057
1105
|
registry,
|
|
1106
|
+
objectImage,
|
|
1058
1107
|
Chrome,
|
|
1059
1108
|
Custom,
|
|
1060
1109
|
debug,
|
|
1061
1110
|
range,
|
|
1062
1111
|
currentIndex,
|
|
1112
|
+
atEnd: follow.atEnd,
|
|
1113
|
+
following: follow.following,
|
|
1063
1114
|
navigation,
|
|
1064
1115
|
mountedRows: mounted,
|
|
1065
1116
|
mountedWidgets,
|
|
@@ -1074,9 +1125,19 @@ var MessageListRoot = ({ children, model, renderer = defaultRenderer, registry,
|
|
|
1074
1125
|
first,
|
|
1075
1126
|
last,
|
|
1076
1127
|
setViewport,
|
|
1128
|
+
revisionOf,
|
|
1077
1129
|
scrollToIndex,
|
|
1078
1130
|
scrollToBottom,
|
|
1079
|
-
children
|
|
1131
|
+
children: /* @__PURE__ */ jsx(MessageListItemProvider, {
|
|
1132
|
+
model,
|
|
1133
|
+
renderer,
|
|
1134
|
+
registry,
|
|
1135
|
+
objectImage,
|
|
1136
|
+
Custom,
|
|
1137
|
+
debug,
|
|
1138
|
+
reportWidgets,
|
|
1139
|
+
children
|
|
1140
|
+
})
|
|
1080
1141
|
})
|
|
1081
1142
|
})
|
|
1082
1143
|
});
|
|
@@ -1096,43 +1157,38 @@ var MESSAGE_LIST_VIEWPORT_NAME = "MessageList.Viewport";
|
|
|
1096
1157
|
* with a toolbar.
|
|
1097
1158
|
*/
|
|
1098
1159
|
var isEmptyContent = (content, hasCustomRenderer) => content.kind === "markdown" && !content.text.trim() || content.kind === "html" && !content.html.trim() || content.kind === "custom" && !hasCustomRenderer;
|
|
1099
|
-
var MessageListViewport = composable(({ autoHide,
|
|
1100
|
-
const { model, renderer, Chrome, Custom, windowRef, offset, sizerExtent, first, last, setViewport } = useMessageListContext(MESSAGE_LIST_VIEWPORT_NAME);
|
|
1160
|
+
var MessageListViewport = Util.composable(({ autoHide, native, scrollbars, gutter = "md", overlay, ...props }, forwardedRef) => {
|
|
1161
|
+
const { model, renderer, Chrome, Custom, windowRef, offset, sizerExtent, first, last, setViewport, revisionOf } = useMessageListContext(MESSAGE_LIST_VIEWPORT_NAME);
|
|
1101
1162
|
const { selectedIds, onSelect } = useItemSelectionValue();
|
|
1102
1163
|
const handleViewportRef = useCallback((element) => {
|
|
1103
1164
|
setViewport(element);
|
|
1104
|
-
setRef(forwardedRef, element);
|
|
1165
|
+
Hooks.setRef(forwardedRef, element);
|
|
1105
1166
|
}, [setViewport, forwardedRef]);
|
|
1167
|
+
const onSelectRef = useRef(onSelect);
|
|
1168
|
+
onSelectRef.current = onSelect;
|
|
1169
|
+
const handleSelect = useCallback((id, additive) => onSelectRef.current?.(id, additive), []);
|
|
1106
1170
|
const rows = [];
|
|
1107
1171
|
for (let index = first; index <= last; index++) {
|
|
1108
1172
|
const message = model.at(index);
|
|
1109
1173
|
if (!message) continue;
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
selected: selectedIds?.has(message.id) ?? false,
|
|
1120
|
-
onSelect: (id, additive) => onSelect?.(id, additive),
|
|
1121
|
-
children: /* @__PURE__ */ jsx(MessageListItem, { message })
|
|
1122
|
-
}) })
|
|
1123
|
-
})
|
|
1174
|
+
rows.push(/* @__PURE__ */ jsx(MessageListRow, {
|
|
1175
|
+
message,
|
|
1176
|
+
revision: revisionOf(message.id),
|
|
1177
|
+
index,
|
|
1178
|
+
empty: isEmptyContent(renderer(message), Boolean(Custom)),
|
|
1179
|
+
selected: selectedIds?.has(message.id) ?? false,
|
|
1180
|
+
gutter,
|
|
1181
|
+
Chrome,
|
|
1182
|
+
onSelect: handleSelect
|
|
1124
1183
|
}, message.id));
|
|
1125
1184
|
}
|
|
1126
|
-
return /* @__PURE__ */
|
|
1127
|
-
...composableProps(props),
|
|
1185
|
+
return /* @__PURE__ */ jsxs(ScrollArea.Root, {
|
|
1186
|
+
...Util.composableProps(props),
|
|
1128
1187
|
orientation: "vertical",
|
|
1129
1188
|
autoHide,
|
|
1130
|
-
centered,
|
|
1131
1189
|
native,
|
|
1132
|
-
padding,
|
|
1133
1190
|
scrollbars,
|
|
1134
|
-
|
|
1135
|
-
children: /* @__PURE__ */ jsxs(ScrollArea.Viewport, {
|
|
1191
|
+
children: [/* @__PURE__ */ jsxs(ScrollArea.Viewport, {
|
|
1136
1192
|
"data-testid": "feed.viewport",
|
|
1137
1193
|
style: {
|
|
1138
1194
|
position: "relative",
|
|
@@ -1150,23 +1206,45 @@ var MessageListViewport = composable(({ autoHide, centered, native, padding, scr
|
|
|
1150
1206
|
"data-testid": "feed.window",
|
|
1151
1207
|
children: rows
|
|
1152
1208
|
})]
|
|
1153
|
-
})
|
|
1209
|
+
}), overlay]
|
|
1154
1210
|
});
|
|
1155
1211
|
});
|
|
1156
1212
|
MessageListViewport.displayName = MESSAGE_LIST_VIEWPORT_NAME;
|
|
1213
|
+
/**
|
|
1214
|
+
* One mounted row. Memoized because the window re-renders on every scroll event and after every
|
|
1215
|
+
* measurement, and nothing in a row depends on where the window is — only on its own props.
|
|
1216
|
+
*/
|
|
1217
|
+
var MessageListRow = memo(({ message, index, empty, selected, gutter, Chrome, onSelect }) => /* @__PURE__ */ jsx("div", {
|
|
1218
|
+
"data-object-id": message.id,
|
|
1219
|
+
...windowRowProps(index, message.id),
|
|
1220
|
+
children: !empty && /* @__PURE__ */ jsx(Layout.Container, {
|
|
1221
|
+
gutter,
|
|
1222
|
+
children: /* @__PURE__ */ jsx("div", {
|
|
1223
|
+
className: "dx-container-type-inline-size",
|
|
1224
|
+
children: /* @__PURE__ */ jsx(Chrome, {
|
|
1225
|
+
message,
|
|
1226
|
+
index,
|
|
1227
|
+
selected,
|
|
1228
|
+
onSelect,
|
|
1229
|
+
children: /* @__PURE__ */ jsx(MessageListItem, { message })
|
|
1230
|
+
})
|
|
1231
|
+
})
|
|
1232
|
+
})
|
|
1233
|
+
}));
|
|
1234
|
+
MessageListRow.displayName = "MessageList.Row";
|
|
1157
1235
|
var MESSAGE_LIST_ITEM_NAME = "MessageList.Item";
|
|
1158
1236
|
/**
|
|
1159
1237
|
* One message, rendered by the kind its renderer resolves. Exposed so a host can render a message
|
|
1160
1238
|
* outside the scrolling window — a pinned message, a preview — through the same path.
|
|
1161
1239
|
*/
|
|
1162
|
-
var MessageListItem = composable(({ message, ...props }, forwardedRef) => {
|
|
1163
|
-
const { model, renderer, registry, Custom, debug, reportWidgets } =
|
|
1240
|
+
var MessageListItem = Util.composable(({ message, ...props }, forwardedRef) => {
|
|
1241
|
+
const { model, renderer, registry, objectImage, Custom, debug, reportWidgets } = useMessageListItemContext(MESSAGE_LIST_ITEM_NAME);
|
|
1164
1242
|
const content = renderer(message);
|
|
1165
1243
|
const decorations = useDecorations(message.id);
|
|
1166
1244
|
const hits = useMemo(() => decorations.length ? decorations.map(({ range }) => [range.offset, range.offset + range.length]) : void 0, [decorations]);
|
|
1167
1245
|
const handleWidgetsChange = useCallback((count) => reportWidgets(message.id, count), [reportWidgets, message.id]);
|
|
1168
1246
|
return /* @__PURE__ */ jsxs("div", {
|
|
1169
|
-
...composableProps(props, { classNames: debug ? "outline outline-1 outline-dashed outline-primary-500/50 [&_.cm-content>*]:outline [&_.cm-content>*]:outline-1 [&_.cm-content>*]:outline-dashed [&_.cm-content>*]:outline-neutral-500/40" : void 0 }),
|
|
1247
|
+
...Util.composableProps(props, { classNames: debug ? "outline outline-1 outline-dashed outline-primary-500/50 [&_.cm-content>*]:outline [&_.cm-content>*]:outline-1 [&_.cm-content>*]:outline-dashed [&_.cm-content>*]:outline-neutral-500/40" : void 0 }),
|
|
1170
1248
|
ref: forwardedRef,
|
|
1171
1249
|
children: [
|
|
1172
1250
|
content.kind === "markdown" && /* @__PURE__ */ jsx(WidgetScopeProvider, {
|
|
@@ -1175,6 +1253,7 @@ var MessageListItem = composable(({ message, ...props }, forwardedRef) => {
|
|
|
1175
1253
|
text: content.text,
|
|
1176
1254
|
stream: model.streamingId === message.id,
|
|
1177
1255
|
registry,
|
|
1256
|
+
objectImage,
|
|
1178
1257
|
hits,
|
|
1179
1258
|
onWidgetsChange: handleWidgetsChange
|
|
1180
1259
|
})
|
|
@@ -1198,7 +1277,7 @@ var MESSAGE_LIST_NAV_NAME = "MessageList.Nav";
|
|
|
1198
1277
|
* the same keymap the viewport itself carries, so focus landing on the toolbar does not change
|
|
1199
1278
|
* what the keys mean.
|
|
1200
1279
|
*/
|
|
1201
|
-
var MessageListNav = composable(({ ends = true, ...props }, forwardedRef) => {
|
|
1280
|
+
var MessageListNav = Util.composable(({ ends = true, ...props }, forwardedRef) => {
|
|
1202
1281
|
const { navigation } = useMessageListContext(MESSAGE_LIST_NAV_NAME);
|
|
1203
1282
|
const onKeyDown = useCallback((event) => {
|
|
1204
1283
|
const delta = event.key === "ArrowDown" ? 1 : event.key === "ArrowUp" ? -1 : 0;
|
|
@@ -1213,11 +1292,11 @@ var MessageListNav = composable(({ ends = true, ...props }, forwardedRef) => {
|
|
|
1213
1292
|
}, [navigation]);
|
|
1214
1293
|
return /* @__PURE__ */ jsxs("div", {
|
|
1215
1294
|
role: "group",
|
|
1216
|
-
...composableProps(props),
|
|
1295
|
+
...Util.composableProps(props),
|
|
1217
1296
|
onKeyDown,
|
|
1218
1297
|
ref: forwardedRef,
|
|
1219
1298
|
children: [
|
|
1220
|
-
ends && /* @__PURE__ */ jsx(
|
|
1299
|
+
ends && /* @__PURE__ */ jsx(Button.Root, {
|
|
1221
1300
|
icon: "ph--arrow-line-up--regular",
|
|
1222
1301
|
iconOnly: true,
|
|
1223
1302
|
label: "First message",
|
|
@@ -1225,7 +1304,7 @@ var MessageListNav = composable(({ ends = true, ...props }, forwardedRef) => {
|
|
|
1225
1304
|
"data-testid": "feed.nav.top",
|
|
1226
1305
|
onClick: () => navigation.first()
|
|
1227
1306
|
}),
|
|
1228
|
-
/* @__PURE__ */ jsx(
|
|
1307
|
+
/* @__PURE__ */ jsx(Button.Root, {
|
|
1229
1308
|
icon: "ph--caret-up--regular",
|
|
1230
1309
|
iconOnly: true,
|
|
1231
1310
|
label: "Previous message",
|
|
@@ -1233,7 +1312,7 @@ var MessageListNav = composable(({ ends = true, ...props }, forwardedRef) => {
|
|
|
1233
1312
|
"data-testid": "feed.nav.back",
|
|
1234
1313
|
onClick: () => navigation.step(-1)
|
|
1235
1314
|
}),
|
|
1236
|
-
/* @__PURE__ */ jsx(
|
|
1315
|
+
/* @__PURE__ */ jsx(Button.Root, {
|
|
1237
1316
|
icon: "ph--caret-down--regular",
|
|
1238
1317
|
iconOnly: true,
|
|
1239
1318
|
label: "Next message",
|
|
@@ -1241,7 +1320,7 @@ var MessageListNav = composable(({ ends = true, ...props }, forwardedRef) => {
|
|
|
1241
1320
|
"data-testid": "feed.nav.forward",
|
|
1242
1321
|
onClick: () => navigation.step(1)
|
|
1243
1322
|
}),
|
|
1244
|
-
ends && /* @__PURE__ */ jsx(
|
|
1323
|
+
ends && /* @__PURE__ */ jsx(Button.Root, {
|
|
1245
1324
|
icon: "ph--arrow-line-down--regular",
|
|
1246
1325
|
iconOnly: true,
|
|
1247
1326
|
label: "Last message",
|
|
@@ -1277,7 +1356,7 @@ var Minimap = ({ classNames, state, width = DEFAULT_WIDTH$1, onSelect }) => {
|
|
|
1277
1356
|
}),
|
|
1278
1357
|
children: state && total > 0 && /* @__PURE__ */ jsxs(Fragment$1, { children: [
|
|
1279
1358
|
/* @__PURE__ */ jsx("div", {
|
|
1280
|
-
className: "absolute inset-x-0 bg-accent-
|
|
1359
|
+
className: "absolute inset-x-0 bg-accent-bg/30",
|
|
1281
1360
|
style: {
|
|
1282
1361
|
top: scale(state.geometry.window.start),
|
|
1283
1362
|
height: scale(state.geometry.window.extent)
|
|
@@ -1289,7 +1368,7 @@ var Minimap = ({ classNames, state, width = DEFAULT_WIDTH$1, onSelect }) => {
|
|
|
1289
1368
|
style: { top: `${(step + 1) / 10 * 100}%` }
|
|
1290
1369
|
}, step)),
|
|
1291
1370
|
/* @__PURE__ */ jsx("div", {
|
|
1292
|
-
className: "absolute inset-x-0 border-y border-accent-bg bg-accent-
|
|
1371
|
+
className: "absolute inset-x-0 border-y border-accent-bg bg-accent-bg/70",
|
|
1293
1372
|
style: {
|
|
1294
1373
|
top: scale(state.geometry.scroll),
|
|
1295
1374
|
height: scale(state.geometry.viewport)
|
|
@@ -1404,6 +1483,12 @@ var Outline = ({ classNames, markers, visibleRange, tickSize = TICK_SIZE, width
|
|
|
1404
1483
|
const hoveredMarker = shown == null ? void 0 : rows[shown]?.marker;
|
|
1405
1484
|
return /* @__PURE__ */ jsxs(Popover.Root, {
|
|
1406
1485
|
open: hoveredMarker != null,
|
|
1486
|
+
positioning: {
|
|
1487
|
+
placement: "right",
|
|
1488
|
+
flip: false,
|
|
1489
|
+
slide: false
|
|
1490
|
+
},
|
|
1491
|
+
autoFocus: false,
|
|
1407
1492
|
children: [/* @__PURE__ */ jsxs("div", {
|
|
1408
1493
|
role: "navigation",
|
|
1409
1494
|
className: mx("relative flex flex-col justify-center overflow-hidden", classNames),
|
|
@@ -1454,21 +1539,19 @@ var Outline = ({ classNames, markers, visibleRange, tickSize = TICK_SIZE, width
|
|
|
1454
1539
|
}
|
|
1455
1540
|
})
|
|
1456
1541
|
})]
|
|
1457
|
-
}), hoveredMarker && /* @__PURE__ */
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
children: [/* @__PURE__ */ jsx(Popover.Viewport, { children: /* @__PURE__ */ jsxs("div", {
|
|
1463
|
-
className: "px-2 py-1 max-w-[24rem] w-[24rem]",
|
|
1542
|
+
}), hoveredMarker && /* @__PURE__ */ jsx(Popover.Content, {
|
|
1543
|
+
classNames: "w-[32rem] max-w-(--available-width)",
|
|
1544
|
+
children: /* @__PURE__ */ jsxs(Layout.Flex, {
|
|
1545
|
+
column: true,
|
|
1546
|
+
classNames: "gap-1 px-2 py-1",
|
|
1464
1547
|
children: [/* @__PURE__ */ jsx("p", {
|
|
1465
|
-
className: "
|
|
1548
|
+
className: "font-medium line-clamp-2",
|
|
1466
1549
|
children: hoveredMarker.title
|
|
1467
1550
|
}), hoveredMarker.description && /* @__PURE__ */ jsx("p", {
|
|
1468
|
-
className: "
|
|
1551
|
+
className: "text-sm text-fg-muted line-clamp-4",
|
|
1469
1552
|
children: hoveredMarker.description
|
|
1470
1553
|
})]
|
|
1471
|
-
})
|
|
1554
|
+
})
|
|
1472
1555
|
}, hoveredMarker.id)]
|
|
1473
1556
|
});
|
|
1474
1557
|
};
|