@tooee/view 0.1.20 → 0.1.21
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 +7 -1
- package/dist/View.d.ts.map +1 -1
- package/dist/View.js +2 -2
- package/dist/View.js.map +1 -1
- package/dist/components/subviews/CodeSubview.d.ts.map +1 -1
- package/dist/components/subviews/CodeSubview.js +28 -3
- package/dist/components/subviews/CodeSubview.js.map +1 -1
- package/dist/components/subviews/CustomSubview.d.ts.map +1 -1
- package/dist/components/subviews/CustomSubview.js +15 -1
- package/dist/components/subviews/CustomSubview.js.map +1 -1
- package/dist/components/subviews/MarkdownSubview.d.ts +3 -1
- package/dist/components/subviews/MarkdownSubview.d.ts.map +1 -1
- package/dist/components/subviews/MarkdownSubview.js +62 -5
- package/dist/components/subviews/MarkdownSubview.js.map +1 -1
- package/dist/components/subviews/TableSubview.d.ts.map +1 -1
- package/dist/components/subviews/TableSubview.js +30 -3
- package/dist/components/subviews/TableSubview.js.map +1 -1
- package/dist/hooks/useContentLoader.d.ts.map +1 -1
- package/dist/hooks/useContentLoader.js +13 -7
- package/dist/hooks/useContentLoader.js.map +1 -1
- package/dist/hooks/useViewCommandContext.d.ts +28 -0
- package/dist/hooks/useViewCommandContext.d.ts.map +1 -1
- package/dist/hooks/useViewCommandContext.js.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/launch.d.ts +7 -0
- package/dist/launch.d.ts.map +1 -1
- package/dist/launch.js +1 -1
- package/dist/launch.js.map +1 -1
- package/dist/types.d.ts +10 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/package.json +10 -10
- package/src/View.tsx +10 -2
- package/src/components/subviews/CodeSubview.tsx +40 -3
- package/src/components/subviews/CustomSubview.tsx +18 -1
- package/src/components/subviews/MarkdownSubview.tsx +84 -3
- package/src/components/subviews/TableSubview.tsx +42 -3
- package/src/hooks/useContentLoader.ts +15 -7
- package/src/hooks/useViewCommandContext.ts +27 -0
- package/src/index.ts +2 -0
- package/src/launch.tsx +8 -0
- package/src/types.ts +10 -0
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAA;AAChC,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAA;AAClD,OAAO,EAAE,MAAM,EAAE,eAAe,EAAE,MAAM,aAAa,CAAA;AAErD,OAAO,EACL,kBAAkB,EAClB,mBAAmB,EACnB,uBAAuB,EACvB,wBAAwB,GACzB,MAAM,uBAAuB,CAAA;AAC9B,OAAO,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAA;AAgB5D,OAAO,EAAE,cAAc,EAAE,gBAAgB,EAAE,eAAe,EAAE,MAAM,YAAY,CAAA"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAA;AAChC,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAA;AAClD,OAAO,EAAE,MAAM,EAAE,eAAe,EAAE,MAAM,aAAa,CAAA;AAErD,OAAO,EACL,kBAAkB,EAClB,mBAAmB,EACnB,uBAAuB,EACvB,wBAAwB,GACzB,MAAM,uBAAuB,CAAA;AAC9B,OAAO,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAA;AAgB5D,OAAO,EAAE,cAAc,EAAE,gBAAgB,EAAE,eAAe,EAAE,MAAM,YAAY,CAAA;AAC9E,OAAO,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAA"}
|
package/dist/launch.d.ts
CHANGED
|
@@ -1,9 +1,16 @@
|
|
|
1
1
|
import type { ActionDefinition } from "@tooee/commands";
|
|
2
|
+
import type { CodeBlockRenderer } from "@tooee/renderers";
|
|
2
3
|
import type { ContentProvider, ContentRenderer } from "./types.js";
|
|
3
4
|
export interface ViewLaunchOptions {
|
|
4
5
|
contentProvider: ContentProvider;
|
|
5
6
|
actions?: ActionDefinition[];
|
|
6
7
|
renderers?: Record<string, ContentRenderer>;
|
|
8
|
+
/**
|
|
9
|
+
* Custom renderers for fenced code blocks in markdown content, keyed by
|
|
10
|
+
* fence type (first word of the fence info string, case-insensitive).
|
|
11
|
+
* Unmatched types fall back to the default syntax-highlighted code block.
|
|
12
|
+
*/
|
|
13
|
+
codeBlockRenderers?: Record<string, CodeBlockRenderer>;
|
|
7
14
|
}
|
|
8
15
|
export interface DirectoryLaunchOptions {
|
|
9
16
|
dirPath: string;
|
package/dist/launch.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"launch.d.ts","sourceRoot":"","sources":["../src/launch.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAA;
|
|
1
|
+
{"version":3,"file":"launch.d.ts","sourceRoot":"","sources":["../src/launch.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAA;AACvD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAA;AAGzD,OAAO,KAAK,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,YAAY,CAAA;AAElE,MAAM,WAAW,iBAAiB;IAChC,eAAe,EAAE,eAAe,CAAA;IAChC,OAAO,CAAC,EAAE,gBAAgB,EAAE,CAAA;IAC5B,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,eAAe,CAAC,CAAA;IAC3C;;;;OAIG;IACH,kBAAkB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,iBAAiB,CAAC,CAAA;CACvD;AAED,MAAM,WAAW,sBAAsB;IACrC,OAAO,EAAE,MAAM,CAAA;IACf,OAAO,CAAC,EAAE,gBAAgB,EAAE,CAAA;CAC7B;AAED,wBAAsB,MAAM,CAAC,OAAO,EAAE,iBAAiB,GAAG,OAAO,CAAC,IAAI,CAAC,CAStE;AAED,wBAAsB,eAAe,CAAC,OAAO,EAAE,sBAAsB,GAAG,OAAO,CAAC,IAAI,CAAC,CAEpF"}
|
package/dist/launch.js
CHANGED
|
@@ -3,7 +3,7 @@ import { launchCli } from "@tooee/shell";
|
|
|
3
3
|
import { View } from "./View.js";
|
|
4
4
|
import { DirectoryView } from "./DirectoryView.js";
|
|
5
5
|
export async function launch(options) {
|
|
6
|
-
await launchCli(_jsx(View, { contentProvider: options.contentProvider, actions: options.actions, renderers: options.renderers }));
|
|
6
|
+
await launchCli(_jsx(View, { contentProvider: options.contentProvider, actions: options.actions, renderers: options.renderers, codeBlockRenderers: options.codeBlockRenderers }));
|
|
7
7
|
}
|
|
8
8
|
export async function launchDirectory(options) {
|
|
9
9
|
await launchCli(_jsx(DirectoryView, { dirPath: options.dirPath, actions: options.actions }));
|
package/dist/launch.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"launch.js","sourceRoot":"","sources":["../src/launch.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAA;
|
|
1
|
+
{"version":3,"file":"launch.js","sourceRoot":"","sources":["../src/launch.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAA;AAGxC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAA;AAChC,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAA;AAoBlD,MAAM,CAAC,KAAK,UAAU,MAAM,CAAC,OAA0B;IACrD,MAAM,SAAS,CACb,KAAC,IAAI,IACH,eAAe,EAAE,OAAO,CAAC,eAAe,EACxC,OAAO,EAAE,OAAO,CAAC,OAAO,EACxB,SAAS,EAAE,OAAO,CAAC,SAAS,EAC5B,kBAAkB,EAAE,OAAO,CAAC,kBAAkB,GAC9C,CACH,CAAA;AACH,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,eAAe,CAAC,OAA+B;IACnE,MAAM,SAAS,CAAC,KAAC,aAAa,IAAC,OAAO,EAAE,OAAO,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC,OAAO,GAAI,CAAC,CAAA;AACxF,CAAC"}
|
package/dist/types.d.ts
CHANGED
|
@@ -41,6 +41,16 @@ export interface ContentRendererProps {
|
|
|
41
41
|
selectionEnd?: number;
|
|
42
42
|
/** Combined mark state (nav + provider + user marks) */
|
|
43
43
|
marks?: MarkState;
|
|
44
|
+
/**
|
|
45
|
+
* Select a row by index — the mouse equivalent of the keyboard cursor move.
|
|
46
|
+
* A custom renderer that has a notion of rows should call this from its own
|
|
47
|
+
* `onMouseDown`/`onRowClick` handlers to make left-click select a row, the
|
|
48
|
+
* same way the built-in code/markdown/table views do. Selection is a no-op
|
|
49
|
+
* while a modal overlay is open (the host guards it), so renderers can wire
|
|
50
|
+
* it unconditionally. Renderers that own a `RowDocumentRenderable` can map a
|
|
51
|
+
* click's screen-Y to a row via its `getRowAtScreenY(event.y)` helper.
|
|
52
|
+
*/
|
|
53
|
+
onSelectRow?: (index: number) => void;
|
|
44
54
|
}
|
|
45
55
|
export type ContentRenderer = (props: ContentRendererProps) => ReactNode;
|
|
46
56
|
export type AppendableFormat = Extract<ContentFormat, "markdown" | "code" | "text">;
|
package/dist/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AACtC,OAAO,KAAK,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAA;AAC3D,OAAO,KAAK,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAA;AAItD,MAAM,MAAM,OAAO,GAAG,eAAe,GAAG,WAAW,GAAG,WAAW,GAAG,YAAY,CAAA;AAEhF,MAAM,MAAM,aAAa,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAA;AAE7C,UAAU,WAAW;IACnB,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,cAAc,CAAC,EAAE,MAAM,MAAM,CAAA;CAC9B;AAED,MAAM,WAAW,eAAgB,SAAQ,WAAW;IAClD,MAAM,EAAE,UAAU,CAAA;IAClB,QAAQ,EAAE,MAAM,CAAA;CACjB;AAED,MAAM,WAAW,WAAY,SAAQ,WAAW;IAC9C,MAAM,EAAE,MAAM,CAAA;IACd,IAAI,EAAE,MAAM,CAAA;IACZ,QAAQ,CAAC,EAAE,MAAM,CAAA;CAClB;AAED,MAAM,WAAW,WAAY,SAAQ,WAAW;IAC9C,MAAM,EAAE,MAAM,CAAA;IACd,IAAI,EAAE,MAAM,CAAA;CACb;AAED,MAAM,WAAW,YAAa,SAAQ,WAAW;IAC/C,MAAM,EAAE,OAAO,CAAA;IACf,OAAO,EAAE,SAAS,EAAE,CAAA;IACpB,IAAI,EAAE,QAAQ,EAAE,CAAA;CACjB;AAID,MAAM,WAAW,aAAa,CAAC,CAAC,GAAG,OAAO;IACxC,MAAM,EAAE,MAAM,CAAA;IACd,IAAI,EAAE,CAAC,CAAA;IACP,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,cAAc,CAAC,EAAE,MAAM,MAAM,CAAA;CAC9B;AAED,yEAAyE;AACzE,MAAM,MAAM,UAAU,GAAG,OAAO,GAAG,aAAa,CAAA;AAIhD,MAAM,WAAW,oBAAoB;IACnC,OAAO,EAAE,aAAa,CAAA;IACtB,SAAS,EAAE,MAAM,CAAA;IACjB,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,wDAAwD;IACxD,KAAK,CAAC,EAAE,SAAS,CAAA;
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|
package/dist/types.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAiHA,oBAAoB;AAEpB,MAAM,eAAe,GAAgB,IAAI,GAAG,CAAC,CAAC,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC,CAAA;AAEnF,MAAM,UAAU,gBAAgB,CAAC,OAAmB;IAClD,OAAO,eAAe,CAAC,GAAG,CAAC,OAAO,CAAC,MAAM,CAAC,CAAA;AAC5C,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,OAAmB;IACjD,OAAO,CAAC,eAAe,CAAC,GAAG,CAAC,OAAO,CAAC,MAAM,CAAC,CAAA;AAC7C,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,OAAmB;IAChD,IAAI,OAAO,CAAC,cAAc,EAAE,CAAC;QAC3B,OAAO,OAAO,CAAC,cAAc,EAAE,CAAA;IACjC,CAAC;IAED,IAAI,eAAe,CAAC,OAAO,CAAC,EAAE,CAAC;QAC7B,OAAO,EAAE,CAAA;IACX,CAAC;IAED,QAAQ,OAAO,CAAC,MAAM,EAAE,CAAC;QACvB,KAAK,UAAU;YACb,OAAO,OAAO,CAAC,QAAQ,CAAA;QACzB,KAAK,MAAM;YACT,OAAO,OAAO,CAAC,IAAI,CAAA;QACrB,KAAK,MAAM;YACT,OAAO,OAAO,CAAC,IAAI,CAAA;QACrB,KAAK,OAAO,CAAC,CAAC,CAAC;YACb,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,MAAM,IAAI,MAAM,CAAC,GAAG,CAAC,CAAA;YAC5E,MAAM,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CACxC,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,aAAa,CAAC,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAC3E,CAAA;YACD,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,GAAG,QAAQ,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;QACrD,CAAC;QACD;YACE,OAAO,EAAE,CAAA;IACb,CAAC;AACH,CAAC;AAED,SAAS,aAAa,CAAC,KAAc;IACnC,IAAI,KAAK,IAAI,IAAI;QAAE,OAAO,EAAE,CAAA;IAC5B,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,OAAO,KAAK,CAAA;IAC3C,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,OAAO,KAAK,KAAK,SAAS;QAAE,OAAO,MAAM,CAAC,KAAK,CAAC,CAAA;IACjF,IAAI,CAAC;QACH,OAAO,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAA;IAC9B,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,MAAM,CAAC,KAAK,CAAC,CAAA;IACtB,CAAC;AACH,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tooee/view",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.21",
|
|
4
4
|
"description": "Terminal content viewer for markdown, code, text, and tables",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"cli",
|
|
@@ -37,15 +37,15 @@
|
|
|
37
37
|
"typecheck": "tsc --noEmit"
|
|
38
38
|
},
|
|
39
39
|
"dependencies": {
|
|
40
|
-
"@tooee/commands": "0.1.
|
|
41
|
-
"@tooee/config": "0.1.
|
|
42
|
-
"@tooee/layout": "0.1.
|
|
43
|
-
"@tooee/marks": "0.1.
|
|
44
|
-
"@tooee/overlays": "0.1.
|
|
45
|
-
"@tooee/renderers": "0.1.
|
|
46
|
-
"@tooee/search": "0.1.
|
|
47
|
-
"@tooee/shell": "0.1.
|
|
48
|
-
"@tooee/themes": "0.1.
|
|
40
|
+
"@tooee/commands": "0.1.21",
|
|
41
|
+
"@tooee/config": "0.1.21",
|
|
42
|
+
"@tooee/layout": "0.1.21",
|
|
43
|
+
"@tooee/marks": "0.1.21",
|
|
44
|
+
"@tooee/overlays": "0.1.21",
|
|
45
|
+
"@tooee/renderers": "0.1.21",
|
|
46
|
+
"@tooee/search": "0.1.21",
|
|
47
|
+
"@tooee/shell": "0.1.21",
|
|
48
|
+
"@tooee/themes": "0.1.21",
|
|
49
49
|
"marked": "^17.0.4"
|
|
50
50
|
},
|
|
51
51
|
"devDependencies": {
|
package/src/View.tsx
CHANGED
|
@@ -2,6 +2,7 @@ import { useState, useCallback } 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
6
|
import { isCustomContent, type ContentProvider, type ContentRenderer } from "./types.js"
|
|
6
7
|
import { useContentLoader } from "./hooks/useContentLoader.js"
|
|
7
8
|
import {
|
|
@@ -15,9 +16,14 @@ interface ViewProps {
|
|
|
15
16
|
contentProvider: ContentProvider
|
|
16
17
|
actions?: ActionDefinition[]
|
|
17
18
|
renderers?: Record<string, ContentRenderer>
|
|
19
|
+
/**
|
|
20
|
+
* Custom renderers for fenced code blocks in markdown content, keyed by
|
|
21
|
+
* fence type (first word of the fence info string, case-insensitive).
|
|
22
|
+
*/
|
|
23
|
+
codeBlockRenderers?: Record<string, CodeBlockRenderer>
|
|
18
24
|
}
|
|
19
25
|
|
|
20
|
-
export function View({ contentProvider, actions, renderers }: ViewProps) {
|
|
26
|
+
export function View({ contentProvider, actions, renderers, codeBlockRenderers }: ViewProps) {
|
|
21
27
|
const { theme } = useTheme()
|
|
22
28
|
const [reloadTrigger, setReloadTrigger] = useState(0)
|
|
23
29
|
const reload = useCallback(() => setReloadTrigger((n) => n + 1), [])
|
|
@@ -77,7 +83,9 @@ export function View({ contentProvider, actions, renderers }: ViewProps) {
|
|
|
77
83
|
|
|
78
84
|
switch (content.format) {
|
|
79
85
|
case "markdown":
|
|
80
|
-
return
|
|
86
|
+
return (
|
|
87
|
+
<MarkdownSubview content={content} codeBlockRenderers={codeBlockRenderers} {...shared} />
|
|
88
|
+
)
|
|
81
89
|
case "code":
|
|
82
90
|
case "text":
|
|
83
91
|
return <CodeSubview content={content} {...shared} />
|
|
@@ -1,8 +1,10 @@
|
|
|
1
|
-
import { useEffect, useMemo, useRef } from "react"
|
|
2
|
-
import { CodeView, type RowDocumentRenderable } from "@tooee/renderers"
|
|
1
|
+
import { useCallback, useEffect, useMemo, useRef } from "react"
|
|
2
|
+
import { CodeView, type ContextMenuEntry, type RowDocumentRenderable } from "@tooee/renderers"
|
|
3
3
|
import { useTheme } from "@tooee/themes"
|
|
4
|
+
import { useCommandContext } from "@tooee/commands"
|
|
5
|
+
import { useHasModalOverlay } from "@tooee/overlays"
|
|
4
6
|
import { useViewCommandContext } from "../../hooks/useViewCommandContext.js"
|
|
5
|
-
import { useCopy, useNavigation } from "@tooee/shell"
|
|
7
|
+
import { useContextMenu, useCopy, useNavigation } from "@tooee/shell"
|
|
6
8
|
import { findMatchingLines, useSearch } from "@tooee/search"
|
|
7
9
|
import type { CodeContent, TextContent } from "../../types.js"
|
|
8
10
|
import { useMarkState } from "../../hooks/useMarkState.js"
|
|
@@ -27,6 +29,9 @@ export function CodeSubview({
|
|
|
27
29
|
}: CodeSubviewProps) {
|
|
28
30
|
const { theme } = useTheme()
|
|
29
31
|
const docRef = useRef<RowDocumentRenderable>(null)
|
|
32
|
+
const hasModalOverlay = useHasModalOverlay()
|
|
33
|
+
const contextMenu = useContextMenu()
|
|
34
|
+
const { invoke } = useCommandContext()
|
|
30
35
|
const textContent = content.format === "code" ? content.code : content.text
|
|
31
36
|
const lines = useMemo(() => textContent.split("\n"), [textContent])
|
|
32
37
|
const lineCount = lines.length
|
|
@@ -76,6 +81,36 @@ export function CodeSubview({
|
|
|
76
81
|
|
|
77
82
|
const text = content.format === "code" ? content.code : content.text
|
|
78
83
|
|
|
84
|
+
const menuEntries = useMemo<ContextMenuEntry[]>(
|
|
85
|
+
() =>
|
|
86
|
+
(actions ?? [])
|
|
87
|
+
.filter((action) => !action.hidden)
|
|
88
|
+
.map((action) => ({ id: action.id, title: action.title, hotkey: action.hotkey })),
|
|
89
|
+
[actions],
|
|
90
|
+
)
|
|
91
|
+
|
|
92
|
+
// Left-click selects the clicked line; right-click selects it and opens the
|
|
93
|
+
// same app-provided action menu as table rows. Both stand down while a modal
|
|
94
|
+
// overlay is up (theme picker, command palette, Ask/Choose), matching the
|
|
95
|
+
// keyboard stand-down and avoiding covered-app mutations.
|
|
96
|
+
const setCursor = nav.setCursor
|
|
97
|
+
const openContextMenu = contextMenu.open
|
|
98
|
+
const handleRowClick = useCallback(
|
|
99
|
+
(index: number) => {
|
|
100
|
+
if (hasModalOverlay) return
|
|
101
|
+
setCursor(index)
|
|
102
|
+
},
|
|
103
|
+
[hasModalOverlay, setCursor],
|
|
104
|
+
)
|
|
105
|
+
const handleRowContextMenu = useCallback(
|
|
106
|
+
(index: number, x: number, y: number) => {
|
|
107
|
+
if (hasModalOverlay) return
|
|
108
|
+
setCursor(index)
|
|
109
|
+
openContextMenu(x, y, menuEntries, invoke)
|
|
110
|
+
},
|
|
111
|
+
[hasModalOverlay, setCursor, openContextMenu, menuEntries, invoke],
|
|
112
|
+
)
|
|
113
|
+
|
|
79
114
|
const extraStatusItems = useMemo(() => {
|
|
80
115
|
const selectionCount = nav.selection != null ? nav.selection.end - nav.selection.start + 1 : 0
|
|
81
116
|
const toggledCount = nav.toggledIndices.size
|
|
@@ -106,6 +141,8 @@ export function CodeSubview({
|
|
|
106
141
|
showLineNumbers={showLineNumbers}
|
|
107
142
|
marks={markState}
|
|
108
143
|
docRef={docRef}
|
|
144
|
+
onRowClick={handleRowClick}
|
|
145
|
+
onRowContextMenu={handleRowContextMenu}
|
|
109
146
|
/>
|
|
110
147
|
</SubviewLayout>
|
|
111
148
|
)
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import { useEffect, useMemo, useRef } from "react"
|
|
1
|
+
import { useCallback, useEffect, useMemo, useRef } from "react"
|
|
2
2
|
import { CodeView, type RowDocumentRenderable } from "@tooee/renderers"
|
|
3
3
|
import { useTheme } from "@tooee/themes"
|
|
4
|
+
import { useHasModalOverlay } from "@tooee/overlays"
|
|
4
5
|
import { useViewCommandContext } from "../../hooks/useViewCommandContext.js"
|
|
5
6
|
import { useCopy, useNavigation } from "@tooee/shell"
|
|
6
7
|
import { findMatchingLines, useSearch } from "@tooee/search"
|
|
@@ -29,6 +30,7 @@ export function CustomSubview({
|
|
|
29
30
|
}: CustomSubviewProps) {
|
|
30
31
|
const { theme } = useTheme()
|
|
31
32
|
const docRef = useRef<RowDocumentRenderable>(null)
|
|
33
|
+
const hasModalOverlay = useHasModalOverlay()
|
|
32
34
|
const textContent = useMemo(() => getTextContent(content), [content])
|
|
33
35
|
const lines = useMemo(() => textContent.split("\n"), [textContent])
|
|
34
36
|
const lineCount = lines.length
|
|
@@ -92,6 +94,20 @@ export function CustomSubview({
|
|
|
92
94
|
]
|
|
93
95
|
}, [content.format, lineCount, nav.selection, nav.toggledIndices])
|
|
94
96
|
|
|
97
|
+
// Left-click selection for custom renderers. The framework does not own the
|
|
98
|
+
// renderer's markup, so it cannot map clicks to rows itself; instead it hands
|
|
99
|
+
// the renderer this callback (the mouse equivalent of the keyboard cursor
|
|
100
|
+
// move) to call from its own handlers. Guarded like the built-in views so a
|
|
101
|
+
// click never mutates the covered app while a modal overlay is up.
|
|
102
|
+
const setCursor = nav.setCursor
|
|
103
|
+
const handleSelectRow = useCallback(
|
|
104
|
+
(index: number) => {
|
|
105
|
+
if (hasModalOverlay) return
|
|
106
|
+
setCursor(index)
|
|
107
|
+
},
|
|
108
|
+
[hasModalOverlay, setCursor],
|
|
109
|
+
)
|
|
110
|
+
|
|
95
111
|
const customRenderer = renderers?.[content.format]
|
|
96
112
|
if (customRenderer) {
|
|
97
113
|
const cursorLine = nav.cursor ?? undefined
|
|
@@ -113,6 +129,7 @@ export function CustomSubview({
|
|
|
113
129
|
selectionStart,
|
|
114
130
|
selectionEnd,
|
|
115
131
|
marks: markState,
|
|
132
|
+
onSelectRow: handleSelectRow,
|
|
116
133
|
})}
|
|
117
134
|
</SubviewLayout>
|
|
118
135
|
)
|
|
@@ -1,9 +1,18 @@
|
|
|
1
|
-
import { useEffect, useMemo, useRef } from "react"
|
|
1
|
+
import { useCallback, useEffect, useMemo, useRef } from "react"
|
|
2
2
|
import { marked } from "marked"
|
|
3
|
-
import {
|
|
3
|
+
import type { TextBufferRenderable } from "@opentui/core"
|
|
4
|
+
import {
|
|
5
|
+
MarkdownView,
|
|
6
|
+
flattenTokens,
|
|
7
|
+
type CodeBlockRenderer,
|
|
8
|
+
type ContextMenuEntry,
|
|
9
|
+
type RowDocumentRenderable,
|
|
10
|
+
} from "@tooee/renderers"
|
|
4
11
|
import { useTheme } from "@tooee/themes"
|
|
12
|
+
import { useCommand, useCommandContext } from "@tooee/commands"
|
|
13
|
+
import { useHasModalOverlay } from "@tooee/overlays"
|
|
5
14
|
import { useViewCommandContext } from "../../hooks/useViewCommandContext.js"
|
|
6
|
-
import { useCopy, useNavigation } from "@tooee/shell"
|
|
15
|
+
import { useContextMenu, useCopy, useNavigation } from "@tooee/shell"
|
|
7
16
|
import { useSearch } from "@tooee/search"
|
|
8
17
|
import type { MarkdownContent } from "../../types.js"
|
|
9
18
|
import { useMarkState } from "../../hooks/useMarkState.js"
|
|
@@ -13,10 +22,15 @@ import type { SubviewProps } from "./types.js"
|
|
|
13
22
|
|
|
14
23
|
interface MarkdownSubviewProps extends SubviewProps {
|
|
15
24
|
content: MarkdownContent
|
|
25
|
+
codeBlockRenderers?: Record<string, CodeBlockRenderer>
|
|
16
26
|
}
|
|
17
27
|
|
|
28
|
+
/** Columns moved per h/l press when scrolling a wide block horizontally. */
|
|
29
|
+
const BLOCK_HSCROLL_STEP = 4
|
|
30
|
+
|
|
18
31
|
export function MarkdownSubview({
|
|
19
32
|
content,
|
|
33
|
+
codeBlockRenderers,
|
|
20
34
|
providerMarks,
|
|
21
35
|
userMarks,
|
|
22
36
|
setMarkSet,
|
|
@@ -28,6 +42,9 @@ export function MarkdownSubview({
|
|
|
28
42
|
}: MarkdownSubviewProps) {
|
|
29
43
|
const { theme } = useTheme()
|
|
30
44
|
const docRef = useRef<RowDocumentRenderable>(null)
|
|
45
|
+
const hasModalOverlay = useHasModalOverlay()
|
|
46
|
+
const contextMenu = useContextMenu()
|
|
47
|
+
const { invoke } = useCommandContext()
|
|
31
48
|
const textContent = content.markdown
|
|
32
49
|
const lineCount = useMemo(() => textContent.split("\n").length, [textContent])
|
|
33
50
|
const blocks = useMemo(() => flattenTokens(marked.lexer(content.markdown)), [content.markdown])
|
|
@@ -66,6 +83,66 @@ export function MarkdownSubview({
|
|
|
66
83
|
}
|
|
67
84
|
}, [nav.cursor])
|
|
68
85
|
|
|
86
|
+
// Horizontal panning for wide blocks (mermaid diagrams and code blocks).
|
|
87
|
+
// Blocks that can overflow horizontally register their text-buffer
|
|
88
|
+
// renderable by block index; h/l in cursor mode pans the block under the
|
|
89
|
+
// nav cursor via `scrollX` (the setter clamps to the content width).
|
|
90
|
+
const hScrollableBlocksRef = useRef<Map<number, TextBufferRenderable>>(new Map())
|
|
91
|
+
const cursorScrollable = () =>
|
|
92
|
+
nav.cursor !== null ? hScrollableBlocksRef.current.get(nav.cursor) : undefined
|
|
93
|
+
useCommand({
|
|
94
|
+
id: "block-scroll-left",
|
|
95
|
+
title: "Scroll block left",
|
|
96
|
+
hotkey: "h",
|
|
97
|
+
modes: ["cursor"],
|
|
98
|
+
when: () => cursorScrollable() != null,
|
|
99
|
+
handler: () => {
|
|
100
|
+
const target = cursorScrollable()
|
|
101
|
+
if (target) target.scrollX -= BLOCK_HSCROLL_STEP
|
|
102
|
+
},
|
|
103
|
+
})
|
|
104
|
+
useCommand({
|
|
105
|
+
id: "block-scroll-right",
|
|
106
|
+
title: "Scroll block right",
|
|
107
|
+
hotkey: "l",
|
|
108
|
+
modes: ["cursor"],
|
|
109
|
+
when: () => cursorScrollable() != null,
|
|
110
|
+
handler: () => {
|
|
111
|
+
const target = cursorScrollable()
|
|
112
|
+
if (target) target.scrollX += BLOCK_HSCROLL_STEP
|
|
113
|
+
},
|
|
114
|
+
})
|
|
115
|
+
|
|
116
|
+
const menuEntries = useMemo<ContextMenuEntry[]>(
|
|
117
|
+
() =>
|
|
118
|
+
(actions ?? [])
|
|
119
|
+
.filter((action) => !action.hidden)
|
|
120
|
+
.map((action) => ({ id: action.id, title: action.title, hotkey: action.hotkey })),
|
|
121
|
+
[actions],
|
|
122
|
+
)
|
|
123
|
+
|
|
124
|
+
// Left-click selects the clicked block; right-click selects it and opens the
|
|
125
|
+
// same app-provided action menu as table/code rows. Both stand down while a
|
|
126
|
+
// modal overlay is up (theme picker, command palette, Ask/Choose), matching
|
|
127
|
+
// keyboard stand-down semantics.
|
|
128
|
+
const setCursor = nav.setCursor
|
|
129
|
+
const openContextMenu = contextMenu.open
|
|
130
|
+
const handleRowClick = useCallback(
|
|
131
|
+
(index: number) => {
|
|
132
|
+
if (hasModalOverlay) return
|
|
133
|
+
setCursor(index)
|
|
134
|
+
},
|
|
135
|
+
[hasModalOverlay, setCursor],
|
|
136
|
+
)
|
|
137
|
+
const handleRowContextMenu = useCallback(
|
|
138
|
+
(index: number, x: number, y: number) => {
|
|
139
|
+
if (hasModalOverlay) return
|
|
140
|
+
setCursor(index)
|
|
141
|
+
openContextMenu(x, y, menuEntries, invoke)
|
|
142
|
+
},
|
|
143
|
+
[hasModalOverlay, setCursor, openContextMenu, menuEntries, invoke],
|
|
144
|
+
)
|
|
145
|
+
|
|
69
146
|
const { themeName, showLineNumbers } = useViewCommands({ content, textContent, actions })
|
|
70
147
|
|
|
71
148
|
const markState = useMarkState({
|
|
@@ -116,6 +193,10 @@ export function MarkdownSubview({
|
|
|
116
193
|
showLineNumbers={showLineNumbers}
|
|
117
194
|
marks={markState}
|
|
118
195
|
docRef={docRef}
|
|
196
|
+
hScrollableBlocksRef={hScrollableBlocksRef}
|
|
197
|
+
codeBlockRenderers={codeBlockRenderers}
|
|
198
|
+
onRowClick={handleRowClick}
|
|
199
|
+
onRowContextMenu={handleRowContextMenu}
|
|
119
200
|
/>
|
|
120
201
|
</SubviewLayout>
|
|
121
202
|
)
|
|
@@ -1,8 +1,10 @@
|
|
|
1
|
-
import { useEffect, useMemo, useRef } from "react"
|
|
2
|
-
import { Table, type RowDocumentRenderable } from "@tooee/renderers"
|
|
1
|
+
import { useCallback, useEffect, useMemo, useRef } from "react"
|
|
2
|
+
import { Table, type RowDocumentRenderable, type ContextMenuEntry } from "@tooee/renderers"
|
|
3
3
|
import { useTheme } from "@tooee/themes"
|
|
4
|
+
import { useCommandContext } from "@tooee/commands"
|
|
5
|
+
import { useHasModalOverlay } from "@tooee/overlays"
|
|
4
6
|
import { useViewCommandContext } from "../../hooks/useViewCommandContext.js"
|
|
5
|
-
import { useCopy, useNavigation } from "@tooee/shell"
|
|
7
|
+
import { useContextMenu, useCopy, useNavigation } from "@tooee/shell"
|
|
6
8
|
import { useSearch } from "@tooee/search"
|
|
7
9
|
import { getTextContent, type TableContent } from "../../types.js"
|
|
8
10
|
import { useMarkState } from "../../hooks/useMarkState.js"
|
|
@@ -28,6 +30,9 @@ export function TableSubview({
|
|
|
28
30
|
const { theme } = useTheme()
|
|
29
31
|
const docRef = useRef<RowDocumentRenderable>(null)
|
|
30
32
|
const textContent = useMemo(() => getTextContent(content), [content])
|
|
33
|
+
const contextMenu = useContextMenu()
|
|
34
|
+
const { invoke } = useCommandContext()
|
|
35
|
+
const hasModalOverlay = useHasModalOverlay()
|
|
31
36
|
|
|
32
37
|
const nav = useNavigation({
|
|
33
38
|
rowCount: content.rows.length,
|
|
@@ -107,6 +112,38 @@ export function TableSubview({
|
|
|
107
112
|
},
|
|
108
113
|
})
|
|
109
114
|
|
|
115
|
+
// Row-scoped context-menu entries come from the app-provided actions.
|
|
116
|
+
const menuEntries = useMemo<ContextMenuEntry[]>(
|
|
117
|
+
() =>
|
|
118
|
+
(actions ?? [])
|
|
119
|
+
.filter((action) => !action.hidden)
|
|
120
|
+
.map((action) => ({ id: action.id, title: action.title, hotkey: action.hotkey })),
|
|
121
|
+
[actions],
|
|
122
|
+
)
|
|
123
|
+
|
|
124
|
+
// Row mouse handlers stand down while a modal overlay (theme picker, command
|
|
125
|
+
// palette, Ask/Choose overlays, the context menu itself) is up: centered
|
|
126
|
+
// overlays leave clickable margins around them, and mouse events route
|
|
127
|
+
// through the hit-grid, bypassing command-surface arbitration. Memoized so
|
|
128
|
+
// Table's rowElements memo keeps doing its job.
|
|
129
|
+
const setCursor = nav.setCursor
|
|
130
|
+
const openContextMenu = contextMenu.open
|
|
131
|
+
const handleRowClick = useCallback(
|
|
132
|
+
(index: number) => {
|
|
133
|
+
if (hasModalOverlay) return
|
|
134
|
+
setCursor(index)
|
|
135
|
+
},
|
|
136
|
+
[hasModalOverlay, setCursor],
|
|
137
|
+
)
|
|
138
|
+
const handleRowContextMenu = useCallback(
|
|
139
|
+
(index: number, x: number, y: number) => {
|
|
140
|
+
if (hasModalOverlay) return
|
|
141
|
+
setCursor(index)
|
|
142
|
+
openContextMenu(x, y, menuEntries, invoke)
|
|
143
|
+
},
|
|
144
|
+
[hasModalOverlay, setCursor, openContextMenu, menuEntries, invoke],
|
|
145
|
+
)
|
|
146
|
+
|
|
110
147
|
const extraStatusItems = useMemo(() => {
|
|
111
148
|
const selectionCount = nav.selection != null ? nav.selection.end - nav.selection.start + 1 : 0
|
|
112
149
|
const toggledCount = nav.toggledIndices.size
|
|
@@ -144,6 +181,8 @@ export function TableSubview({
|
|
|
144
181
|
showLineNumbers={showLineNumbers}
|
|
145
182
|
marks={markState}
|
|
146
183
|
docRef={docRef}
|
|
184
|
+
onRowClick={handleRowClick}
|
|
185
|
+
onRowContextMenu={handleRowContextMenu}
|
|
147
186
|
/>
|
|
148
187
|
</SubviewLayout>
|
|
149
188
|
)
|
|
@@ -92,12 +92,18 @@ export function useContentLoader(contentProvider: ContentProvider, reloadTrigger
|
|
|
92
92
|
setContent(current)
|
|
93
93
|
setStreaming(true)
|
|
94
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.
|
|
98
|
+
const iterator = loaded[Symbol.asyncIterator]()
|
|
99
|
+
|
|
95
100
|
;(async () => {
|
|
96
101
|
try {
|
|
97
|
-
|
|
98
|
-
|
|
102
|
+
while (true) {
|
|
103
|
+
const result = await iterator.next()
|
|
104
|
+
if (cancelled || result.done) break
|
|
105
|
+
const chunk = result.value
|
|
99
106
|
if (chunk.type === "marks") {
|
|
100
|
-
if (cancelled) break
|
|
101
107
|
// Streamed mark set: merge by replacing any existing set with same namespace
|
|
102
108
|
setProviderMarks((prev) => {
|
|
103
109
|
const filtered = prev.filter((s) => s.namespace !== chunk.set.namespace)
|
|
@@ -109,8 +115,8 @@ export function useContentLoader(contentProvider: ContentProvider, reloadTrigger
|
|
|
109
115
|
setContent(current)
|
|
110
116
|
}
|
|
111
117
|
} catch (err) {
|
|
112
|
-
if (!cancelled
|
|
113
|
-
setError(err.message)
|
|
118
|
+
if (!cancelled) {
|
|
119
|
+
setError(err instanceof Error ? err.message : String(err))
|
|
114
120
|
}
|
|
115
121
|
} finally {
|
|
116
122
|
if (!cancelled) {
|
|
@@ -121,6 +127,8 @@ export function useContentLoader(contentProvider: ContentProvider, reloadTrigger
|
|
|
121
127
|
|
|
122
128
|
return () => {
|
|
123
129
|
cancelled = true
|
|
130
|
+
// Close the iterator to release provider resources mid-stream.
|
|
131
|
+
Promise.resolve(iterator.return?.()).catch(() => {})
|
|
124
132
|
}
|
|
125
133
|
}
|
|
126
134
|
|
|
@@ -130,8 +138,8 @@ export function useContentLoader(contentProvider: ContentProvider, reloadTrigger
|
|
|
130
138
|
.then((value) => {
|
|
131
139
|
if (active) setContent(value)
|
|
132
140
|
})
|
|
133
|
-
.catch((err:
|
|
134
|
-
if (active) setError(err.message)
|
|
141
|
+
.catch((err: unknown) => {
|
|
142
|
+
if (active) setError(err instanceof Error ? err.message : String(err))
|
|
135
143
|
})
|
|
136
144
|
return () => {
|
|
137
145
|
active = false
|
|
@@ -1,8 +1,35 @@
|
|
|
1
1
|
import { useProvideCommandContext, useMode } from "@tooee/commands"
|
|
2
|
+
import type { Mode } from "@tooee/commands"
|
|
2
3
|
import type { NavigationState } from "@tooee/shell"
|
|
3
4
|
import type { MarkSet } from "@tooee/marks"
|
|
4
5
|
import type { AnyContent } from "../types.js"
|
|
5
6
|
|
|
7
|
+
/** The `view` field contributed to the command context (see augmentation below). */
|
|
8
|
+
export interface ViewCommandContext {
|
|
9
|
+
content: AnyContent
|
|
10
|
+
format: string
|
|
11
|
+
cursor: number | null
|
|
12
|
+
selection: { start: number; end: number } | null
|
|
13
|
+
mode: Mode
|
|
14
|
+
toggledIndices: Set<number>
|
|
15
|
+
reload: () => void
|
|
16
|
+
marks: {
|
|
17
|
+
setMarkSet: (set: MarkSet) => void
|
|
18
|
+
clearNamespace: (namespace: string) => void
|
|
19
|
+
clearAll: () => void
|
|
20
|
+
userMarks: MarkSet[]
|
|
21
|
+
providerMarks: MarkSet[]
|
|
22
|
+
}
|
|
23
|
+
/** Subview extras (e.g. activeRow/selectedRows for tables). */
|
|
24
|
+
[key: string]: unknown
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
declare module "@tooee/commands" {
|
|
28
|
+
interface CommandContext {
|
|
29
|
+
view: ViewCommandContext
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
6
33
|
interface UseViewCommandContextParams {
|
|
7
34
|
content: AnyContent
|
|
8
35
|
nav: NavigationState
|
package/src/index.ts
CHANGED
|
@@ -25,3 +25,5 @@ export type {
|
|
|
25
25
|
TableRow,
|
|
26
26
|
} from "./types.js"
|
|
27
27
|
export { getTextContent, isBuiltinContent, isCustomContent } from "./types.js"
|
|
28
|
+
export { CodeBlockChrome } from "@tooee/renderers"
|
|
29
|
+
export type { CodeBlockRenderer, CodeBlockRendererProps, CodeBlockHScroll } from "@tooee/renderers"
|
package/src/launch.tsx
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { launchCli } from "@tooee/shell"
|
|
2
2
|
import type { ActionDefinition } from "@tooee/commands"
|
|
3
|
+
import type { CodeBlockRenderer } from "@tooee/renderers"
|
|
3
4
|
import { View } from "./View.js"
|
|
4
5
|
import { DirectoryView } from "./DirectoryView.js"
|
|
5
6
|
import type { ContentProvider, ContentRenderer } from "./types.js"
|
|
@@ -8,6 +9,12 @@ export interface ViewLaunchOptions {
|
|
|
8
9
|
contentProvider: ContentProvider
|
|
9
10
|
actions?: ActionDefinition[]
|
|
10
11
|
renderers?: Record<string, ContentRenderer>
|
|
12
|
+
/**
|
|
13
|
+
* Custom renderers for fenced code blocks in markdown content, keyed by
|
|
14
|
+
* fence type (first word of the fence info string, case-insensitive).
|
|
15
|
+
* Unmatched types fall back to the default syntax-highlighted code block.
|
|
16
|
+
*/
|
|
17
|
+
codeBlockRenderers?: Record<string, CodeBlockRenderer>
|
|
11
18
|
}
|
|
12
19
|
|
|
13
20
|
export interface DirectoryLaunchOptions {
|
|
@@ -21,6 +28,7 @@ export async function launch(options: ViewLaunchOptions): Promise<void> {
|
|
|
21
28
|
contentProvider={options.contentProvider}
|
|
22
29
|
actions={options.actions}
|
|
23
30
|
renderers={options.renderers}
|
|
31
|
+
codeBlockRenderers={options.codeBlockRenderers}
|
|
24
32
|
/>,
|
|
25
33
|
)
|
|
26
34
|
}
|
package/src/types.ts
CHANGED
|
@@ -57,6 +57,16 @@ export interface ContentRendererProps {
|
|
|
57
57
|
selectionEnd?: number
|
|
58
58
|
/** Combined mark state (nav + provider + user marks) */
|
|
59
59
|
marks?: MarkState
|
|
60
|
+
/**
|
|
61
|
+
* Select a row by index — the mouse equivalent of the keyboard cursor move.
|
|
62
|
+
* A custom renderer that has a notion of rows should call this from its own
|
|
63
|
+
* `onMouseDown`/`onRowClick` handlers to make left-click select a row, the
|
|
64
|
+
* same way the built-in code/markdown/table views do. Selection is a no-op
|
|
65
|
+
* while a modal overlay is open (the host guards it), so renderers can wire
|
|
66
|
+
* it unconditionally. Renderers that own a `RowDocumentRenderable` can map a
|
|
67
|
+
* click's screen-Y to a row via its `getRowAtScreenY(event.y)` helper.
|
|
68
|
+
*/
|
|
69
|
+
onSelectRow?: (index: number) => void
|
|
60
70
|
}
|
|
61
71
|
|
|
62
72
|
export type ContentRenderer = (props: ContentRendererProps) => ReactNode
|