@tooee/view 0.2.2 → 0.2.4

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/View.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import type { ActionDefinition } from "@tooee/commands";
2
- import type { CodeBlockRenderer } from "@tooee/renderers";
2
+ import type { CodeBlockRenderer, MarkdownLinkHandler } from "@tooee/renderers";
3
3
  import { type ContentProvider, type ContentRenderer } from "./types.js";
4
4
  interface ViewProps {
5
5
  contentProvider: ContentProvider;
@@ -10,7 +10,9 @@ interface ViewProps {
10
10
  * fence type (first word of the fence info string, case-insensitive).
11
11
  */
12
12
  codeBlockRenderers?: Record<string, CodeBlockRenderer>;
13
+ /** Handles primary-button activation of inline Markdown links. */
14
+ onMarkdownLinkActivate?: MarkdownLinkHandler;
13
15
  }
14
- export declare function View({ contentProvider, actions, renderers, codeBlockRenderers }: ViewProps): import("react").ReactNode;
16
+ export declare function View({ contentProvider, actions, renderers, codeBlockRenderers, onMarkdownLinkActivate, }: ViewProps): import("react").ReactNode;
15
17
  export {};
16
18
  //# sourceMappingURL=View.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"View.d.ts","sourceRoot":"","sources":["../src/View.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAA;AAEvD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAA;AACzD,OAAO,EAAmB,KAAK,eAAe,EAAE,KAAK,eAAe,EAAE,MAAM,YAAY,CAAA;AASxF,UAAU,SAAS;IACjB,eAAe,EAAE,eAAe,CAAA;IAChC,OAAO,CAAC,EAAE,gBAAgB,EAAE,CAAA;IAC5B,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,eAAe,CAAC,CAAA;IAC3C;;;OAGG;IACH,kBAAkB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,iBAAiB,CAAC,CAAA;CACvD;AAED,wBAAgB,IAAI,CAAC,EAAE,eAAe,EAAE,OAAO,EAAE,SAAS,EAAE,kBAAkB,EAAE,EAAE,SAAS,6BA0E1F"}
1
+ {"version":3,"file":"View.d.ts","sourceRoot":"","sources":["../src/View.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAA;AAEvD,OAAO,KAAK,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAA;AAC9E,OAAO,EAAmB,KAAK,eAAe,EAAE,KAAK,eAAe,EAAE,MAAM,YAAY,CAAA;AASxF,UAAU,SAAS;IACjB,eAAe,EAAE,eAAe,CAAA;IAChC,OAAO,CAAC,EAAE,gBAAgB,EAAE,CAAA;IAC5B,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,eAAe,CAAC,CAAA;IAC3C;;;OAGG;IACH,kBAAkB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,iBAAiB,CAAC,CAAA;IACtD,kEAAkE;IAClE,sBAAsB,CAAC,EAAE,mBAAmB,CAAA;CAC7C;AAED,wBAAgB,IAAI,CAAC,EACnB,eAAe,EACf,OAAO,EACP,SAAS,EACT,kBAAkB,EAClB,sBAAsB,GACvB,EAAE,SAAS,6BAyEX"}
package/dist/View.js CHANGED
@@ -4,11 +4,9 @@ import { useTheme } from "@tooee/themes";
4
4
  import { isCustomContent } from "./types.js";
5
5
  import { useContentLoader } from "./hooks/useContentLoader.js";
6
6
  import { MarkdownSubview, CodeSubview, TableSubview, CustomSubview, } from "./components/subviews/index.js";
7
- export function View({ contentProvider, actions, renderers, codeBlockRenderers }) {
7
+ export function View({ contentProvider, actions, renderers, codeBlockRenderers, onMarkdownLinkActivate, }) {
8
8
  const { theme } = useTheme();
9
- const [reloadTrigger, setReloadTrigger] = useState(0);
10
- const reload = useCallback(() => setReloadTrigger((n) => n + 1), []);
11
- const { content, streaming, error, providerMarks } = useContentLoader(contentProvider, reloadTrigger);
9
+ const { content, streaming, error, providerMarks, reload } = useContentLoader(contentProvider);
12
10
  const [userMarks, setUserMarks] = useState([]);
13
11
  const setMarkSet = useCallback((set) => {
14
12
  setUserMarks((prev) => {
@@ -47,7 +45,7 @@ export function View({ contentProvider, actions, renderers, codeBlockRenderers }
47
45
  }
48
46
  switch (content.format) {
49
47
  case "markdown":
50
- return (_jsx(MarkdownSubview, { content: content, codeBlockRenderers: codeBlockRenderers, ...shared }));
48
+ return (_jsx(MarkdownSubview, { content: content, codeBlockRenderers: codeBlockRenderers, onLinkActivate: onMarkdownLinkActivate, ...shared }));
51
49
  case "code":
52
50
  case "text":
53
51
  return _jsx(CodeSubview, { content: content, ...shared });
package/dist/View.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"View.js","sourceRoot":"","sources":["../src/View.tsx"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"View.js","sourceRoot":"","sources":["../src/View.tsx"],"names":[],"mappings":"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"}
@@ -1,10 +1,11 @@
1
- import { type CodeBlockRenderer } from "@tooee/renderers";
1
+ import { type CodeBlockRenderer, type MarkdownLinkHandler } from "@tooee/renderers";
2
2
  import type { MarkdownContent } from "../../types.js";
3
3
  import type { SubviewProps } from "./types.js";
4
4
  interface MarkdownSubviewProps extends SubviewProps {
5
5
  content: MarkdownContent;
6
6
  codeBlockRenderers?: Record<string, CodeBlockRenderer>;
7
+ onLinkActivate?: MarkdownLinkHandler;
7
8
  }
8
- export declare function MarkdownSubview({ content, codeBlockRenderers, decorations, actions, ...screen }: MarkdownSubviewProps): import("react").ReactNode;
9
+ export declare function MarkdownSubview({ content, codeBlockRenderers, onLinkActivate, decorations, actions, ...screen }: MarkdownSubviewProps): import("react").ReactNode;
9
10
  export {};
10
11
  //# sourceMappingURL=MarkdownSubview.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"MarkdownSubview.d.ts","sourceRoot":"","sources":["../../../src/components/subviews/MarkdownSubview.tsx"],"names":[],"mappings":"AAEA,OAAO,EAIL,KAAK,iBAAiB,EAEvB,MAAM,kBAAkB,CAAA;AAGzB,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAA;AAGrD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAA;AAE9C,UAAU,oBAAqB,SAAQ,YAAY;IACjD,OAAO,EAAE,eAAe,CAAA;IACxB,kBAAkB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,iBAAiB,CAAC,CAAA;CACvD;AAeD,wBAAgB,eAAe,CAAC,EAC9B,OAAO,EACP,kBAAkB,EAClB,WAAW,EACX,OAAO,EACP,GAAG,MAAM,EACV,EAAE,oBAAoB,6BAsEtB"}
1
+ {"version":3,"file":"MarkdownSubview.d.ts","sourceRoot":"","sources":["../../../src/components/subviews/MarkdownSubview.tsx"],"names":[],"mappings":"AAEA,OAAO,EAIL,KAAK,iBAAiB,EACtB,KAAK,mBAAmB,EAEzB,MAAM,kBAAkB,CAAA;AAGzB,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAA;AAGrD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAA;AAE9C,UAAU,oBAAqB,SAAQ,YAAY;IACjD,OAAO,EAAE,eAAe,CAAA;IACxB,kBAAkB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,iBAAiB,CAAC,CAAA;IACtD,cAAc,CAAC,EAAE,mBAAmB,CAAA;CACrC;AAeD,wBAAgB,eAAe,CAAC,EAC9B,OAAO,EACP,kBAAkB,EAClB,cAAc,EACd,WAAW,EACX,OAAO,EACP,GAAG,MAAM,EACV,EAAE,oBAAoB,6BAuEtB"}
@@ -16,7 +16,7 @@ const MARKDOWN_BLOCK_ADAPTER = {
16
16
  getText: (block) => getFlatBlockText(block),
17
17
  getSource: (block) => block.source,
18
18
  };
19
- export function MarkdownSubview({ content, codeBlockRenderers, decorations, actions, ...screen }) {
19
+ export function MarkdownSubview({ content, codeBlockRenderers, onLinkActivate, decorations, actions, ...screen }) {
20
20
  const textContent = content.markdown;
21
21
  const lineCount = useMemo(() => textContent.split("\n").length, [textContent]);
22
22
  const blocks = useMemo(() => flattenMarkdown(content.markdown), [content.markdown]);
@@ -61,6 +61,6 @@ export function MarkdownSubview({ content, codeBlockRenderers, decorations, acti
61
61
  { label: "Format:", value: content.format },
62
62
  { label: "Lines:", value: String(lineCount) },
63
63
  ], [content.format, lineCount]);
64
- return (_jsx(ViewScreen, { content: content, controller: document, actions: actions, statusItems: statusItems, ...screen, children: _jsx(MarkdownView, { content: content.markdown, blocks: blocks, showLineNumbers: showLineNumbers, document: document, hScrollableBlocksRef: hScrollableBlocksRef, codeBlockRenderers: codeBlockRenderers }) }));
64
+ return (_jsx(ViewScreen, { content: content, controller: document, actions: actions, statusItems: statusItems, ...screen, children: _jsx(MarkdownView, { content: content.markdown, blocks: blocks, showLineNumbers: showLineNumbers, document: document, hScrollableBlocksRef: hScrollableBlocksRef, codeBlockRenderers: codeBlockRenderers, onLinkActivate: onLinkActivate }) }));
65
65
  }
66
66
  //# sourceMappingURL=MarkdownSubview.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"MarkdownSubview.js","sourceRoot":"","sources":["../../../src/components/subviews/MarkdownSubview.tsx"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"MarkdownSubview.js","sourceRoot":"","sources":["../../../src/components/subviews/MarkdownSubview.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,OAAO,CAAA;AAEvC,OAAO,EACL,YAAY,EACZ,eAAe,EACf,gBAAgB,GAIjB,MAAM,kBAAkB,CAAA;AACzB,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAA;AAC5C,OAAO,EAAE,qBAAqB,EAA2B,MAAM,cAAc,CAAA;AAE7E,OAAO,EAAE,kBAAkB,EAAE,MAAM,mCAAmC,CAAA;AACtE,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAA;AAS7C,4EAA4E;AAC5E,MAAM,kBAAkB,GAAG,CAAC,CAAA;AAE5B;;;;GAIG;AACH,MAAM,sBAAsB,GAAkC;IAC5D,OAAO,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,gBAAgB,CAAC,KAAK,CAAC;IAC3C,SAAS,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,MAAM;CACnC,CAAA;AAED,MAAM,UAAU,eAAe,CAAC,EAC9B,OAAO,EACP,kBAAkB,EAClB,cAAc,EACd,WAAW,EACX,OAAO,EACP,GAAG,MAAM,EACY;IACrB,MAAM,WAAW,GAAG,OAAO,CAAC,QAAQ,CAAA;IACpC,MAAM,SAAS,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC,WAAW,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,CAAC,WAAW,CAAC,CAAC,CAAA;IAC9E,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC,eAAe,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAA;IAEnF,MAAM,EAAE,eAAe,EAAE,GAAG,kBAAkB,CAAC,EAAE,OAAO,EAAE,WAAW,EAAE,CAAC,CAAA;IAExE,MAAM,QAAQ,GAAG,qBAAqB,CAAY;QAChD,IAAI,EAAE,MAAM;QACZ,OAAO,EAAE,sBAAsB;QAC/B,WAAW,EAAE,IAAI;QACjB,WAAW;QACX,+EAA+E;QAC/E,WAAW,EAAE,OAAO;KACrB,CAAC,CAAA;IAEF,MAAM,oBAAoB,GAAG,MAAM,CAAoC,IAAI,GAAG,EAAE,CAAC,CAAA;IACjF,MAAM,gBAAgB,GAAG,GAAG,EAAE,CAC5B,QAAQ,CAAC,WAAW,KAAK,IAAI;QAC3B,CAAC,CAAC,oBAAoB,CAAC,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,WAAW,CAAC;QACxD,CAAC,CAAC,SAAS,CAAA;IACf,UAAU,CAAC;QACT,EAAE,EAAE,mBAAmB;QACvB,KAAK,EAAE,mBAAmB;QAC1B,MAAM,EAAE,GAAG;QACX,KAAK,EAAE,CAAC,QAAQ,CAAC;QACjB,IAAI,EAAE,GAAG,EAAE,CAAC,gBAAgB,EAAE,IAAI,IAAI;QACtC,OAAO,EAAE,GAAG,EAAE;YACZ,MAAM,MAAM,GAAG,gBAAgB,EAAE,CAAA;YACjC,IAAI,MAAM;gBAAE,MAAM,CAAC,OAAO,IAAI,kBAAkB,CAAA;QAClD,CAAC;KACF,CAAC,CAAA;IACF,UAAU,CAAC;QACT,EAAE,EAAE,oBAAoB;QACxB,KAAK,EAAE,oBAAoB;QAC3B,MAAM,EAAE,GAAG;QACX,KAAK,EAAE,CAAC,QAAQ,CAAC;QACjB,IAAI,EAAE,GAAG,EAAE,CAAC,gBAAgB,EAAE,IAAI,IAAI;QACtC,OAAO,EAAE,GAAG,EAAE;YACZ,MAAM,MAAM,GAAG,gBAAgB,EAAE,CAAA;YACjC,IAAI,MAAM;gBAAE,MAAM,CAAC,OAAO,IAAI,kBAAkB,CAAA;QAClD,CAAC;KACF,CAAC,CAAA;IAEF,MAAM,WAAW,GAAG,OAAO,CACzB,GAAG,EAAE,CAAC;QACJ,EAAE,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE,OAAO,CAAC,MAAM,EAAE;QAC3C,EAAE,KAAK,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,CAAC,SAAS,CAAC,EAAE;KAC9C,EACD,CAAC,OAAO,CAAC,MAAM,EAAE,SAAS,CAAC,CAC5B,CAAA;IAED,OAAO,CACL,KAAC,UAAU,IACT,OAAO,EAAE,OAAO,EAChB,UAAU,EAAE,QAAQ,EACpB,OAAO,EAAE,OAAO,EAChB,WAAW,EAAE,WAAW,KACpB,MAAM,YAEV,KAAC,YAAY,IACX,OAAO,EAAE,OAAO,CAAC,QAAQ,EACzB,MAAM,EAAE,MAAM,EACd,eAAe,EAAE,eAAe,EAChC,QAAQ,EAAE,QAAQ,EAClB,oBAAoB,EAAE,oBAAoB,EAC1C,kBAAkB,EAAE,kBAAkB,EACtC,cAAc,EAAE,cAAc,GAC9B,GACS,CACd,CAAA;AACH,CAAC"}
@@ -0,0 +1,53 @@
1
+ import type { MarkSet } from "@tooee/marks";
2
+ import type { AnyContent, ContentChunk } from "./types.js";
3
+ export type ContentLoaderStatus = "idle" | "loading" | "streaming" | "ready" | "error";
4
+ export interface ContentLoaderContext {
5
+ status: ContentLoaderStatus;
6
+ requestId: number;
7
+ loadSeq: number;
8
+ content: AnyContent | null;
9
+ error: string | null;
10
+ providerMarks: MarkSet[];
11
+ title: string | undefined;
12
+ }
13
+ export type ContentLoaderEvents = {
14
+ loadStarted: {
15
+ marks: MarkSet[];
16
+ title?: string;
17
+ };
18
+ streamStarted: {
19
+ requestId: number;
20
+ format: string;
21
+ };
22
+ chunkReceived: {
23
+ requestId: number;
24
+ chunk: ContentChunk;
25
+ };
26
+ loaded: {
27
+ requestId: number;
28
+ content: AnyContent;
29
+ };
30
+ streamEnded: {
31
+ requestId: number;
32
+ };
33
+ loadFailed: {
34
+ requestId: number;
35
+ error: string;
36
+ };
37
+ loadCancelled: {
38
+ requestId: number;
39
+ };
40
+ reloadRequested: {};
41
+ };
42
+ export declare function isAsyncIterable(value: unknown): value is AsyncIterable<ContentChunk>;
43
+ export declare function createEmptyContent(format: string, title?: string): AnyContent;
44
+ export declare function applyContentChunk(current: AnyContent | null, chunk: ContentChunk, title?: string): AnyContent;
45
+ export declare function normalizeError(error: unknown): string;
46
+ export declare function createContentLoaderStore(): import("@xstate/store").Store<ContentLoaderContext, ContentLoaderEvents, never>;
47
+ export declare const selectContent: (ctx: ContentLoaderContext) => AnyContent | null;
48
+ export declare const selectStatus: (ctx: ContentLoaderContext) => ContentLoaderStatus;
49
+ export declare const selectStreaming: (ctx: ContentLoaderContext) => boolean;
50
+ export declare const selectError: (ctx: ContentLoaderContext) => string | null;
51
+ export declare const selectProviderMarks: (ctx: ContentLoaderContext) => MarkSet[];
52
+ export declare const selectLoadSeq: (ctx: ContentLoaderContext) => number;
53
+ //# sourceMappingURL=content-loader-store.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"content-loader-store.d.ts","sourceRoot":"","sources":["../src/content-loader-store.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,cAAc,CAAA;AAC3C,OAAO,KAAK,EAAE,UAAU,EAAW,YAAY,EAAgC,MAAM,YAAY,CAAA;AAEjG,MAAM,MAAM,mBAAmB,GAAG,MAAM,GAAG,SAAS,GAAG,WAAW,GAAG,OAAO,GAAG,OAAO,CAAA;AAEtF,MAAM,WAAW,oBAAoB;IACnC,MAAM,EAAE,mBAAmB,CAAA;IAC3B,SAAS,EAAE,MAAM,CAAA;IACjB,OAAO,EAAE,MAAM,CAAA;IACf,OAAO,EAAE,UAAU,GAAG,IAAI,CAAA;IAC1B,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;IACpB,aAAa,EAAE,OAAO,EAAE,CAAA;IACxB,KAAK,EAAE,MAAM,GAAG,SAAS,CAAA;CAC1B;AAED,MAAM,MAAM,mBAAmB,GAAG;IAChC,WAAW,EAAE;QAAE,KAAK,EAAE,OAAO,EAAE,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,CAAA;IACjD,aAAa,EAAE;QAAE,SAAS,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAA;IACpD,aAAa,EAAE;QAAE,SAAS,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,YAAY,CAAA;KAAE,CAAA;IACzD,MAAM,EAAE;QAAE,SAAS,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,UAAU,CAAA;KAAE,CAAA;IAClD,WAAW,EAAE;QAAE,SAAS,EAAE,MAAM,CAAA;KAAE,CAAA;IAClC,UAAU,EAAE;QAAE,SAAS,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAA;IAChD,aAAa,EAAE;QAAE,SAAS,EAAE,MAAM,CAAA;KAAE,CAAA;IACpC,eAAe,EAAE,EAAE,CAAA;CACpB,CAAA;AAED,wBAAgB,eAAe,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,aAAa,CAAC,YAAY,CAAC,CAEpF;AAED,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,UAAU,CAa7E;AAaD,wBAAgB,iBAAiB,CAC/B,OAAO,EAAE,UAAU,GAAG,IAAI,EAC1B,KAAK,EAAE,YAAY,EACnB,KAAK,CAAC,EAAE,MAAM,GACb,UAAU,CA0BZ;AAED,wBAAgB,cAAc,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAErD;AAED,wBAAgB,wBAAwB,oFAqDvC;AAED,eAAO,MAAM,aAAa,GAAI,KAAK,oBAAoB,sBAAgB,CAAA;AACvE,eAAO,MAAM,YAAY,GAAI,KAAK,oBAAoB,wBAAe,CAAA;AACrE,eAAO,MAAM,eAAe,GAAI,KAAK,oBAAoB,YAA+B,CAAA;AACxF,eAAO,MAAM,WAAW,GAAI,KAAK,oBAAoB,kBAAc,CAAA;AACnE,eAAO,MAAM,mBAAmB,GAAI,KAAK,oBAAoB,cAAsB,CAAA;AACnF,eAAO,MAAM,aAAa,GAAI,KAAK,oBAAoB,WAAgB,CAAA"}
@@ -0,0 +1,111 @@
1
+ import { createStore } from "@xstate/store";
2
+ export function isAsyncIterable(value) {
3
+ return value != null && typeof value === "object" && Symbol.asyncIterator in value;
4
+ }
5
+ export function createEmptyContent(format, title) {
6
+ switch (format) {
7
+ case "markdown":
8
+ return { format, markdown: "", title };
9
+ case "code":
10
+ return { format, code: "", title };
11
+ case "text":
12
+ return { format, text: "", title };
13
+ case "table":
14
+ return { format, columns: [], rows: [], title };
15
+ default:
16
+ return { format, data: undefined, title };
17
+ }
18
+ }
19
+ function ensureContentFormat(current, format, title) {
20
+ if (!current || current.format !== format) {
21
+ return createEmptyContent(format, title);
22
+ }
23
+ return current;
24
+ }
25
+ export function applyContentChunk(current, chunk, title) {
26
+ switch (chunk.type) {
27
+ case "replace":
28
+ return chunk.content;
29
+ case "append":
30
+ if (chunk.format === "markdown") {
31
+ const target = ensureContentFormat(current, "markdown", title);
32
+ return { ...target, markdown: target.markdown + chunk.data };
33
+ }
34
+ if (chunk.format === "code") {
35
+ const target = ensureContentFormat(current, "code", title);
36
+ return {
37
+ ...target,
38
+ code: target.code + chunk.data,
39
+ language: chunk.language ?? target.language,
40
+ };
41
+ }
42
+ {
43
+ const target = ensureContentFormat(current, "text", title);
44
+ return { ...target, text: target.text + chunk.data };
45
+ }
46
+ case "patch":
47
+ return chunk.apply(current);
48
+ default:
49
+ return current ?? createEmptyContent("markdown", title);
50
+ }
51
+ }
52
+ export function normalizeError(error) {
53
+ return error instanceof Error ? error.message : String(error);
54
+ }
55
+ export function createContentLoaderStore() {
56
+ return createStore({
57
+ context: {
58
+ status: "idle",
59
+ requestId: 0,
60
+ loadSeq: 0,
61
+ content: null,
62
+ error: null,
63
+ providerMarks: [],
64
+ title: undefined,
65
+ },
66
+ on: {
67
+ loadStarted: (ctx, event) => ({
68
+ ...ctx,
69
+ status: "loading",
70
+ requestId: ctx.requestId + 1,
71
+ error: null,
72
+ providerMarks: event.marks,
73
+ title: event.title,
74
+ }),
75
+ streamStarted: (ctx, event) => event.requestId !== ctx.requestId
76
+ ? ctx
77
+ : {
78
+ ...ctx,
79
+ status: "streaming",
80
+ content: createEmptyContent(event.format, ctx.title),
81
+ },
82
+ chunkReceived: (ctx, event) => {
83
+ if (event.requestId !== ctx.requestId)
84
+ return ctx;
85
+ if (event.chunk.type === "marks") {
86
+ const markSet = event.chunk.set;
87
+ const providerMarks = [
88
+ ...ctx.providerMarks.filter((set) => set.namespace !== markSet.namespace),
89
+ markSet,
90
+ ];
91
+ return { ...ctx, providerMarks };
92
+ }
93
+ return { ...ctx, content: applyContentChunk(ctx.content, event.chunk, ctx.title) };
94
+ },
95
+ loaded: (ctx, event) => event.requestId !== ctx.requestId
96
+ ? ctx
97
+ : { ...ctx, status: "ready", content: event.content },
98
+ streamEnded: (ctx, event) => event.requestId !== ctx.requestId ? ctx : { ...ctx, status: "ready" },
99
+ loadFailed: (ctx, event) => event.requestId !== ctx.requestId ? ctx : { ...ctx, status: "error", error: event.error },
100
+ loadCancelled: (ctx, event) => event.requestId !== ctx.requestId ? ctx : { ...ctx, requestId: ctx.requestId + 1 },
101
+ reloadRequested: (ctx) => ({ ...ctx, loadSeq: ctx.loadSeq + 1 }),
102
+ },
103
+ });
104
+ }
105
+ export const selectContent = (ctx) => ctx.content;
106
+ export const selectStatus = (ctx) => ctx.status;
107
+ export const selectStreaming = (ctx) => ctx.status === "streaming";
108
+ export const selectError = (ctx) => ctx.error;
109
+ export const selectProviderMarks = (ctx) => ctx.providerMarks;
110
+ export const selectLoadSeq = (ctx) => ctx.loadSeq;
111
+ //# sourceMappingURL=content-loader-store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"content-loader-store.js","sourceRoot":"","sources":["../src/content-loader-store.ts"],"names":[],"mappings":"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"}
@@ -1,10 +1,10 @@
1
- import type { MarkSet } from "@tooee/marks";
2
- import type { AnyContent, ContentProvider } from "../types.js";
3
- export declare function useContentLoader(contentProvider: ContentProvider, reloadTrigger: number): {
4
- content: AnyContent | null;
1
+ import type { ContentProvider } from "../types.js";
2
+ export declare function useContentLoader(contentProvider: ContentProvider): {
3
+ content: import("../types.js").AnyContent | null;
5
4
  streaming: boolean;
6
5
  error: string | null;
7
- providerMarks: MarkSet[];
8
- setProviderMarks: import("react").Dispatch<import("react").SetStateAction<MarkSet[]>>;
6
+ providerMarks: import("@tooee/marks").MarkSet[];
7
+ status: import("../content-loader-store.js").ContentLoaderStatus;
8
+ reload: () => void;
9
9
  };
10
10
  //# sourceMappingURL=useContentLoader.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"useContentLoader.d.ts","sourceRoot":"","sources":["../../src/hooks/useContentLoader.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,cAAc,CAAA;AAC3C,OAAO,KAAK,EACV,UAAU,EAGV,eAAe,EAGhB,MAAM,aAAa,CAAA;AAgEpB,wBAAgB,gBAAgB,CAAC,eAAe,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM;;;;;;EA+EvF"}
1
+ {"version":3,"file":"useContentLoader.d.ts","sourceRoot":"","sources":["../../src/hooks/useContentLoader.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,aAAa,CAAA;AAalD,wBAAgB,gBAAgB,CAAC,eAAe,EAAE,eAAe;;;;;;;EAwDhE"}
@@ -1,129 +1,55 @@
1
- import { useState, useEffect } from "react";
2
- function isAsyncIterable(value) {
3
- return value != null && typeof value === "object" && Symbol.asyncIterator in value;
4
- }
5
- function createEmptyContent(format, title) {
6
- switch (format) {
7
- case "markdown":
8
- return { format, markdown: "", title };
9
- case "code":
10
- return { format, code: "", title };
11
- case "text":
12
- return { format, text: "", title };
13
- case "table":
14
- return { format, columns: [], rows: [], title };
15
- default:
16
- return { format, data: undefined, title };
17
- }
18
- }
19
- function ensureContentFormat(current, format, title) {
20
- if (!current || current.format !== format) {
21
- return createEmptyContent(format, title);
22
- }
23
- return current;
24
- }
25
- function applyContentChunk(current, chunk, title) {
26
- switch (chunk.type) {
27
- case "replace":
28
- return chunk.content;
29
- case "append":
30
- if (chunk.format === "markdown") {
31
- const target = ensureContentFormat(current, "markdown", title);
32
- return { ...target, markdown: target.markdown + chunk.data };
33
- }
34
- if (chunk.format === "code") {
35
- const target = ensureContentFormat(current, "code", title);
36
- return {
37
- ...target,
38
- code: target.code + chunk.data,
39
- language: chunk.language ?? target.language,
40
- };
41
- }
42
- {
43
- const target = ensureContentFormat(current, "text", title);
44
- return { ...target, text: target.text + chunk.data };
45
- }
46
- case "patch":
47
- return chunk.apply(current);
48
- default:
49
- return current ?? createEmptyContent("markdown", title);
50
- }
51
- }
52
- export function useContentLoader(contentProvider, reloadTrigger) {
53
- const [content, setContent] = useState(null);
54
- const [streaming, setStreaming] = useState(false);
55
- const [error, setError] = useState(null);
56
- const [providerMarks, setProviderMarks] = useState([]);
1
+ import { useCallback, useEffect, useState } from "react";
2
+ import { useSelector } from "@xstate/store-react";
3
+ import { createContentLoaderStore, isAsyncIterable, normalizeError, selectContent, selectError, selectLoadSeq, selectProviderMarks, selectStatus, selectStreaming, } from "../content-loader-store.js";
4
+ export function useContentLoader(contentProvider) {
5
+ const [store] = useState(createContentLoaderStore);
6
+ const loadSeq = useSelector(store, (snapshot) => selectLoadSeq(snapshot.context));
57
7
  useEffect(() => {
58
- setError(null);
59
- setStreaming(false);
60
- // Reset provider marks on reload; initialize with static marks if provided
61
- setProviderMarks(contentProvider.marks ?? []);
8
+ store.trigger.loadStarted({
9
+ marks: contentProvider.marks ?? [],
10
+ title: contentProvider.title,
11
+ });
12
+ const requestId = store.getSnapshot().context.requestId;
62
13
  const loaded = contentProvider.load();
63
14
  if (isAsyncIterable(loaded)) {
64
- const fallbackFormat = contentProvider.format ?? "markdown";
65
- const title = contentProvider.title;
66
- let cancelled = false;
67
- let current = createEmptyContent(fallbackFormat, title);
68
- setContent(current);
69
- setStreaming(true);
70
- // Hold the iterator explicitly so cleanup can close it: a cleanup that
71
- // only flips `cancelled` leaves a pending next() (and the provider's
72
- // subprocess/file handle/network stream) alive until the next chunk.
15
+ store.trigger.streamStarted({
16
+ requestId,
17
+ format: contentProvider.format ?? "markdown",
18
+ });
73
19
  const iterator = loaded[Symbol.asyncIterator]();
74
20
  (async () => {
75
21
  try {
76
22
  while (true) {
77
23
  const result = await iterator.next();
78
- if (cancelled || result.done)
24
+ if (result.done)
79
25
  break;
80
- const chunk = result.value;
81
- if (chunk.type === "marks") {
82
- // Streamed mark set: merge by replacing any existing set with same namespace
83
- setProviderMarks((prev) => {
84
- const filtered = prev.filter((s) => s.namespace !== chunk.set.namespace);
85
- return [...filtered, chunk.set];
86
- });
87
- continue;
88
- }
89
- current = applyContentChunk(current, chunk, title);
90
- setContent(current);
26
+ store.trigger.chunkReceived({ requestId, chunk: result.value });
91
27
  }
28
+ store.trigger.streamEnded({ requestId });
92
29
  }
93
- catch (err) {
94
- if (!cancelled) {
95
- setError(err instanceof Error ? err.message : String(err));
96
- }
97
- }
98
- finally {
99
- if (!cancelled) {
100
- setStreaming(false);
101
- }
30
+ catch (error) {
31
+ store.trigger.loadFailed({ requestId, error: normalizeError(error) });
102
32
  }
103
33
  })();
104
34
  return () => {
105
- cancelled = true;
106
- // Close the iterator to release provider resources mid-stream.
35
+ store.trigger.loadCancelled({ requestId });
107
36
  Promise.resolve(iterator.return?.()).catch(() => { });
108
37
  };
109
38
  }
110
39
  if (loaded instanceof Promise) {
111
- let active = true;
112
40
  loaded
113
- .then((value) => {
114
- if (active)
115
- setContent(value);
116
- })
117
- .catch((err) => {
118
- if (active)
119
- setError(err instanceof Error ? err.message : String(err));
120
- });
121
- return () => {
122
- active = false;
123
- };
41
+ .then((content) => store.trigger.loaded({ requestId, content }))
42
+ .catch((error) => store.trigger.loadFailed({ requestId, error: normalizeError(error) }));
43
+ return () => store.trigger.loadCancelled({ requestId });
124
44
  }
125
- setContent(loaded);
126
- }, [contentProvider, reloadTrigger]);
127
- return { content, streaming, error, providerMarks, setProviderMarks };
45
+ store.trigger.loaded({ requestId, content: loaded });
46
+ }, [contentProvider, loadSeq, store]);
47
+ const content = useSelector(store, (snapshot) => selectContent(snapshot.context));
48
+ const streaming = useSelector(store, (snapshot) => selectStreaming(snapshot.context));
49
+ const error = useSelector(store, (snapshot) => selectError(snapshot.context));
50
+ const providerMarks = useSelector(store, (snapshot) => selectProviderMarks(snapshot.context));
51
+ const status = useSelector(store, (snapshot) => selectStatus(snapshot.context));
52
+ const reload = useCallback(() => store.trigger.reloadRequested({}), [store]);
53
+ return { content, streaming, error, providerMarks, status, reload };
128
54
  }
129
55
  //# sourceMappingURL=useContentLoader.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"useContentLoader.js","sourceRoot":"","sources":["../../src/hooks/useContentLoader.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"useContentLoader.js","sourceRoot":"","sources":["../../src/hooks/useContentLoader.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tooee/view",
3
- "version": "0.2.2",
3
+ "version": "0.2.4",
4
4
  "description": "Terminal content viewer for markdown, code, text, and tables",
5
5
  "keywords": [
6
6
  "cli",
@@ -37,15 +37,17 @@
37
37
  "typecheck": "tsc --noEmit"
38
38
  },
39
39
  "dependencies": {
40
- "@tooee/commands": "0.2.2",
41
- "@tooee/config": "0.2.2",
42
- "@tooee/layout": "0.2.2",
43
- "@tooee/marks": "0.2.2",
44
- "@tooee/overlays": "0.2.2",
45
- "@tooee/renderers": "0.2.2",
46
- "@tooee/search": "0.2.2",
47
- "@tooee/shell": "0.2.2",
48
- "@tooee/themes": "0.2.2",
40
+ "@tooee/commands": "0.2.4",
41
+ "@tooee/config": "0.2.4",
42
+ "@tooee/layout": "0.2.4",
43
+ "@tooee/marks": "0.2.4",
44
+ "@tooee/overlays": "0.2.4",
45
+ "@tooee/renderers": "0.2.4",
46
+ "@tooee/search": "0.2.4",
47
+ "@tooee/shell": "0.2.4",
48
+ "@tooee/themes": "0.2.4",
49
+ "@xstate/store": "^4",
50
+ "@xstate/store-react": "^2",
49
51
  "marked": "^17.0.4"
50
52
  },
51
53
  "devDependencies": {
package/src/View.tsx CHANGED
@@ -2,7 +2,7 @@ import { useCallback, useMemo, useState } from "react"
2
2
  import { useTheme } from "@tooee/themes"
3
3
  import type { ActionDefinition } from "@tooee/commands"
4
4
  import type { MarkSet } from "@tooee/marks"
5
- import type { CodeBlockRenderer } from "@tooee/renderers"
5
+ import type { CodeBlockRenderer, MarkdownLinkHandler } from "@tooee/renderers"
6
6
  import { isCustomContent, type ContentProvider, type ContentRenderer } from "./types.js"
7
7
  import { useContentLoader } from "./hooks/useContentLoader.js"
8
8
  import {
@@ -21,17 +21,19 @@ interface ViewProps {
21
21
  * fence type (first word of the fence info string, case-insensitive).
22
22
  */
23
23
  codeBlockRenderers?: Record<string, CodeBlockRenderer>
24
+ /** Handles primary-button activation of inline Markdown links. */
25
+ onMarkdownLinkActivate?: MarkdownLinkHandler
24
26
  }
25
27
 
26
- export function View({ contentProvider, actions, renderers, codeBlockRenderers }: ViewProps) {
28
+ export function View({
29
+ contentProvider,
30
+ actions,
31
+ renderers,
32
+ codeBlockRenderers,
33
+ onMarkdownLinkActivate,
34
+ }: ViewProps) {
27
35
  const { theme } = useTheme()
28
- const [reloadTrigger, setReloadTrigger] = useState(0)
29
- const reload = useCallback(() => setReloadTrigger((n) => n + 1), [])
30
-
31
- const { content, streaming, error, providerMarks } = useContentLoader(
32
- contentProvider,
33
- reloadTrigger,
34
- )
36
+ const { content, streaming, error, providerMarks, reload } = useContentLoader(contentProvider)
35
37
 
36
38
  const [userMarks, setUserMarks] = useState<MarkSet[]>([])
37
39
 
@@ -89,7 +91,12 @@ export function View({ contentProvider, actions, renderers, codeBlockRenderers }
89
91
  switch (content.format) {
90
92
  case "markdown":
91
93
  return (
92
- <MarkdownSubview content={content} codeBlockRenderers={codeBlockRenderers} {...shared} />
94
+ <MarkdownSubview
95
+ content={content}
96
+ codeBlockRenderers={codeBlockRenderers}
97
+ onLinkActivate={onMarkdownLinkActivate}
98
+ {...shared}
99
+ />
93
100
  )
94
101
  case "code":
95
102
  case "text":
@@ -5,6 +5,7 @@ import {
5
5
  flattenMarkdown,
6
6
  getFlatBlockText,
7
7
  type CodeBlockRenderer,
8
+ type MarkdownLinkHandler,
8
9
  type FlatBlock,
9
10
  } from "@tooee/renderers"
10
11
  import { useCommand } from "@tooee/commands"
@@ -17,6 +18,7 @@ import type { SubviewProps } from "./types.js"
17
18
  interface MarkdownSubviewProps extends SubviewProps {
18
19
  content: MarkdownContent
19
20
  codeBlockRenderers?: Record<string, CodeBlockRenderer>
21
+ onLinkActivate?: MarkdownLinkHandler
20
22
  }
21
23
 
22
24
  /** Columns moved per h/l press when scrolling a wide block horizontally. */
@@ -35,6 +37,7 @@ const MARKDOWN_BLOCK_ADAPTER: DocumentRowAdapter<FlatBlock> = {
35
37
  export function MarkdownSubview({
36
38
  content,
37
39
  codeBlockRenderers,
40
+ onLinkActivate,
38
41
  decorations,
39
42
  actions,
40
43
  ...screen
@@ -105,6 +108,7 @@ export function MarkdownSubview({
105
108
  document={document}
106
109
  hScrollableBlocksRef={hScrollableBlocksRef}
107
110
  codeBlockRenderers={codeBlockRenderers}
111
+ onLinkActivate={onLinkActivate}
108
112
  />
109
113
  </ViewScreen>
110
114
  )
@@ -0,0 +1,154 @@
1
+ import { createStore } from "@xstate/store"
2
+ import type { MarkSet } from "@tooee/marks"
3
+ import type { AnyContent, Content, ContentChunk, ContentFormat, CustomContent } from "./types.js"
4
+
5
+ export type ContentLoaderStatus = "idle" | "loading" | "streaming" | "ready" | "error"
6
+
7
+ export interface ContentLoaderContext {
8
+ status: ContentLoaderStatus
9
+ requestId: number
10
+ loadSeq: number
11
+ content: AnyContent | null
12
+ error: string | null
13
+ providerMarks: MarkSet[]
14
+ title: string | undefined
15
+ }
16
+
17
+ export type ContentLoaderEvents = {
18
+ loadStarted: { marks: MarkSet[]; title?: string }
19
+ streamStarted: { requestId: number; format: string }
20
+ chunkReceived: { requestId: number; chunk: ContentChunk }
21
+ loaded: { requestId: number; content: AnyContent }
22
+ streamEnded: { requestId: number }
23
+ loadFailed: { requestId: number; error: string }
24
+ loadCancelled: { requestId: number }
25
+ reloadRequested: {}
26
+ }
27
+
28
+ export function isAsyncIterable(value: unknown): value is AsyncIterable<ContentChunk> {
29
+ return value != null && typeof value === "object" && Symbol.asyncIterator in value
30
+ }
31
+
32
+ export function createEmptyContent(format: string, title?: string): AnyContent {
33
+ switch (format) {
34
+ case "markdown":
35
+ return { format, markdown: "", title }
36
+ case "code":
37
+ return { format, code: "", title }
38
+ case "text":
39
+ return { format, text: "", title }
40
+ case "table":
41
+ return { format, columns: [], rows: [], title }
42
+ default:
43
+ return { format, data: undefined, title } as CustomContent
44
+ }
45
+ }
46
+
47
+ function ensureContentFormat<F extends ContentFormat>(
48
+ current: AnyContent | null,
49
+ format: F,
50
+ title?: string,
51
+ ): Extract<Content, { format: F }> {
52
+ if (!current || current.format !== format) {
53
+ return createEmptyContent(format, title) as Extract<Content, { format: F }>
54
+ }
55
+ return current as Extract<Content, { format: F }>
56
+ }
57
+
58
+ export function applyContentChunk(
59
+ current: AnyContent | null,
60
+ chunk: ContentChunk,
61
+ title?: string,
62
+ ): AnyContent {
63
+ switch (chunk.type) {
64
+ case "replace":
65
+ return chunk.content
66
+ case "append":
67
+ if (chunk.format === "markdown") {
68
+ const target = ensureContentFormat(current, "markdown", title)
69
+ return { ...target, markdown: target.markdown + chunk.data }
70
+ }
71
+ if (chunk.format === "code") {
72
+ const target = ensureContentFormat(current, "code", title)
73
+ return {
74
+ ...target,
75
+ code: target.code + chunk.data,
76
+ language: chunk.language ?? target.language,
77
+ }
78
+ }
79
+ {
80
+ const target = ensureContentFormat(current, "text", title)
81
+ return { ...target, text: target.text + chunk.data }
82
+ }
83
+ case "patch":
84
+ return chunk.apply(current)
85
+ default:
86
+ return current ?? createEmptyContent("markdown", title)
87
+ }
88
+ }
89
+
90
+ export function normalizeError(error: unknown): string {
91
+ return error instanceof Error ? error.message : String(error)
92
+ }
93
+
94
+ export function createContentLoaderStore() {
95
+ return createStore<ContentLoaderContext, ContentLoaderEvents>({
96
+ context: {
97
+ status: "idle",
98
+ requestId: 0,
99
+ loadSeq: 0,
100
+ content: null,
101
+ error: null,
102
+ providerMarks: [],
103
+ title: undefined,
104
+ },
105
+ on: {
106
+ loadStarted: (ctx, event) => ({
107
+ ...ctx,
108
+ status: "loading",
109
+ requestId: ctx.requestId + 1,
110
+ error: null,
111
+ providerMarks: event.marks,
112
+ title: event.title,
113
+ }),
114
+ streamStarted: (ctx, event) =>
115
+ event.requestId !== ctx.requestId
116
+ ? ctx
117
+ : {
118
+ ...ctx,
119
+ status: "streaming",
120
+ content: createEmptyContent(event.format, ctx.title),
121
+ },
122
+ chunkReceived: (ctx, event) => {
123
+ if (event.requestId !== ctx.requestId) return ctx
124
+ if (event.chunk.type === "marks") {
125
+ const markSet = event.chunk.set
126
+ const providerMarks = [
127
+ ...ctx.providerMarks.filter((set) => set.namespace !== markSet.namespace),
128
+ markSet,
129
+ ]
130
+ return { ...ctx, providerMarks }
131
+ }
132
+ return { ...ctx, content: applyContentChunk(ctx.content, event.chunk, ctx.title) }
133
+ },
134
+ loaded: (ctx, event) =>
135
+ event.requestId !== ctx.requestId
136
+ ? ctx
137
+ : { ...ctx, status: "ready", content: event.content },
138
+ streamEnded: (ctx, event) =>
139
+ event.requestId !== ctx.requestId ? ctx : { ...ctx, status: "ready" },
140
+ loadFailed: (ctx, event) =>
141
+ event.requestId !== ctx.requestId ? ctx : { ...ctx, status: "error", error: event.error },
142
+ loadCancelled: (ctx, event) =>
143
+ event.requestId !== ctx.requestId ? ctx : { ...ctx, requestId: ctx.requestId + 1 },
144
+ reloadRequested: (ctx) => ({ ...ctx, loadSeq: ctx.loadSeq + 1 }),
145
+ },
146
+ })
147
+ }
148
+
149
+ export const selectContent = (ctx: ContentLoaderContext) => ctx.content
150
+ export const selectStatus = (ctx: ContentLoaderContext) => ctx.status
151
+ export const selectStreaming = (ctx: ContentLoaderContext) => ctx.status === "streaming"
152
+ export const selectError = (ctx: ContentLoaderContext) => ctx.error
153
+ export const selectProviderMarks = (ctx: ContentLoaderContext) => ctx.providerMarks
154
+ export const selectLoadSeq = (ctx: ContentLoaderContext) => ctx.loadSeq
@@ -1,153 +1,72 @@
1
- import { useState, useEffect } from "react"
2
- import type { MarkSet } from "@tooee/marks"
3
- import type {
4
- AnyContent,
5
- Content,
6
- ContentFormat,
7
- ContentProvider,
8
- ContentChunk,
9
- CustomContent,
10
- } from "../types.js"
1
+ import { useCallback, useEffect, useState } from "react"
2
+ import { useSelector } from "@xstate/store-react"
3
+ import type { ContentProvider } from "../types.js"
4
+ import {
5
+ createContentLoaderStore,
6
+ isAsyncIterable,
7
+ normalizeError,
8
+ selectContent,
9
+ selectError,
10
+ selectLoadSeq,
11
+ selectProviderMarks,
12
+ selectStatus,
13
+ selectStreaming,
14
+ } from "../content-loader-store.js"
11
15
 
12
- function isAsyncIterable(value: unknown): value is AsyncIterable<ContentChunk> {
13
- return value != null && typeof value === "object" && Symbol.asyncIterator in value
14
- }
15
-
16
- function createEmptyContent(format: string, title?: string): AnyContent {
17
- switch (format) {
18
- case "markdown":
19
- return { format, markdown: "", title }
20
- case "code":
21
- return { format, code: "", title }
22
- case "text":
23
- return { format, text: "", title }
24
- case "table":
25
- return { format, columns: [], rows: [], title }
26
- default:
27
- return { format, data: undefined, title } as CustomContent
28
- }
29
- }
30
-
31
- function ensureContentFormat<F extends ContentFormat>(
32
- current: AnyContent | null,
33
- format: F,
34
- title?: string,
35
- ): Extract<Content, { format: F }> {
36
- if (!current || current.format !== format) {
37
- return createEmptyContent(format, title) as Extract<Content, { format: F }>
38
- }
39
- return current as Extract<Content, { format: F }>
40
- }
41
-
42
- function applyContentChunk(
43
- current: AnyContent | null,
44
- chunk: ContentChunk,
45
- title?: string,
46
- ): AnyContent {
47
- switch (chunk.type) {
48
- case "replace":
49
- return chunk.content
50
- case "append":
51
- if (chunk.format === "markdown") {
52
- const target = ensureContentFormat(current, "markdown", title)
53
- return { ...target, markdown: target.markdown + chunk.data }
54
- }
55
- if (chunk.format === "code") {
56
- const target = ensureContentFormat(current, "code", title)
57
- return {
58
- ...target,
59
- code: target.code + chunk.data,
60
- language: chunk.language ?? target.language,
61
- }
62
- }
63
- {
64
- const target = ensureContentFormat(current, "text", title)
65
- return { ...target, text: target.text + chunk.data }
66
- }
67
- case "patch":
68
- return chunk.apply(current)
69
- default:
70
- return current ?? createEmptyContent("markdown", title)
71
- }
72
- }
73
-
74
- export function useContentLoader(contentProvider: ContentProvider, reloadTrigger: number) {
75
- const [content, setContent] = useState<AnyContent | null>(null)
76
- const [streaming, setStreaming] = useState(false)
77
- const [error, setError] = useState<string | null>(null)
78
- const [providerMarks, setProviderMarks] = useState<MarkSet[]>([])
16
+ export function useContentLoader(contentProvider: ContentProvider) {
17
+ const [store] = useState(createContentLoaderStore)
18
+ const loadSeq = useSelector(store, (snapshot) => selectLoadSeq(snapshot.context))
79
19
 
80
20
  useEffect(() => {
81
- setError(null)
82
- setStreaming(false)
83
- // Reset provider marks on reload; initialize with static marks if provided
84
- setProviderMarks(contentProvider.marks ?? [])
21
+ store.trigger.loadStarted({
22
+ marks: contentProvider.marks ?? [],
23
+ title: contentProvider.title,
24
+ })
25
+ const requestId = store.getSnapshot().context.requestId
85
26
  const loaded = contentProvider.load()
86
27
 
87
28
  if (isAsyncIterable(loaded)) {
88
- const fallbackFormat = contentProvider.format ?? "markdown"
89
- const title = contentProvider.title
90
- let cancelled = false
91
- let current: AnyContent | null = createEmptyContent(fallbackFormat, title)
92
- setContent(current)
93
- setStreaming(true)
94
-
95
- // Hold the iterator explicitly so cleanup can close it: a cleanup that
96
- // only flips `cancelled` leaves a pending next() (and the provider's
97
- // subprocess/file handle/network stream) alive until the next chunk.
29
+ store.trigger.streamStarted({
30
+ requestId,
31
+ format: contentProvider.format ?? "markdown",
32
+ })
98
33
  const iterator = loaded[Symbol.asyncIterator]()
99
-
100
34
  ;(async () => {
101
35
  try {
102
36
  while (true) {
103
37
  const result = await iterator.next()
104
- if (cancelled || result.done) break
105
- const chunk = result.value
106
- if (chunk.type === "marks") {
107
- // Streamed mark set: merge by replacing any existing set with same namespace
108
- setProviderMarks((prev) => {
109
- const filtered = prev.filter((s) => s.namespace !== chunk.set.namespace)
110
- return [...filtered, chunk.set]
111
- })
112
- continue
113
- }
114
- current = applyContentChunk(current, chunk, title)
115
- setContent(current)
116
- }
117
- } catch (err) {
118
- if (!cancelled) {
119
- setError(err instanceof Error ? err.message : String(err))
120
- }
121
- } finally {
122
- if (!cancelled) {
123
- setStreaming(false)
38
+ if (result.done) break
39
+ store.trigger.chunkReceived({ requestId, chunk: result.value })
124
40
  }
41
+ store.trigger.streamEnded({ requestId })
42
+ } catch (error) {
43
+ store.trigger.loadFailed({ requestId, error: normalizeError(error) })
125
44
  }
126
45
  })()
127
-
128
46
  return () => {
129
- cancelled = true
130
- // Close the iterator to release provider resources mid-stream.
47
+ store.trigger.loadCancelled({ requestId })
131
48
  Promise.resolve(iterator.return?.()).catch(() => {})
132
49
  }
133
50
  }
134
51
 
135
52
  if (loaded instanceof Promise) {
136
- let active = true
137
53
  loaded
138
- .then((value) => {
139
- if (active) setContent(value)
140
- })
141
- .catch((err: unknown) => {
142
- if (active) setError(err instanceof Error ? err.message : String(err))
143
- })
144
- return () => {
145
- active = false
146
- }
54
+ .then((content) => store.trigger.loaded({ requestId, content }))
55
+ .catch((error: unknown) =>
56
+ store.trigger.loadFailed({ requestId, error: normalizeError(error) }),
57
+ )
58
+ return () => store.trigger.loadCancelled({ requestId })
147
59
  }
148
60
 
149
- setContent(loaded)
150
- }, [contentProvider, reloadTrigger])
61
+ store.trigger.loaded({ requestId, content: loaded })
62
+ }, [contentProvider, loadSeq, store])
63
+
64
+ const content = useSelector(store, (snapshot) => selectContent(snapshot.context))
65
+ const streaming = useSelector(store, (snapshot) => selectStreaming(snapshot.context))
66
+ const error = useSelector(store, (snapshot) => selectError(snapshot.context))
67
+ const providerMarks = useSelector(store, (snapshot) => selectProviderMarks(snapshot.context))
68
+ const status = useSelector(store, (snapshot) => selectStatus(snapshot.context))
69
+ const reload = useCallback(() => store.trigger.reloadRequested({}), [store])
151
70
 
152
- return { content, streaming, error, providerMarks, setProviderMarks }
71
+ return { content, streaming, error, providerMarks, status, reload }
153
72
  }