@tooee/view 0.7.3 → 0.8.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.
Files changed (89) hide show
  1. package/dist/components/subviews/code-subview.d.ts +2 -1
  2. package/dist/components/subviews/code-subview.d.ts.map +1 -1
  3. package/dist/components/subviews/code-subview.js +2 -15
  4. package/dist/components/subviews/code-subview.js.map +1 -1
  5. package/dist/components/subviews/custom-subview.d.ts +2 -1
  6. package/dist/components/subviews/custom-subview.d.ts.map +1 -1
  7. package/dist/components/subviews/custom-subview.js +3 -13
  8. package/dist/components/subviews/custom-subview.js.map +1 -1
  9. package/dist/components/subviews/diff-subview.d.ts +2 -1
  10. package/dist/components/subviews/diff-subview.d.ts.map +1 -1
  11. package/dist/components/subviews/diff-subview.js +23 -33
  12. package/dist/components/subviews/diff-subview.js.map +1 -1
  13. package/dist/components/subviews/image-subview.d.ts +2 -1
  14. package/dist/components/subviews/image-subview.d.ts.map +1 -1
  15. package/dist/components/subviews/image-subview.js +4 -9
  16. package/dist/components/subviews/image-subview.js.map +1 -1
  17. package/dist/components/subviews/markdown-subview.d.ts +2 -1
  18. package/dist/components/subviews/markdown-subview.d.ts.map +1 -1
  19. package/dist/components/subviews/markdown-subview.js +8 -15
  20. package/dist/components/subviews/markdown-subview.js.map +1 -1
  21. package/dist/components/subviews/table-subview.d.ts +2 -1
  22. package/dist/components/subviews/table-subview.d.ts.map +1 -1
  23. package/dist/components/subviews/table-subview.js +9 -48
  24. package/dist/components/subviews/table-subview.js.map +1 -1
  25. package/dist/components/view-state.d.ts +2 -1
  26. package/dist/components/view-state.d.ts.map +1 -1
  27. package/dist/components/view-state.js.map +1 -1
  28. package/dist/content-loader-store.d.ts +3 -2
  29. package/dist/content-loader-store.d.ts.map +1 -1
  30. package/dist/content-loader-store.js +5 -4
  31. package/dist/content-loader-store.js.map +1 -1
  32. package/dist/default-provider.d.ts.map +1 -1
  33. package/dist/default-provider.js +29 -29
  34. package/dist/default-provider.js.map +1 -1
  35. package/dist/directory-view.d.ts +2 -1
  36. package/dist/directory-view.d.ts.map +1 -1
  37. package/dist/directory-view.js +2 -1
  38. package/dist/directory-view.js.map +1 -1
  39. package/dist/hooks/use-content-commands.d.ts +5 -3
  40. package/dist/hooks/use-content-commands.d.ts.map +1 -1
  41. package/dist/hooks/use-content-commands.js.map +1 -1
  42. package/dist/hooks/use-content-document.d.ts +28 -0
  43. package/dist/hooks/use-content-document.d.ts.map +1 -0
  44. package/dist/hooks/use-content-document.js +21 -0
  45. package/dist/hooks/use-content-document.js.map +1 -0
  46. package/dist/hooks/use-content-loader.d.ts +10 -6
  47. package/dist/hooks/use-content-loader.d.ts.map +1 -1
  48. package/dist/hooks/use-content-loader.js +1 -1
  49. package/dist/hooks/use-content-loader.js.map +1 -1
  50. package/dist/hooks/use-view-command-context.d.ts +4 -2
  51. package/dist/hooks/use-view-command-context.d.ts.map +1 -1
  52. package/dist/hooks/use-view-command-context.js +6 -1
  53. package/dist/hooks/use-view-command-context.js.map +1 -1
  54. package/dist/index.d.ts +2 -0
  55. package/dist/index.d.ts.map +1 -1
  56. package/dist/index.js.map +1 -1
  57. package/dist/launch.d.ts.map +1 -1
  58. package/dist/launch.js +2 -2
  59. package/dist/launch.js.map +1 -1
  60. package/dist/types.d.ts.map +1 -1
  61. package/dist/types.js +3 -33
  62. package/dist/types.js.map +1 -1
  63. package/dist/view.d.ts +2 -1
  64. package/dist/view.d.ts.map +1 -1
  65. package/dist/view.js.map +1 -1
  66. package/package.json +12 -11
  67. package/src/components/subviews/code-subview.tsx +8 -20
  68. package/src/components/subviews/custom-subview.tsx +9 -19
  69. package/src/components/subviews/diff-subview.tsx +35 -43
  70. package/src/components/subviews/image-subview.tsx +12 -11
  71. package/src/components/subviews/markdown-subview.tsx +16 -22
  72. package/src/components/subviews/table-subview.tsx +16 -56
  73. package/src/components/view-state.tsx +2 -1
  74. package/src/content-loader-store.ts +8 -5
  75. package/src/default-provider.ts +34 -32
  76. package/src/directory-view.tsx +4 -2
  77. package/src/hooks/use-content-commands.ts +6 -1
  78. package/src/hooks/use-content-document.ts +54 -0
  79. package/src/hooks/use-content-loader.ts +17 -3
  80. package/src/hooks/use-view-command-context.ts +16 -4
  81. package/src/index.ts +2 -0
  82. package/src/launch.tsx +11 -8
  83. package/src/types.ts +4 -35
  84. package/src/view.tsx +2 -1
  85. package/dist/hooks/index.d.ts +0 -5
  86. package/dist/hooks/index.d.ts.map +0 -1
  87. package/dist/hooks/index.js +0 -4
  88. package/dist/hooks/index.js.map +0 -1
  89. package/src/hooks/index.ts +0 -11
