@tooee/view 0.7.2 → 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.
- package/dist/components/subviews/code-subview.d.ts +2 -1
- package/dist/components/subviews/code-subview.d.ts.map +1 -1
- package/dist/components/subviews/code-subview.js +2 -15
- package/dist/components/subviews/code-subview.js.map +1 -1
- package/dist/components/subviews/custom-subview.d.ts +2 -1
- package/dist/components/subviews/custom-subview.d.ts.map +1 -1
- package/dist/components/subviews/custom-subview.js +3 -13
- package/dist/components/subviews/custom-subview.js.map +1 -1
- package/dist/components/subviews/diff-subview.d.ts +2 -1
- package/dist/components/subviews/diff-subview.d.ts.map +1 -1
- package/dist/components/subviews/diff-subview.js +23 -33
- package/dist/components/subviews/diff-subview.js.map +1 -1
- package/dist/components/subviews/image-subview.d.ts +2 -1
- package/dist/components/subviews/image-subview.d.ts.map +1 -1
- package/dist/components/subviews/image-subview.js +4 -9
- package/dist/components/subviews/image-subview.js.map +1 -1
- package/dist/components/subviews/markdown-subview.d.ts +2 -1
- package/dist/components/subviews/markdown-subview.d.ts.map +1 -1
- package/dist/components/subviews/markdown-subview.js +8 -15
- package/dist/components/subviews/markdown-subview.js.map +1 -1
- package/dist/components/subviews/table-subview.d.ts +2 -1
- package/dist/components/subviews/table-subview.d.ts.map +1 -1
- package/dist/components/subviews/table-subview.js +9 -48
- package/dist/components/subviews/table-subview.js.map +1 -1
- package/dist/components/view-state.d.ts +2 -1
- package/dist/components/view-state.d.ts.map +1 -1
- package/dist/components/view-state.js.map +1 -1
- package/dist/content-loader-store.d.ts +3 -2
- package/dist/content-loader-store.d.ts.map +1 -1
- package/dist/content-loader-store.js +5 -4
- package/dist/content-loader-store.js.map +1 -1
- package/dist/default-provider.d.ts.map +1 -1
- package/dist/default-provider.js +29 -29
- package/dist/default-provider.js.map +1 -1
- package/dist/directory-view.d.ts +2 -1
- package/dist/directory-view.d.ts.map +1 -1
- package/dist/directory-view.js +2 -1
- package/dist/directory-view.js.map +1 -1
- package/dist/hooks/use-content-commands.d.ts +5 -3
- package/dist/hooks/use-content-commands.d.ts.map +1 -1
- package/dist/hooks/use-content-commands.js +2 -0
- package/dist/hooks/use-content-commands.js.map +1 -1
- package/dist/hooks/use-content-document.d.ts +28 -0
- package/dist/hooks/use-content-document.d.ts.map +1 -0
- package/dist/hooks/use-content-document.js +21 -0
- package/dist/hooks/use-content-document.js.map +1 -0
- package/dist/hooks/use-content-loader.d.ts +10 -6
- package/dist/hooks/use-content-loader.d.ts.map +1 -1
- package/dist/hooks/use-content-loader.js +1 -1
- package/dist/hooks/use-content-loader.js.map +1 -1
- package/dist/hooks/use-view-command-context.d.ts +4 -2
- package/dist/hooks/use-view-command-context.d.ts.map +1 -1
- package/dist/hooks/use-view-command-context.js +6 -1
- package/dist/hooks/use-view-command-context.js.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/launch.d.ts.map +1 -1
- package/dist/launch.js +2 -2
- package/dist/launch.js.map +1 -1
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +3 -33
- package/dist/types.js.map +1 -1
- package/dist/view.d.ts +2 -1
- package/dist/view.d.ts.map +1 -1
- package/dist/view.js.map +1 -1
- package/package.json +12 -11
- package/src/components/subviews/code-subview.tsx +8 -20
- package/src/components/subviews/custom-subview.tsx +9 -19
- package/src/components/subviews/diff-subview.tsx +35 -43
- package/src/components/subviews/image-subview.tsx +12 -11
- package/src/components/subviews/markdown-subview.tsx +16 -22
- package/src/components/subviews/table-subview.tsx +16 -56
- package/src/components/view-state.tsx +2 -1
- package/src/content-loader-store.ts +8 -5
- package/src/default-provider.ts +34 -32
- package/src/directory-view.tsx +4 -2
- package/src/hooks/use-content-commands.ts +8 -1
- package/src/hooks/use-content-document.ts +54 -0
- package/src/hooks/use-content-loader.ts +17 -3
- package/src/hooks/use-view-command-context.ts +16 -4
- package/src/index.ts +2 -0
- package/src/launch.tsx +11 -8
- package/src/types.ts +4 -35
- package/src/view.tsx +2 -1
- package/dist/hooks/index.d.ts +0 -5
- package/dist/hooks/index.d.ts.map +0 -1
- package/dist/hooks/index.js +0 -4
- package/dist/hooks/index.js.map +0 -1
- 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,12 +21,14 @@ 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
|
|
|
23
28
|
useCopyCommand({
|
|
24
29
|
getText: () => (content ? textContent : undefined),
|
|
30
|
+
hotkey: "y g",
|
|
31
|
+
modes: ["cursor", "select"],
|
|
25
32
|
});
|
|
26
33
|
useToggleLineNumbersCommand({
|
|
27
34
|
onToggle: () => {
|
|
@@ -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
|
-
|
|
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
|
-
//
|
|
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
|
|
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:
|
|
88
|
-
) {
|
|
99
|
+
options: ViewCommandContextInput,
|
|
100
|
+
): void {
|
|
89
101
|
useProvideCommandContextKey("view", () =>
|
|
90
|
-
createViewCommandContext(
|
|
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
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
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) =>
|
|
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):
|
|
40
|
+
}: ViewProps): ReactNode {
|
|
40
41
|
const { theme } = useTheme();
|
|
41
42
|
const { content, streaming, error, providerMarks, reload } = useContentLoader(contentProvider);
|
|
42
43
|
|
package/dist/hooks/index.d.ts
DELETED
|
@@ -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"}
|
package/dist/hooks/index.js
DELETED
package/dist/hooks/index.js.map
DELETED
|
@@ -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"}
|
package/src/hooks/index.ts
DELETED
|
@@ -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";
|