@@ -8,6 +8,11 @@ interface UseContentCommandsParams {
8
8
  textContent: string;
9
9
  }
10
10
 
11
+ /** View display state controlled by content-level commands. */
12
+ export interface ContentCommandsResult {
13
+ showLineNumbers: boolean;
14
+ }
15
+
11
16
  /**
12
17
  * The commands a content viewer owns on top of `DocumentScreen`: copying the
13
18
  * whole document and toggling the line-number gutter. Theme, quit, actions and
@@ -16,7 +21,7 @@ interface UseContentCommandsParams {
16
21
  export const useContentCommands = function useContentCommands({
17
22
  content,
18
23
  textContent,
19
- }: UseContentCommandsParams) {
24
+ }: UseContentCommandsParams): ContentCommandsResult {
20
25
  const config = useConfig();
21
26
  const [showLineNumbers, setShowLineNumbers] = useState(config.view?.gutter ?? true);
22
27
 
@@ -0,0 +1,54 @@
1
+ import type { ActionDefinition } from "@tooee/commands";
2
+ import type { StatusBarItem } from "@tooee/layout";
3
+ import type { DecorationLayer } from "@tooee/renderers";
4
+ import { useDocumentController } from "@tooee/shell";
5
+ import type { DocumentController, DocumentRowAdapter } from "@tooee/shell";
6
+ import type { AnyContent } from "../types.js";
7
+ import { useContentCommands } from "./use-content-commands.js";
8
+
9
+ interface ContentDocumentProps {
10
+ actions?: ActionDefinition[];
11
+ content: AnyContent;
12
+ decorations: DecorationLayer[];
13
+ textContent: string;
14
+ }
15
+
16
+ interface ContentDocumentOptions<T> {
17
+ buildStatusItems?: (document: DocumentController<T>) => StatusBarItem[];
18
+ contextMenu?: boolean;
19
+ multiSelect: boolean;
20
+ preserveCursorByKey?: boolean;
21
+ statusItems?: StatusBarItem[];
22
+ }
23
+
24
+ /** Shared document state used by each built-in content subview. */
25
+ export interface ContentDocumentResult<T> {
26
+ document: DocumentController<T>;
27
+ showLineNumbers: boolean;
28
+ statusItems: StatusBarItem[];
29
+ }
30
+
31
+ /** Build the command, controller, and status state shared by content subviews. */
32
+ export const useContentDocument = function useContentDocument<T>(
33
+ rows: readonly T[],
34
+ adapter: DocumentRowAdapter<T>,
35
+ { actions, content, decorations, textContent }: ContentDocumentProps,
36
+ options: ContentDocumentOptions<T>,
37
+ ): ContentDocumentResult<T> {
38
+ const { showLineNumbers } = useContentCommands({ content, textContent });
39
+ const document = useDocumentController<T>({
40
+ adapter,
41
+ contextMenu: options.contextMenu === false ? false : actions,
42
+ decorations,
43
+ multiSelect: options.multiSelect,
44
+ preserveCursorByKey: options.preserveCursorByKey,
45
+ rows,
46
+ });
47
+ const statusItems = options.buildStatusItems?.(document) ??
48
+ options.statusItems ?? [
49
+ { label: "Format:", value: content.format },
50
+ { label: "Lines:", value: String(rows.length) },
51
+ ];
52
+
53
+ return { document, showLineNumbers, statusItems };
54
+ };
@@ -1,6 +1,8 @@
1
1
  import { useCallback, useEffect, useMemo } from "react";
2
2
  import { useSelector } from "@xstate/store-react";
3
- import type { ContentProvider } from "../types.js";
3
+ import type { AnyContent, ContentProvider } from "../types.js";
4
+ import type { MarkSet } from "@tooee/marks";
5
+ import type { ContentLoaderStatus } from "../content-loader-store.js";
4
6
  import {
5
7
  createContentLoaderStore,
6
8
  isAsyncIterable,
@@ -13,7 +15,19 @@ import {
13
15
  selectStreaming,
14
16
  } from "../content-loader-store.js";
15
17
 
16
- export const useContentLoader = function useContentLoader(contentProvider: ContentProvider) {
18
+ /** Reactive state exposed by the content provider loader. */
19
+ export interface ContentLoaderState {
20
+ content: AnyContent | null;
21
+ error: string | null;
22
+ providerMarks: MarkSet[];
23
+ reload: () => void;
24
+ status: ContentLoaderStatus;
25
+ streaming: boolean;
26
+ }
27
+
28
+ export const useContentLoader = function useContentLoader(
29
+ contentProvider: ContentProvider,
30
+ ): ContentLoaderState {
17
31
  const store = useMemo(createContentLoaderStore, []);
18
32
  const loadSeq = useSelector(store, (snapshot) => selectLoadSeq(snapshot.context));
19
33
 
@@ -37,7 +51,7 @@ export const useContentLoader = function useContentLoader(contentProvider: Conte
37
51
  void (async () => {
38
52
  try {
39
53
  while (true) {
40
- // Deferred(lint-sweep): preserve sequential stream consumption for ordered backpressure.
54
+ // preserve sequential stream consumption for ordered backpressure.
41
55
  // oxlint-disable-next-line no-await-in-loop -- each chunk must be consumed in iterator order
42
56
  const result = await iterator.next();
43
57
  if (result.done === true) {
@@ -27,7 +27,7 @@ export interface ViewCommandContext {
27
27
 
28
28
  declare module "@tooee/commands" {
29
29
  interface CommandContext {
30
- view: ViewCommandContext;
30
+ view?: ViewCommandContext;
31
31
  }
32
32
  }
33
33
 
@@ -50,6 +50,18 @@ export interface CreateViewCommandContextOptions {
50
50
 
51
51
  export type ProvideViewCommandContextOptions = CreateViewCommandContextOptions;
52
52
 
53
+ type ViewCommandContextInput =
54
+ | ProvideViewCommandContextOptions
55
+ | (() => ProvideViewCommandContextOptions);
56
+
57
+ /** Resolve the hook's public eager-or-lazy option union at its boundary. */
58
+ const resolveViewCommandContextOptions = function resolveViewCommandContextOptions(
59
+ options: ViewCommandContextInput,
60
+ ): ProvideViewCommandContextOptions {
61
+ // oxlint-disable-next-line anti-slop/no-runtime-typeof -- public hook boundary distinguishes the documented lazy options callback from eager options
62
+ return typeof options === "function" ? options() : options;
63
+ };
64
+
53
65
  export const createViewCommandContext = function createViewCommandContext({
54
66
  content,
55
67
  format,
@@ -84,9 +96,9 @@ export const createViewCommandContext = function createViewCommandContext({
84
96
  };
85
97
 
86
98
  export const useProvideViewCommandContext = function useProvideViewCommandContext(
87
- options: ProvideViewCommandContextOptions | (() => ProvideViewCommandContextOptions),
88
- ) {
99
+ options: ViewCommandContextInput,
100
+ ): void {
89
101
  useProvideCommandContextKey("view", () =>
90
- createViewCommandContext(typeof options === "function" ? options() : options),
102
+ createViewCommandContext(resolveViewCommandContextOptions(options)),
91
103
  );
92
104
  };
package/src/index.ts CHANGED
@@ -20,6 +20,8 @@ export type {
20
20
  ProvideViewCommandContextOptions,
21
21
  ViewCommandContext,
22
22
  } from "./hooks/use-view-command-context.js";
23
+ export type { ContentLoaderState } from "./hooks/use-content-loader.js";
24
+ export type { ContentCommandsResult } from "./hooks/use-content-commands.js";
23
25
  export type {
24
26
  AnyContent,
25
27
  Content,
package/src/launch.tsx CHANGED
@@ -1,9 +1,10 @@
1
- import { launchCli } from "@tooee/shell";
1
+ import { launchCli, runCliSession } from "@tooee/shell";
2
2
  import type { ActionDefinition } from "@tooee/commands";
3
3
  import type { CodeBlockRenderer } from "@tooee/renderers";
4
4
  import { View } from "./view.js";
5
5
  import { DirectoryView } from "./directory-view.js";
6
6
  import type { ContentProvider, ContentRenderer } from "./types.js";
7
+ import type { ReactNode } from "react";
7
8
 
8
9
  export interface ViewLaunchOptions {
9
10
  contentProvider: ContentProvider;
@@ -23,13 +24,15 @@ export interface DirectoryLaunchOptions {
23
24
  }
24
25
 
25
26
  export const launch = async function launch(options: ViewLaunchOptions): Promise<void> {
26
- await launchCli(
27
- <View
28
- contentProvider={options.contentProvider}
29
- actions={options.actions}
30
- renderers={options.renderers}
31
- codeBlockRenderers={options.codeBlockRenderers}
32
- />,
27
+ await runCliSession<undefined>(
28
+ (): ReactNode => (
29
+ <View
30
+ contentProvider={options.contentProvider}
31
+ actions={options.actions}
32
+ renderers={options.renderers}
33
+ codeBlockRenderers={options.codeBlockRenderers}
34
+ />
35
+ ),
33
36
  );
34
37
  };
35
38
 
package/src/types.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  import type { ReactNode } from "react";
2
2
  import type { ImageFit, ImageRenderProtocol, ImageSource } from "@opentui/core";
3
3
  import type { CommandContext } from "@tooee/commands";
4
+ import { formatTableCell } from "@tooee/renderers";
4
5
  import type { ColumnDef, SourceLineRow, TableRow } from "@tooee/renderers";
5
6
  import type { DocumentController } from "@tooee/shell";
6
7
  import type { MarkSet } from "@tooee/marks";
@@ -18,6 +19,7 @@ export type Content =
18
19
  export type ContentFormat = Content["format"];
19
20
 
20
21
  /** Handles an inline Markdown link with the View's live command context. */
22
+ // oxlint-disable-next-line anti-slop/no-unknown-returns -- public callback contract preserves the renderer's exact-true link-consumption result and permits legacy return values
21
23
  export type MarkdownLinkActivateHandler = (href: string, context: CommandContext) => unknown;
22
24
 
23
25
  interface BaseContent {
@@ -152,40 +154,6 @@ export const isCustomContent = function isCustomContent(
152
154
  return !BUILTIN_FORMATS.has(content.format);
153
155
  };
154
156
 
155
- const stringifyCell = function stringifyCell(value: unknown): string {
156
- if (value === null || value === undefined) {
157
- return "";
158
- }
159
- if (typeof value === "string") {
160
- return value;
161
- }
162
- if (typeof value === "number" || typeof value === "boolean") {
163
- return String(value);
164
- }
165
- if (typeof value === "string") {
166
- return value;
167
- }
168
- if (
169
- typeof value === "number" ||
170
- typeof value === "bigint" ||
171
- typeof value === "boolean" ||
172
- typeof value === "symbol"
173
- ) {
174
- return value.toString();
175
- }
176
- if (value === null || value === undefined) {
177
- return String(value);
178
- }
179
- if (typeof value === "function") {
180
- return Function.prototype.toString.call(value);
181
- }
182
- try {
183
- return JSON.stringify(value);
184
- } catch {
185
- return Array.isArray(value) ? value.join(",") : Object.prototype.toString.call(value);
186
- }
187
- };
188
-
189
157
  export const getTextContent = function getTextContent(content: AnyContent): string {
190
158
  if (content.getTextContent) {
191
159
  return content.getTextContent();
@@ -209,6 +177,7 @@ export const getTextContent = function getTextContent(content: AnyContent): stri
209
177
  return content.text;
210
178
  }
211
179
  case "image": {
180
+ // oxlint-disable-next-line anti-slop/no-runtime-typeof -- ImageSource is OpenTUI's public string-or-binary union; only its string and URL variants have text content
212
181
  if (typeof content.src === "string") {
213
182
  return content.src;
214
183
  }
@@ -217,7 +186,7 @@ export const getTextContent = function getTextContent(content: AnyContent): stri
217
186
  case "table": {
218
187
  const headers = content.columns.map((column) => column.header ?? column.key);
219
188
  const rowLines = content.rows.map((row) =>
220
- content.columns.map((column) => stringifyCell(row[column.key])).join("\t"),
189
+ content.columns.map((column) => formatTableCell(row[column.key])).join("\t"),
221
190
  );
222
191
  return [headers.join("\t"), ...rowLines].join("\n");
223
192
  }
package/src/view.tsx CHANGED
@@ -16,6 +16,7 @@ import {
16
16
  DiffSubview,
17
17
  CustomSubview,
18
18
  } from "./components/subviews/index.js";
19
+ import type { ReactNode } from "react";
19
20
 
20
21
  interface ViewProps {
21
22
  contentProvider: ContentProvider;
@@ -36,7 +37,7 @@ export const View = function View({
36
37
  renderers,
37
38
  codeBlockRenderers,
38
39
  onMarkdownLinkActivate,
39
- }: ViewProps): React.ReactNode {
40
+ }: ViewProps): ReactNode {
40
41
  const { theme } = useTheme();
41
42
  const { content, streaming, error, providerMarks, reload } = useContentLoader(contentProvider);
42
43
 
@@ -1,5 +0,0 @@
1
- export { useContentLoader } from "./use-content-loader.js";
2
- export { useContentCommands } from "./use-content-commands.js";
3
- export { createViewCommandContext, useProvideViewCommandContext, } from "./use-view-command-context.js";
4
- export type { CreateViewCommandContextOptions, ProvideViewCommandContextOptions, ViewCommandContext, } from "./use-view-command-context.js";
5
- //# sourceMappingURL=index.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/hooks/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAC3D,OAAO,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAC/D,OAAO,EACL,wBAAwB,EACxB,4BAA4B,GAC7B,MAAM,+BAA+B,CAAC;AACvC,YAAY,EACV,+BAA+B,EAC/B,gCAAgC,EAChC,kBAAkB,GACnB,MAAM,+BAA+B,CAAC"}
@@ -1,4 +0,0 @@
1
- export { useContentLoader } from "./use-content-loader.js";
2
- export { useContentCommands } from "./use-content-commands.js";
3
- export { createViewCommandContext, useProvideViewCommandContext, } from "./use-view-command-context.js";
4
- //# sourceMappingURL=index.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/hooks/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAC3D,OAAO,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAC/D,OAAO,EACL,wBAAwB,EACxB,4BAA4B,GAC7B,MAAM,+BAA+B,CAAC"}
@@ -1,11 +0,0 @@
1
- export { useContentLoader } from "./use-content-loader.js";
2
- export { useContentCommands } from "./use-content-commands.js";
3
- export {
4
- createViewCommandContext,
5
- useProvideViewCommandContext,
6
- } from "./use-view-command-context.js";
7
- export type {
8
- CreateViewCommandContextOptions,
9
- ProvideViewCommandContextOptions,
10
- ViewCommandContext,
11
- } from "./use-view-command-context.js";