@suds-cli/markdown 0.1.0-alpha.0 → 0.1.0-alpha.1

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/index.cjs ADDED
@@ -0,0 +1,200 @@
1
+ 'use strict';
2
+
3
+ var chapstick = require('@suds-cli/chapstick');
4
+ var filesystem = require('@suds-cli/filesystem');
5
+ var viewport = require('@suds-cli/viewport');
6
+ var marked = require('marked');
7
+ var markedTerminal = require('marked-terminal');
8
+ var machine = require('@suds-cli/machine');
9
+
10
+ // src/model.ts
11
+
12
+ // src/messages.ts
13
+ var RenderMarkdownMsg = class {
14
+ constructor(content) {
15
+ this.content = content;
16
+ }
17
+ _tag = "markdown-render";
18
+ };
19
+ var ErrorMsg = class {
20
+ constructor(error) {
21
+ this.error = error;
22
+ }
23
+ _tag = "markdown-error";
24
+ };
25
+ function renderMarkdown(content, options = {}) {
26
+ const width = options.width ?? 80;
27
+ const background = options.background ?? options.env?.getTerminalBackground() ?? "dark";
28
+ const isDark = background !== "light";
29
+ const style = machine.createAlwaysEnabledStyle();
30
+ const marked$1 = new marked.Marked(
31
+ markedTerminal.markedTerminal({
32
+ // Wrap text at specified width
33
+ width,
34
+ reflowText: true,
35
+ // Headings - brighter on dark backgrounds
36
+ firstHeading: isDark ? style.cyan.bold : style.blue.bold,
37
+ heading: isDark ? style.cyan.bold : style.blue.bold,
38
+ // Code blocks
39
+ code: isDark ? style.white : style.gray,
40
+ blockquote: isDark ? style.white : style.gray,
41
+ // Emphasis
42
+ strong: style.bold,
43
+ em: style.italic,
44
+ // Lists
45
+ listitem: style,
46
+ // Links
47
+ link: isDark ? style.blueBright : style.blue,
48
+ // Other elements
49
+ hr: style.gray,
50
+ paragraph: style
51
+ })
52
+ );
53
+ try {
54
+ const rendered = marked$1.parse(content);
55
+ return rendered.trim();
56
+ } catch (error) {
57
+ throw new Error(
58
+ `Failed to render markdown: ${error instanceof Error ? error.message : String(error)}`
59
+ );
60
+ }
61
+ }
62
+
63
+ // src/model.ts
64
+ var MarkdownModel = class _MarkdownModel {
65
+ viewport;
66
+ active;
67
+ fileName;
68
+ filesystem;
69
+ constructor(options) {
70
+ this.viewport = options.viewport;
71
+ this.active = options.active;
72
+ this.fileName = options.fileName;
73
+ this.filesystem = options.filesystem;
74
+ }
75
+ /**
76
+ * Create a new markdown model.
77
+ * @param options - Configuration options
78
+ */
79
+ static new(options) {
80
+ const viewport$1 = viewport.ViewportModel.new({
81
+ width: options.width ?? 0,
82
+ height: options.height ?? 0,
83
+ style: options.style
84
+ });
85
+ return new _MarkdownModel({
86
+ viewport: viewport$1,
87
+ active: options.active ?? true,
88
+ fileName: "",
89
+ filesystem: options.filesystem
90
+ });
91
+ }
92
+ /**
93
+ * Tea init hook (no-op).
94
+ */
95
+ init() {
96
+ return null;
97
+ }
98
+ /**
99
+ * Set the filename to render. Returns a command that will read and render the file.
100
+ * @param fileName - Path to the markdown file
101
+ */
102
+ setFileName(fileName) {
103
+ const updated = this.with({ fileName });
104
+ const cmd = renderMarkdownCmd(this.filesystem, this.viewport.width, fileName);
105
+ return [updated, cmd];
106
+ }
107
+ /**
108
+ * Set the size of the viewport and re-render if a file is set.
109
+ * @param width - New width
110
+ * @param height - New height
111
+ */
112
+ setSize(width, height) {
113
+ const updatedViewport = this.viewport.setWidth(width).setHeight(height);
114
+ const updated = this.with({ viewport: updatedViewport });
115
+ if (this.fileName !== "") {
116
+ const cmd = renderMarkdownCmd(this.filesystem, width, this.fileName);
117
+ return [updated, cmd];
118
+ }
119
+ return [updated, null];
120
+ }
121
+ /**
122
+ * Set whether the component is active and should handle input.
123
+ * @param active - Active state
124
+ */
125
+ setIsActive(active) {
126
+ if (active === this.active) return this;
127
+ return this.with({ active });
128
+ }
129
+ /**
130
+ * Scroll to the top of the viewport.
131
+ */
132
+ gotoTop() {
133
+ const updatedViewport = this.viewport.scrollToTop();
134
+ if (updatedViewport === this.viewport) return this;
135
+ return this.with({ viewport: updatedViewport });
136
+ }
137
+ /**
138
+ * Handle messages. Processes viewport scrolling and markdown rendering.
139
+ * @param msg - The message to handle
140
+ */
141
+ update(msg) {
142
+ if (msg instanceof RenderMarkdownMsg) {
143
+ const styled = new chapstick.Style().width(this.viewport.width).alignHorizontal("left").render(msg.content);
144
+ const updatedViewport = this.viewport.setContent(styled);
145
+ return [this.with({ viewport: updatedViewport }), null];
146
+ }
147
+ if (msg instanceof ErrorMsg) {
148
+ const errorContent = msg.error.message;
149
+ const updatedViewport = this.viewport.setContent(errorContent);
150
+ return [
151
+ this.with({
152
+ fileName: "",
153
+ viewport: updatedViewport
154
+ }),
155
+ null
156
+ ];
157
+ }
158
+ if (this.active) {
159
+ const [updatedViewport, cmd] = this.viewport.update(msg);
160
+ if (updatedViewport !== this.viewport) {
161
+ return [this.with({ viewport: updatedViewport }), cmd];
162
+ }
163
+ }
164
+ return [this, null];
165
+ }
166
+ /**
167
+ * Render the markdown viewport.
168
+ */
169
+ view() {
170
+ return this.viewport.view();
171
+ }
172
+ with(patch) {
173
+ return new _MarkdownModel({
174
+ viewport: patch.viewport ?? this.viewport,
175
+ active: patch.active ?? this.active,
176
+ fileName: patch.fileName ?? this.fileName,
177
+ filesystem: patch.filesystem ?? this.filesystem
178
+ });
179
+ }
180
+ };
181
+ function renderMarkdownCmd(fs, width, fileName) {
182
+ return async () => {
183
+ try {
184
+ const content = await filesystem.readFileContent(fs, fileName);
185
+ const rendered = renderMarkdown(content, { width });
186
+ return new RenderMarkdownMsg(rendered);
187
+ } catch (error) {
188
+ return new ErrorMsg(
189
+ error instanceof Error ? error : new Error(String(error))
190
+ );
191
+ }
192
+ };
193
+ }
194
+
195
+ exports.ErrorMsg = ErrorMsg;
196
+ exports.MarkdownModel = MarkdownModel;
197
+ exports.RenderMarkdownMsg = RenderMarkdownMsg;
198
+ exports.renderMarkdown = renderMarkdown;
199
+ //# sourceMappingURL=index.cjs.map
200
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/messages.ts","../src/renderer.ts","../src/model.ts"],"names":["createAlwaysEnabledStyle","marked","Marked","markedTerminal","viewport","ViewportModel","Style","readFileContent"],"mappings":"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","file":"index.cjs","sourcesContent":["/**\n * Message types for the markdown component.\n */\n\n/**\n * Message containing rendered markdown content.\n * @public\n */\nexport class RenderMarkdownMsg {\n readonly _tag = 'markdown-render'\n\n constructor(public readonly content: string) {}\n}\n\n/**\n * Message containing an error from file reading or rendering.\n * @public\n */\nexport class ErrorMsg {\n readonly _tag = 'markdown-error'\n\n constructor(public readonly error: Error) {}\n}\n","/**\n * Markdown rendering utilities.\n */\n\nimport { Marked } from 'marked'\nimport { markedTerminal } from 'marked-terminal'\nimport type { EnvironmentAdapter, TerminalBackground } from '@suds-cli/machine'\nimport { createAlwaysEnabledStyle } from '@suds-cli/machine'\n\n/**\n * Options for rendering markdown.\n * @public\n */\nexport interface RenderMarkdownOptions {\n /**\n * Width for word wrapping. Defaults to 80.\n */\n width?: number\n /**\n * Terminal background mode. Defaults to 'dark'.\n */\n background?: TerminalBackground\n /**\n * Environment adapter for detecting terminal capabilities.\n */\n env?: EnvironmentAdapter\n}\n\n/**\n * Renders markdown content with terminal styling.\n * Detects terminal background (light/dark) and applies appropriate styling.\n *\n * @param content - The markdown string to render\n * @param options - Rendering options\n * @returns The styled markdown output\n * @public\n */\nexport function renderMarkdown(\n content: string,\n options: RenderMarkdownOptions = {},\n): string {\n const width = options.width ?? 80\n const background = options.background ?? options.env?.getTerminalBackground() ?? 'dark'\n\n // Use appropriate colors for terminal background\n const isDark = background !== 'light'\n\n // Create a style function with full color support for markdown rendering\n const style = createAlwaysEnabledStyle()\n\n // Create marked instance with terminal renderer\n const marked = new Marked(\n markedTerminal({\n // Wrap text at specified width\n width,\n reflowText: true,\n // Headings - brighter on dark backgrounds\n firstHeading: isDark ? style.cyan.bold : style.blue.bold,\n heading: isDark ? style.cyan.bold : style.blue.bold,\n // Code blocks\n code: isDark ? style.white : style.gray,\n blockquote: isDark ? style.white : style.gray,\n // Emphasis\n strong: style.bold,\n em: style.italic,\n // Lists\n listitem: style,\n // Links\n link: isDark ? style.blueBright : style.blue,\n // Other elements\n hr: style.gray,\n paragraph: style,\n }),\n )\n\n try {\n const rendered = marked.parse(content) as string\n return rendered.trim()\n } catch (error) {\n throw new Error(\n `Failed to render markdown: ${error instanceof Error ? error.message : String(error)}`,\n )\n }\n}\n","/**\n * Markdown viewer component.\n */\n\nimport { Style } from '@suds-cli/chapstick'\nimport { readFileContent } from '@suds-cli/filesystem'\nimport { ViewportModel } from '@suds-cli/viewport'\nimport type { Cmd, Msg } from '@suds-cli/tea'\nimport type { FileSystemAdapter } from '@suds-cli/machine'\nimport { RenderMarkdownMsg, ErrorMsg } from './messages.js'\nimport { renderMarkdown } from './renderer.js'\n\n/**\n * Options for creating a markdown model.\n * @public\n */\nexport interface MarkdownOptions {\n /**\n * Filesystem adapter for file operations.\n */\n filesystem: FileSystemAdapter\n /**\n * Whether the component is active and should handle input.\n * Defaults to true.\n */\n active?: boolean\n /**\n * Initial width for the viewport.\n * Defaults to 0.\n */\n width?: number\n /**\n * Initial height for the viewport.\n * Defaults to 0.\n */\n height?: number\n /**\n * Style for the viewport.\n */\n style?: Style\n}\n\n/**\n * Markdown viewer model that renders markdown files with terminal styling\n * in a scrollable viewport.\n * @public\n */\nexport class MarkdownModel {\n readonly viewport: ViewportModel\n readonly active: boolean\n readonly fileName: string\n readonly filesystem: FileSystemAdapter\n\n private constructor(options: {\n viewport: ViewportModel\n active: boolean\n fileName: string\n filesystem: FileSystemAdapter\n }) {\n this.viewport = options.viewport\n this.active = options.active\n this.fileName = options.fileName\n this.filesystem = options.filesystem\n }\n\n /**\n * Create a new markdown model.\n * @param options - Configuration options\n */\n static new(options: MarkdownOptions): MarkdownModel {\n const viewport = ViewportModel.new({\n width: options.width ?? 0,\n height: options.height ?? 0,\n style: options.style,\n })\n\n return new MarkdownModel({\n viewport,\n active: options.active ?? true,\n fileName: '',\n filesystem: options.filesystem,\n })\n }\n\n /**\n * Tea init hook (no-op).\n */\n init(): Cmd<Msg> {\n return null\n }\n\n /**\n * Set the filename to render. Returns a command that will read and render the file.\n * @param fileName - Path to the markdown file\n */\n setFileName(fileName: string): [MarkdownModel, Cmd<Msg>] {\n const updated = this.with({ fileName })\n const cmd = renderMarkdownCmd(this.filesystem, this.viewport.width, fileName)\n return [updated, cmd]\n }\n\n /**\n * Set the size of the viewport and re-render if a file is set.\n * @param width - New width\n * @param height - New height\n */\n setSize(width: number, height: number): [MarkdownModel, Cmd<Msg>] {\n const updatedViewport = this.viewport.setWidth(width).setHeight(height)\n const updated = this.with({ viewport: updatedViewport })\n\n if (this.fileName !== '') {\n const cmd = renderMarkdownCmd(this.filesystem, width, this.fileName)\n return [updated, cmd]\n }\n\n return [updated, null]\n }\n\n /**\n * Set whether the component is active and should handle input.\n * @param active - Active state\n */\n setIsActive(active: boolean): MarkdownModel {\n if (active === this.active) return this\n return this.with({ active })\n }\n\n /**\n * Scroll to the top of the viewport.\n */\n gotoTop(): MarkdownModel {\n const updatedViewport = this.viewport.scrollToTop()\n if (updatedViewport === this.viewport) return this\n return this.with({ viewport: updatedViewport })\n }\n\n /**\n * Handle messages. Processes viewport scrolling and markdown rendering.\n * @param msg - The message to handle\n */\n update(msg: Msg): [MarkdownModel, Cmd<Msg>] {\n // Handle markdown rendering\n if (msg instanceof RenderMarkdownMsg) {\n // Apply width for word wrapping and left-align to pad lines to consistent width\n // Viewport handles height/scrolling\n const styled = new Style()\n .width(this.viewport.width)\n .alignHorizontal('left')\n .render(msg.content)\n\n const updatedViewport = this.viewport.setContent(styled)\n return [this.with({ viewport: updatedViewport }), null]\n }\n\n // Handle errors\n if (msg instanceof ErrorMsg) {\n const errorContent = msg.error.message\n const updatedViewport = this.viewport.setContent(errorContent)\n return [\n this.with({\n fileName: '',\n viewport: updatedViewport,\n }),\n null,\n ]\n }\n\n // Handle viewport updates if active\n if (this.active) {\n const [updatedViewport, cmd] = this.viewport.update(msg)\n if (updatedViewport !== this.viewport) {\n return [this.with({ viewport: updatedViewport }), cmd]\n }\n }\n\n return [this, null]\n }\n\n /**\n * Render the markdown viewport.\n */\n view(): string {\n return this.viewport.view()\n }\n\n private with(patch: Partial<MarkdownModel>): MarkdownModel {\n return new MarkdownModel({\n viewport: patch.viewport ?? this.viewport,\n active: patch.active ?? this.active,\n fileName: patch.fileName ?? this.fileName,\n filesystem: patch.filesystem ?? this.filesystem,\n })\n }\n}\n\n/**\n * Command to read and render a markdown file.\n */\nfunction renderMarkdownCmd(\n fs: FileSystemAdapter,\n width: number,\n fileName: string,\n): Cmd<Msg> {\n return async () => {\n try {\n const content = await readFileContent(fs, fileName)\n const rendered = renderMarkdown(content, { width })\n return new RenderMarkdownMsg(rendered)\n } catch (error) {\n return new ErrorMsg(\n error instanceof Error ? error : new Error(String(error)),\n )\n }\n }\n}\n"]}
@@ -0,0 +1,146 @@
1
+ import { Style } from '@suds-cli/chapstick';
2
+ import { ViewportModel } from '@suds-cli/viewport';
3
+ import { Cmd, Msg } from '@suds-cli/tea';
4
+ import { FileSystemAdapter, TerminalBackground, EnvironmentAdapter } from '@suds-cli/machine';
5
+
6
+ /**
7
+ * Markdown viewer component.
8
+ */
9
+
10
+ /**
11
+ * Options for creating a markdown model.
12
+ * @public
13
+ */
14
+ interface MarkdownOptions {
15
+ /**
16
+ * Filesystem adapter for file operations.
17
+ */
18
+ filesystem: FileSystemAdapter;
19
+ /**
20
+ * Whether the component is active and should handle input.
21
+ * Defaults to true.
22
+ */
23
+ active?: boolean;
24
+ /**
25
+ * Initial width for the viewport.
26
+ * Defaults to 0.
27
+ */
28
+ width?: number;
29
+ /**
30
+ * Initial height for the viewport.
31
+ * Defaults to 0.
32
+ */
33
+ height?: number;
34
+ /**
35
+ * Style for the viewport.
36
+ */
37
+ style?: Style;
38
+ }
39
+ /**
40
+ * Markdown viewer model that renders markdown files with terminal styling
41
+ * in a scrollable viewport.
42
+ * @public
43
+ */
44
+ declare class MarkdownModel {
45
+ readonly viewport: ViewportModel;
46
+ readonly active: boolean;
47
+ readonly fileName: string;
48
+ readonly filesystem: FileSystemAdapter;
49
+ private constructor();
50
+ /**
51
+ * Create a new markdown model.
52
+ * @param options - Configuration options
53
+ */
54
+ static new(options: MarkdownOptions): MarkdownModel;
55
+ /**
56
+ * Tea init hook (no-op).
57
+ */
58
+ init(): Cmd<Msg>;
59
+ /**
60
+ * Set the filename to render. Returns a command that will read and render the file.
61
+ * @param fileName - Path to the markdown file
62
+ */
63
+ setFileName(fileName: string): [MarkdownModel, Cmd<Msg>];
64
+ /**
65
+ * Set the size of the viewport and re-render if a file is set.
66
+ * @param width - New width
67
+ * @param height - New height
68
+ */
69
+ setSize(width: number, height: number): [MarkdownModel, Cmd<Msg>];
70
+ /**
71
+ * Set whether the component is active and should handle input.
72
+ * @param active - Active state
73
+ */
74
+ setIsActive(active: boolean): MarkdownModel;
75
+ /**
76
+ * Scroll to the top of the viewport.
77
+ */
78
+ gotoTop(): MarkdownModel;
79
+ /**
80
+ * Handle messages. Processes viewport scrolling and markdown rendering.
81
+ * @param msg - The message to handle
82
+ */
83
+ update(msg: Msg): [MarkdownModel, Cmd<Msg>];
84
+ /**
85
+ * Render the markdown viewport.
86
+ */
87
+ view(): string;
88
+ private with;
89
+ }
90
+
91
+ /**
92
+ * Message types for the markdown component.
93
+ */
94
+ /**
95
+ * Message containing rendered markdown content.
96
+ * @public
97
+ */
98
+ declare class RenderMarkdownMsg {
99
+ readonly content: string;
100
+ readonly _tag = "markdown-render";
101
+ constructor(content: string);
102
+ }
103
+ /**
104
+ * Message containing an error from file reading or rendering.
105
+ * @public
106
+ */
107
+ declare class ErrorMsg {
108
+ readonly error: Error;
109
+ readonly _tag = "markdown-error";
110
+ constructor(error: Error);
111
+ }
112
+
113
+ /**
114
+ * Markdown rendering utilities.
115
+ */
116
+
117
+ /**
118
+ * Options for rendering markdown.
119
+ * @public
120
+ */
121
+ interface RenderMarkdownOptions {
122
+ /**
123
+ * Width for word wrapping. Defaults to 80.
124
+ */
125
+ width?: number;
126
+ /**
127
+ * Terminal background mode. Defaults to 'dark'.
128
+ */
129
+ background?: TerminalBackground;
130
+ /**
131
+ * Environment adapter for detecting terminal capabilities.
132
+ */
133
+ env?: EnvironmentAdapter;
134
+ }
135
+ /**
136
+ * Renders markdown content with terminal styling.
137
+ * Detects terminal background (light/dark) and applies appropriate styling.
138
+ *
139
+ * @param content - The markdown string to render
140
+ * @param options - Rendering options
141
+ * @returns The styled markdown output
142
+ * @public
143
+ */
144
+ declare function renderMarkdown(content: string, options?: RenderMarkdownOptions): string;
145
+
146
+ export { ErrorMsg, MarkdownModel, type MarkdownOptions, RenderMarkdownMsg, type RenderMarkdownOptions, renderMarkdown };
package/dist/index.d.ts CHANGED
@@ -1,9 +1,146 @@
1
+ import { Style } from '@suds-cli/chapstick';
2
+ import { ViewportModel } from '@suds-cli/viewport';
3
+ import { Cmd, Msg } from '@suds-cli/tea';
4
+ import { FileSystemAdapter, TerminalBackground, EnvironmentAdapter } from '@suds-cli/machine';
5
+
1
6
  /**
2
- * Markdown viewer component for Suds terminal UIs.
7
+ * Markdown viewer component.
8
+ */
9
+
10
+ /**
11
+ * Options for creating a markdown model.
12
+ * @public
13
+ */
14
+ interface MarkdownOptions {
15
+ /**
16
+ * Filesystem adapter for file operations.
17
+ */
18
+ filesystem: FileSystemAdapter;
19
+ /**
20
+ * Whether the component is active and should handle input.
21
+ * Defaults to true.
22
+ */
23
+ active?: boolean;
24
+ /**
25
+ * Initial width for the viewport.
26
+ * Defaults to 0.
27
+ */
28
+ width?: number;
29
+ /**
30
+ * Initial height for the viewport.
31
+ * Defaults to 0.
32
+ */
33
+ height?: number;
34
+ /**
35
+ * Style for the viewport.
36
+ */
37
+ style?: Style;
38
+ }
39
+ /**
40
+ * Markdown viewer model that renders markdown files with terminal styling
41
+ * in a scrollable viewport.
42
+ * @public
43
+ */
44
+ declare class MarkdownModel {
45
+ readonly viewport: ViewportModel;
46
+ readonly active: boolean;
47
+ readonly fileName: string;
48
+ readonly filesystem: FileSystemAdapter;
49
+ private constructor();
50
+ /**
51
+ * Create a new markdown model.
52
+ * @param options - Configuration options
53
+ */
54
+ static new(options: MarkdownOptions): MarkdownModel;
55
+ /**
56
+ * Tea init hook (no-op).
57
+ */
58
+ init(): Cmd<Msg>;
59
+ /**
60
+ * Set the filename to render. Returns a command that will read and render the file.
61
+ * @param fileName - Path to the markdown file
62
+ */
63
+ setFileName(fileName: string): [MarkdownModel, Cmd<Msg>];
64
+ /**
65
+ * Set the size of the viewport and re-render if a file is set.
66
+ * @param width - New width
67
+ * @param height - New height
68
+ */
69
+ setSize(width: number, height: number): [MarkdownModel, Cmd<Msg>];
70
+ /**
71
+ * Set whether the component is active and should handle input.
72
+ * @param active - Active state
73
+ */
74
+ setIsActive(active: boolean): MarkdownModel;
75
+ /**
76
+ * Scroll to the top of the viewport.
77
+ */
78
+ gotoTop(): MarkdownModel;
79
+ /**
80
+ * Handle messages. Processes viewport scrolling and markdown rendering.
81
+ * @param msg - The message to handle
82
+ */
83
+ update(msg: Msg): [MarkdownModel, Cmd<Msg>];
84
+ /**
85
+ * Render the markdown viewport.
86
+ */
87
+ view(): string;
88
+ private with;
89
+ }
90
+
91
+ /**
92
+ * Message types for the markdown component.
93
+ */
94
+ /**
95
+ * Message containing rendered markdown content.
96
+ * @public
97
+ */
98
+ declare class RenderMarkdownMsg {
99
+ readonly content: string;
100
+ readonly _tag = "markdown-render";
101
+ constructor(content: string);
102
+ }
103
+ /**
104
+ * Message containing an error from file reading or rendering.
105
+ * @public
106
+ */
107
+ declare class ErrorMsg {
108
+ readonly error: Error;
109
+ readonly _tag = "markdown-error";
110
+ constructor(error: Error);
111
+ }
112
+
113
+ /**
114
+ * Markdown rendering utilities.
115
+ */
116
+
117
+ /**
118
+ * Options for rendering markdown.
119
+ * @public
120
+ */
121
+ interface RenderMarkdownOptions {
122
+ /**
123
+ * Width for word wrapping. Defaults to 80.
124
+ */
125
+ width?: number;
126
+ /**
127
+ * Terminal background mode. Defaults to 'dark'.
128
+ */
129
+ background?: TerminalBackground;
130
+ /**
131
+ * Environment adapter for detecting terminal capabilities.
132
+ */
133
+ env?: EnvironmentAdapter;
134
+ }
135
+ /**
136
+ * Renders markdown content with terminal styling.
137
+ * Detects terminal background (light/dark) and applies appropriate styling.
3
138
  *
4
- * @packageDocumentation
139
+ * @param content - The markdown string to render
140
+ * @param options - Rendering options
141
+ * @returns The styled markdown output
142
+ * @public
5
143
  */
6
- export { MarkdownModel, type MarkdownOptions } from "./model.js";
7
- export { RenderMarkdownMsg, ErrorMsg } from "./messages.js";
8
- export { renderMarkdown, type RenderMarkdownOptions } from "./renderer.js";
9
- //# sourceMappingURL=index.d.ts.map
144
+ declare function renderMarkdown(content: string, options?: RenderMarkdownOptions): string;
145
+
146
+ export { ErrorMsg, MarkdownModel, type MarkdownOptions, RenderMarkdownMsg, type RenderMarkdownOptions, renderMarkdown };
package/dist/index.js CHANGED
@@ -1,9 +1,195 @@
1
- /**
2
- * Markdown viewer component for Suds terminal UIs.
3
- *
4
- * @packageDocumentation
5
- */
6
- export { MarkdownModel } from "./model.js";
7
- export { RenderMarkdownMsg, ErrorMsg } from "./messages.js";
8
- export { renderMarkdown } from "./renderer.js";
1
+ import { Style } from '@suds-cli/chapstick';
2
+ import { readFileContent } from '@suds-cli/filesystem';
3
+ import { ViewportModel } from '@suds-cli/viewport';
4
+ import { Marked } from 'marked';
5
+ import { markedTerminal } from 'marked-terminal';
6
+ import { createAlwaysEnabledStyle } from '@suds-cli/machine';
7
+
8
+ // src/model.ts
9
+
10
+ // src/messages.ts
11
+ var RenderMarkdownMsg = class {
12
+ constructor(content) {
13
+ this.content = content;
14
+ }
15
+ _tag = "markdown-render";
16
+ };
17
+ var ErrorMsg = class {
18
+ constructor(error) {
19
+ this.error = error;
20
+ }
21
+ _tag = "markdown-error";
22
+ };
23
+ function renderMarkdown(content, options = {}) {
24
+ const width = options.width ?? 80;
25
+ const background = options.background ?? options.env?.getTerminalBackground() ?? "dark";
26
+ const isDark = background !== "light";
27
+ const style = createAlwaysEnabledStyle();
28
+ const marked = new Marked(
29
+ markedTerminal({
30
+ // Wrap text at specified width
31
+ width,
32
+ reflowText: true,
33
+ // Headings - brighter on dark backgrounds
34
+ firstHeading: isDark ? style.cyan.bold : style.blue.bold,
35
+ heading: isDark ? style.cyan.bold : style.blue.bold,
36
+ // Code blocks
37
+ code: isDark ? style.white : style.gray,
38
+ blockquote: isDark ? style.white : style.gray,
39
+ // Emphasis
40
+ strong: style.bold,
41
+ em: style.italic,
42
+ // Lists
43
+ listitem: style,
44
+ // Links
45
+ link: isDark ? style.blueBright : style.blue,
46
+ // Other elements
47
+ hr: style.gray,
48
+ paragraph: style
49
+ })
50
+ );
51
+ try {
52
+ const rendered = marked.parse(content);
53
+ return rendered.trim();
54
+ } catch (error) {
55
+ throw new Error(
56
+ `Failed to render markdown: ${error instanceof Error ? error.message : String(error)}`
57
+ );
58
+ }
59
+ }
60
+
61
+ // src/model.ts
62
+ var MarkdownModel = class _MarkdownModel {
63
+ viewport;
64
+ active;
65
+ fileName;
66
+ filesystem;
67
+ constructor(options) {
68
+ this.viewport = options.viewport;
69
+ this.active = options.active;
70
+ this.fileName = options.fileName;
71
+ this.filesystem = options.filesystem;
72
+ }
73
+ /**
74
+ * Create a new markdown model.
75
+ * @param options - Configuration options
76
+ */
77
+ static new(options) {
78
+ const viewport = ViewportModel.new({
79
+ width: options.width ?? 0,
80
+ height: options.height ?? 0,
81
+ style: options.style
82
+ });
83
+ return new _MarkdownModel({
84
+ viewport,
85
+ active: options.active ?? true,
86
+ fileName: "",
87
+ filesystem: options.filesystem
88
+ });
89
+ }
90
+ /**
91
+ * Tea init hook (no-op).
92
+ */
93
+ init() {
94
+ return null;
95
+ }
96
+ /**
97
+ * Set the filename to render. Returns a command that will read and render the file.
98
+ * @param fileName - Path to the markdown file
99
+ */
100
+ setFileName(fileName) {
101
+ const updated = this.with({ fileName });
102
+ const cmd = renderMarkdownCmd(this.filesystem, this.viewport.width, fileName);
103
+ return [updated, cmd];
104
+ }
105
+ /**
106
+ * Set the size of the viewport and re-render if a file is set.
107
+ * @param width - New width
108
+ * @param height - New height
109
+ */
110
+ setSize(width, height) {
111
+ const updatedViewport = this.viewport.setWidth(width).setHeight(height);
112
+ const updated = this.with({ viewport: updatedViewport });
113
+ if (this.fileName !== "") {
114
+ const cmd = renderMarkdownCmd(this.filesystem, width, this.fileName);
115
+ return [updated, cmd];
116
+ }
117
+ return [updated, null];
118
+ }
119
+ /**
120
+ * Set whether the component is active and should handle input.
121
+ * @param active - Active state
122
+ */
123
+ setIsActive(active) {
124
+ if (active === this.active) return this;
125
+ return this.with({ active });
126
+ }
127
+ /**
128
+ * Scroll to the top of the viewport.
129
+ */
130
+ gotoTop() {
131
+ const updatedViewport = this.viewport.scrollToTop();
132
+ if (updatedViewport === this.viewport) return this;
133
+ return this.with({ viewport: updatedViewport });
134
+ }
135
+ /**
136
+ * Handle messages. Processes viewport scrolling and markdown rendering.
137
+ * @param msg - The message to handle
138
+ */
139
+ update(msg) {
140
+ if (msg instanceof RenderMarkdownMsg) {
141
+ const styled = new Style().width(this.viewport.width).alignHorizontal("left").render(msg.content);
142
+ const updatedViewport = this.viewport.setContent(styled);
143
+ return [this.with({ viewport: updatedViewport }), null];
144
+ }
145
+ if (msg instanceof ErrorMsg) {
146
+ const errorContent = msg.error.message;
147
+ const updatedViewport = this.viewport.setContent(errorContent);
148
+ return [
149
+ this.with({
150
+ fileName: "",
151
+ viewport: updatedViewport
152
+ }),
153
+ null
154
+ ];
155
+ }
156
+ if (this.active) {
157
+ const [updatedViewport, cmd] = this.viewport.update(msg);
158
+ if (updatedViewport !== this.viewport) {
159
+ return [this.with({ viewport: updatedViewport }), cmd];
160
+ }
161
+ }
162
+ return [this, null];
163
+ }
164
+ /**
165
+ * Render the markdown viewport.
166
+ */
167
+ view() {
168
+ return this.viewport.view();
169
+ }
170
+ with(patch) {
171
+ return new _MarkdownModel({
172
+ viewport: patch.viewport ?? this.viewport,
173
+ active: patch.active ?? this.active,
174
+ fileName: patch.fileName ?? this.fileName,
175
+ filesystem: patch.filesystem ?? this.filesystem
176
+ });
177
+ }
178
+ };
179
+ function renderMarkdownCmd(fs, width, fileName) {
180
+ return async () => {
181
+ try {
182
+ const content = await readFileContent(fs, fileName);
183
+ const rendered = renderMarkdown(content, { width });
184
+ return new RenderMarkdownMsg(rendered);
185
+ } catch (error) {
186
+ return new ErrorMsg(
187
+ error instanceof Error ? error : new Error(String(error))
188
+ );
189
+ }
190
+ };
191
+ }
192
+
193
+ export { ErrorMsg, MarkdownModel, RenderMarkdownMsg, renderMarkdown };
194
+ //# sourceMappingURL=index.js.map
9
195
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,aAAa,EAAwB,MAAM,YAAY,CAAC;AACjE,OAAO,EAAE,iBAAiB,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAC5D,OAAO,EAAE,cAAc,EAA8B,MAAM,eAAe,CAAC"}
1
+ {"version":3,"sources":["../src/messages.ts","../src/renderer.ts","../src/model.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["/**\n * Message types for the markdown component.\n */\n\n/**\n * Message containing rendered markdown content.\n * @public\n */\nexport class RenderMarkdownMsg {\n readonly _tag = 'markdown-render'\n\n constructor(public readonly content: string) {}\n}\n\n/**\n * Message containing an error from file reading or rendering.\n * @public\n */\nexport class ErrorMsg {\n readonly _tag = 'markdown-error'\n\n constructor(public readonly error: Error) {}\n}\n","/**\n * Markdown rendering utilities.\n */\n\nimport { Marked } from 'marked'\nimport { markedTerminal } from 'marked-terminal'\nimport type { EnvironmentAdapter, TerminalBackground } from '@suds-cli/machine'\nimport { createAlwaysEnabledStyle } from '@suds-cli/machine'\n\n/**\n * Options for rendering markdown.\n * @public\n */\nexport interface RenderMarkdownOptions {\n /**\n * Width for word wrapping. Defaults to 80.\n */\n width?: number\n /**\n * Terminal background mode. Defaults to 'dark'.\n */\n background?: TerminalBackground\n /**\n * Environment adapter for detecting terminal capabilities.\n */\n env?: EnvironmentAdapter\n}\n\n/**\n * Renders markdown content with terminal styling.\n * Detects terminal background (light/dark) and applies appropriate styling.\n *\n * @param content - The markdown string to render\n * @param options - Rendering options\n * @returns The styled markdown output\n * @public\n */\nexport function renderMarkdown(\n content: string,\n options: RenderMarkdownOptions = {},\n): string {\n const width = options.width ?? 80\n const background = options.background ?? options.env?.getTerminalBackground() ?? 'dark'\n\n // Use appropriate colors for terminal background\n const isDark = background !== 'light'\n\n // Create a style function with full color support for markdown rendering\n const style = createAlwaysEnabledStyle()\n\n // Create marked instance with terminal renderer\n const marked = new Marked(\n markedTerminal({\n // Wrap text at specified width\n width,\n reflowText: true,\n // Headings - brighter on dark backgrounds\n firstHeading: isDark ? style.cyan.bold : style.blue.bold,\n heading: isDark ? style.cyan.bold : style.blue.bold,\n // Code blocks\n code: isDark ? style.white : style.gray,\n blockquote: isDark ? style.white : style.gray,\n // Emphasis\n strong: style.bold,\n em: style.italic,\n // Lists\n listitem: style,\n // Links\n link: isDark ? style.blueBright : style.blue,\n // Other elements\n hr: style.gray,\n paragraph: style,\n }),\n )\n\n try {\n const rendered = marked.parse(content) as string\n return rendered.trim()\n } catch (error) {\n throw new Error(\n `Failed to render markdown: ${error instanceof Error ? error.message : String(error)}`,\n )\n }\n}\n","/**\n * Markdown viewer component.\n */\n\nimport { Style } from '@suds-cli/chapstick'\nimport { readFileContent } from '@suds-cli/filesystem'\nimport { ViewportModel } from '@suds-cli/viewport'\nimport type { Cmd, Msg } from '@suds-cli/tea'\nimport type { FileSystemAdapter } from '@suds-cli/machine'\nimport { RenderMarkdownMsg, ErrorMsg } from './messages.js'\nimport { renderMarkdown } from './renderer.js'\n\n/**\n * Options for creating a markdown model.\n * @public\n */\nexport interface MarkdownOptions {\n /**\n * Filesystem adapter for file operations.\n */\n filesystem: FileSystemAdapter\n /**\n * Whether the component is active and should handle input.\n * Defaults to true.\n */\n active?: boolean\n /**\n * Initial width for the viewport.\n * Defaults to 0.\n */\n width?: number\n /**\n * Initial height for the viewport.\n * Defaults to 0.\n */\n height?: number\n /**\n * Style for the viewport.\n */\n style?: Style\n}\n\n/**\n * Markdown viewer model that renders markdown files with terminal styling\n * in a scrollable viewport.\n * @public\n */\nexport class MarkdownModel {\n readonly viewport: ViewportModel\n readonly active: boolean\n readonly fileName: string\n readonly filesystem: FileSystemAdapter\n\n private constructor(options: {\n viewport: ViewportModel\n active: boolean\n fileName: string\n filesystem: FileSystemAdapter\n }) {\n this.viewport = options.viewport\n this.active = options.active\n this.fileName = options.fileName\n this.filesystem = options.filesystem\n }\n\n /**\n * Create a new markdown model.\n * @param options - Configuration options\n */\n static new(options: MarkdownOptions): MarkdownModel {\n const viewport = ViewportModel.new({\n width: options.width ?? 0,\n height: options.height ?? 0,\n style: options.style,\n })\n\n return new MarkdownModel({\n viewport,\n active: options.active ?? true,\n fileName: '',\n filesystem: options.filesystem,\n })\n }\n\n /**\n * Tea init hook (no-op).\n */\n init(): Cmd<Msg> {\n return null\n }\n\n /**\n * Set the filename to render. Returns a command that will read and render the file.\n * @param fileName - Path to the markdown file\n */\n setFileName(fileName: string): [MarkdownModel, Cmd<Msg>] {\n const updated = this.with({ fileName })\n const cmd = renderMarkdownCmd(this.filesystem, this.viewport.width, fileName)\n return [updated, cmd]\n }\n\n /**\n * Set the size of the viewport and re-render if a file is set.\n * @param width - New width\n * @param height - New height\n */\n setSize(width: number, height: number): [MarkdownModel, Cmd<Msg>] {\n const updatedViewport = this.viewport.setWidth(width).setHeight(height)\n const updated = this.with({ viewport: updatedViewport })\n\n if (this.fileName !== '') {\n const cmd = renderMarkdownCmd(this.filesystem, width, this.fileName)\n return [updated, cmd]\n }\n\n return [updated, null]\n }\n\n /**\n * Set whether the component is active and should handle input.\n * @param active - Active state\n */\n setIsActive(active: boolean): MarkdownModel {\n if (active === this.active) return this\n return this.with({ active })\n }\n\n /**\n * Scroll to the top of the viewport.\n */\n gotoTop(): MarkdownModel {\n const updatedViewport = this.viewport.scrollToTop()\n if (updatedViewport === this.viewport) return this\n return this.with({ viewport: updatedViewport })\n }\n\n /**\n * Handle messages. Processes viewport scrolling and markdown rendering.\n * @param msg - The message to handle\n */\n update(msg: Msg): [MarkdownModel, Cmd<Msg>] {\n // Handle markdown rendering\n if (msg instanceof RenderMarkdownMsg) {\n // Apply width for word wrapping and left-align to pad lines to consistent width\n // Viewport handles height/scrolling\n const styled = new Style()\n .width(this.viewport.width)\n .alignHorizontal('left')\n .render(msg.content)\n\n const updatedViewport = this.viewport.setContent(styled)\n return [this.with({ viewport: updatedViewport }), null]\n }\n\n // Handle errors\n if (msg instanceof ErrorMsg) {\n const errorContent = msg.error.message\n const updatedViewport = this.viewport.setContent(errorContent)\n return [\n this.with({\n fileName: '',\n viewport: updatedViewport,\n }),\n null,\n ]\n }\n\n // Handle viewport updates if active\n if (this.active) {\n const [updatedViewport, cmd] = this.viewport.update(msg)\n if (updatedViewport !== this.viewport) {\n return [this.with({ viewport: updatedViewport }), cmd]\n }\n }\n\n return [this, null]\n }\n\n /**\n * Render the markdown viewport.\n */\n view(): string {\n return this.viewport.view()\n }\n\n private with(patch: Partial<MarkdownModel>): MarkdownModel {\n return new MarkdownModel({\n viewport: patch.viewport ?? this.viewport,\n active: patch.active ?? this.active,\n fileName: patch.fileName ?? this.fileName,\n filesystem: patch.filesystem ?? this.filesystem,\n })\n }\n}\n\n/**\n * Command to read and render a markdown file.\n */\nfunction renderMarkdownCmd(\n fs: FileSystemAdapter,\n width: number,\n fileName: string,\n): Cmd<Msg> {\n return async () => {\n try {\n const content = await readFileContent(fs, fileName)\n const rendered = renderMarkdown(content, { width })\n return new RenderMarkdownMsg(rendered)\n } catch (error) {\n return new ErrorMsg(\n error instanceof Error ? error : new Error(String(error)),\n )\n }\n }\n}\n"]}
package/package.json CHANGED
@@ -1,36 +1,49 @@
1
1
  {
2
2
  "name": "@suds-cli/markdown",
3
- "version": "0.1.0-alpha.0",
4
3
  "description": "Markdown viewer component for Suds terminal UIs",
5
- "type": "module",
6
- "main": "./dist/index.js",
7
- "types": "./dist/index.d.ts",
8
- "files": [
9
- "dist"
10
- ],
4
+ "version": "0.1.0-alpha.1",
11
5
  "dependencies": {
12
- "chalk": "^5.4.1",
13
6
  "marked": "^15.0.0",
14
7
  "marked-terminal": "^7.3.0",
15
- "@suds-cli/chapstick": "0.1.0-alpha.0",
16
- "@suds-cli/filesystem": "0.0.0",
17
- "@suds-cli/tea": "0.0.0",
18
- "@suds-cli/viewport": "0.0.1-alpha.0"
8
+ "@suds-cli/chapstick": "0.1.0-alpha.1",
9
+ "@suds-cli/filesystem": "0.1.0-alpha.0",
10
+ "@suds-cli/machine": "0.1.0-alpha.0",
11
+ "@suds-cli/tea": "0.1.0-alpha.0",
12
+ "@suds-cli/viewport": "0.1.0-alpha.1"
19
13
  },
20
14
  "devDependencies": {
21
15
  "typescript": "5.8.2",
22
- "vitest": "^4.0.15"
16
+ "vitest": "^4.0.16"
23
17
  },
24
18
  "engines": {
25
19
  "node": ">=20.0.0"
26
20
  },
21
+ "exports": {
22
+ ".": {
23
+ "import": {
24
+ "types": "./dist/index.d.ts",
25
+ "default": "./dist/index.js"
26
+ },
27
+ "require": {
28
+ "types": "./dist/index.d.cts",
29
+ "default": "./dist/index.cjs"
30
+ }
31
+ },
32
+ "./package.json": "./package.json"
33
+ },
34
+ "files": [
35
+ "dist"
36
+ ],
37
+ "main": "./dist/index.cjs",
38
+ "module": "./dist/index.js",
39
+ "type": "module",
40
+ "types": "./dist/index.d.ts",
27
41
  "scripts": {
28
- "clean": "rm -rf dist",
29
- "build": "pnpm run clean && tsc -p ./tsconfig.json",
30
- "test": "vitest run",
31
- "generate:api-report": "api-extractor run --local",
42
+ "build": "tsup",
32
43
  "check:api-report": "pnpm run generate:api-report",
33
44
  "check:eslint": "pnpm run lint",
34
- "lint": "eslint \"{src,test}/**/*.{ts,tsx}\""
45
+ "generate:api-report": "api-extractor run --local",
46
+ "lint": "eslint \"{src,test}/**/*.{ts,tsx}\"",
47
+ "test": "vitest run"
35
48
  }
36
49
  }
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,aAAa,EAAE,KAAK,eAAe,EAAE,MAAM,YAAY,CAAC;AACjE,OAAO,EAAE,iBAAiB,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAC5D,OAAO,EAAE,cAAc,EAAE,KAAK,qBAAqB,EAAE,MAAM,eAAe,CAAC"}
@@ -1,22 +0,0 @@
1
- /**
2
- * Message types for the markdown component.
3
- */
4
- /**
5
- * Message containing rendered markdown content.
6
- * @public
7
- */
8
- export declare class RenderMarkdownMsg {
9
- readonly content: string;
10
- readonly _tag = "markdown-render";
11
- constructor(content: string);
12
- }
13
- /**
14
- * Message containing an error from file reading or rendering.
15
- * @public
16
- */
17
- export declare class ErrorMsg {
18
- readonly error: Error;
19
- readonly _tag = "markdown-error";
20
- constructor(error: Error);
21
- }
22
- //# sourceMappingURL=messages.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"messages.d.ts","sourceRoot":"","sources":["../src/messages.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH;;;GAGG;AACH,qBAAa,iBAAiB;aAGA,OAAO,EAAE,MAAM;IAF3C,QAAQ,CAAC,IAAI,qBAAqB;gBAEN,OAAO,EAAE,MAAM;CAC5C;AAED;;;GAGG;AACH,qBAAa,QAAQ;aAGS,KAAK,EAAE,KAAK;IAFxC,QAAQ,CAAC,IAAI,oBAAoB;gBAEL,KAAK,EAAE,KAAK;CACzC"}
package/dist/messages.js DELETED
@@ -1,26 +0,0 @@
1
- /**
2
- * Message types for the markdown component.
3
- */
4
- /**
5
- * Message containing rendered markdown content.
6
- * @public
7
- */
8
- export class RenderMarkdownMsg {
9
- content;
10
- _tag = "markdown-render";
11
- constructor(content) {
12
- this.content = content;
13
- }
14
- }
15
- /**
16
- * Message containing an error from file reading or rendering.
17
- * @public
18
- */
19
- export class ErrorMsg {
20
- error;
21
- _tag = "markdown-error";
22
- constructor(error) {
23
- this.error = error;
24
- }
25
- }
26
- //# sourceMappingURL=messages.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"messages.js","sourceRoot":"","sources":["../src/messages.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH;;;GAGG;AACH,MAAM,OAAO,iBAAiB;IAGA;IAFnB,IAAI,GAAG,iBAAiB,CAAC;IAElC,YAA4B,OAAe;QAAf,YAAO,GAAP,OAAO,CAAQ;IAAG,CAAC;CAChD;AAED;;;GAGG;AACH,MAAM,OAAO,QAAQ;IAGS;IAFnB,IAAI,GAAG,gBAAgB,CAAC;IAEjC,YAA4B,KAAY;QAAZ,UAAK,GAAL,KAAK,CAAO;IAAG,CAAC;CAC7C"}
package/dist/model.d.ts DELETED
@@ -1,82 +0,0 @@
1
- /**
2
- * Markdown viewer component.
3
- */
4
- import { Style } from "@suds-cli/chapstick";
5
- import { ViewportModel } from "@suds-cli/viewport";
6
- import type { Cmd, Msg } from "@suds-cli/tea";
7
- /**
8
- * Options for creating a markdown model.
9
- * @public
10
- */
11
- export interface MarkdownOptions {
12
- /**
13
- * Whether the component is active and should handle input.
14
- * Defaults to true.
15
- */
16
- active?: boolean;
17
- /**
18
- * Initial width for the viewport.
19
- * Defaults to 0.
20
- */
21
- width?: number;
22
- /**
23
- * Initial height for the viewport.
24
- * Defaults to 0.
25
- */
26
- height?: number;
27
- /**
28
- * Style for the viewport.
29
- */
30
- style?: Style;
31
- }
32
- /**
33
- * Markdown viewer model that renders markdown files with terminal styling
34
- * in a scrollable viewport.
35
- * @public
36
- */
37
- export declare class MarkdownModel {
38
- readonly viewport: ViewportModel;
39
- readonly active: boolean;
40
- readonly fileName: string;
41
- private constructor();
42
- /**
43
- * Create a new markdown model.
44
- * @param options - Configuration options
45
- */
46
- static new(options?: MarkdownOptions): MarkdownModel;
47
- /**
48
- * Tea init hook (no-op).
49
- */
50
- init(): Cmd<Msg>;
51
- /**
52
- * Set the filename to render. Returns a command that will read and render the file.
53
- * @param fileName - Path to the markdown file
54
- */
55
- setFileName(fileName: string): [MarkdownModel, Cmd<Msg>];
56
- /**
57
- * Set the size of the viewport and re-render if a file is set.
58
- * @param width - New width
59
- * @param height - New height
60
- */
61
- setSize(width: number, height: number): [MarkdownModel, Cmd<Msg>];
62
- /**
63
- * Set whether the component is active and should handle input.
64
- * @param active - Active state
65
- */
66
- setIsActive(active: boolean): MarkdownModel;
67
- /**
68
- * Scroll to the top of the viewport.
69
- */
70
- gotoTop(): MarkdownModel;
71
- /**
72
- * Handle messages. Processes viewport scrolling and markdown rendering.
73
- * @param msg - The message to handle
74
- */
75
- update(msg: Msg): [MarkdownModel, Cmd<Msg>];
76
- /**
77
- * Render the markdown viewport.
78
- */
79
- view(): string;
80
- private with;
81
- }
82
- //# sourceMappingURL=model.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"model.d.ts","sourceRoot":"","sources":["../src/model.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,KAAK,EAAE,MAAM,qBAAqB,CAAC;AAE5C,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACnD,OAAO,KAAK,EAAE,GAAG,EAAE,GAAG,EAAE,MAAM,eAAe,CAAC;AAI9C;;;GAGG;AACH,MAAM,WAAW,eAAe;IAC9B;;;OAGG;IACH,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB;;;OAGG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IACf;;;OAGG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;;OAEG;IACH,KAAK,CAAC,EAAE,KAAK,CAAC;CACf;AAED;;;;GAIG;AACH,qBAAa,aAAa;IACxB,QAAQ,CAAC,QAAQ,EAAE,aAAa,CAAC;IACjC,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC;IACzB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAE1B,OAAO;IAUP;;;OAGG;IACH,MAAM,CAAC,GAAG,CAAC,OAAO,GAAE,eAAoB,GAAG,aAAa;IAcxD;;OAEG;IACH,IAAI,IAAI,GAAG,CAAC,GAAG,CAAC;IAIhB;;;OAGG;IACH,WAAW,CAAC,QAAQ,EAAE,MAAM,GAAG,CAAC,aAAa,EAAE,GAAG,CAAC,GAAG,CAAC,CAAC;IAMxD;;;;OAIG;IACH,OAAO,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,CAAC,aAAa,EAAE,GAAG,CAAC,GAAG,CAAC,CAAC;IAYjE;;;OAGG;IACH,WAAW,CAAC,MAAM,EAAE,OAAO,GAAG,aAAa;IAK3C;;OAEG;IACH,OAAO,IAAI,aAAa;IAMxB;;;OAGG;IACH,MAAM,CAAC,GAAG,EAAE,GAAG,GAAG,CAAC,aAAa,EAAE,GAAG,CAAC,GAAG,CAAC,CAAC;IAsC3C;;OAEG;IACH,IAAI,IAAI,MAAM;IAId,OAAO,CAAC,IAAI;CAOb"}
package/dist/model.js DELETED
@@ -1,152 +0,0 @@
1
- /**
2
- * Markdown viewer component.
3
- */
4
- import { Style } from "@suds-cli/chapstick";
5
- import { readFileContent } from "@suds-cli/filesystem";
6
- import { ViewportModel } from "@suds-cli/viewport";
7
- import { RenderMarkdownMsg, ErrorMsg } from "./messages.js";
8
- import { renderMarkdown } from "./renderer.js";
9
- /**
10
- * Markdown viewer model that renders markdown files with terminal styling
11
- * in a scrollable viewport.
12
- * @public
13
- */
14
- export class MarkdownModel {
15
- viewport;
16
- active;
17
- fileName;
18
- constructor(options) {
19
- this.viewport = options.viewport;
20
- this.active = options.active;
21
- this.fileName = options.fileName;
22
- }
23
- /**
24
- * Create a new markdown model.
25
- * @param options - Configuration options
26
- */
27
- static new(options = {}) {
28
- const viewport = ViewportModel.new({
29
- width: options.width ?? 0,
30
- height: options.height ?? 0,
31
- style: options.style,
32
- });
33
- return new MarkdownModel({
34
- viewport,
35
- active: options.active ?? true,
36
- fileName: "",
37
- });
38
- }
39
- /**
40
- * Tea init hook (no-op).
41
- */
42
- init() {
43
- return null;
44
- }
45
- /**
46
- * Set the filename to render. Returns a command that will read and render the file.
47
- * @param fileName - Path to the markdown file
48
- */
49
- setFileName(fileName) {
50
- const updated = this.with({ fileName });
51
- const cmd = renderMarkdownCmd(this.viewport.width, fileName);
52
- return [updated, cmd];
53
- }
54
- /**
55
- * Set the size of the viewport and re-render if a file is set.
56
- * @param width - New width
57
- * @param height - New height
58
- */
59
- setSize(width, height) {
60
- const updatedViewport = this.viewport.setWidth(width).setHeight(height);
61
- const updated = this.with({ viewport: updatedViewport });
62
- if (this.fileName !== "") {
63
- const cmd = renderMarkdownCmd(width, this.fileName);
64
- return [updated, cmd];
65
- }
66
- return [updated, null];
67
- }
68
- /**
69
- * Set whether the component is active and should handle input.
70
- * @param active - Active state
71
- */
72
- setIsActive(active) {
73
- if (active === this.active)
74
- return this;
75
- return this.with({ active });
76
- }
77
- /**
78
- * Scroll to the top of the viewport.
79
- */
80
- gotoTop() {
81
- const updatedViewport = this.viewport.scrollToTop();
82
- if (updatedViewport === this.viewport)
83
- return this;
84
- return this.with({ viewport: updatedViewport });
85
- }
86
- /**
87
- * Handle messages. Processes viewport scrolling and markdown rendering.
88
- * @param msg - The message to handle
89
- */
90
- update(msg) {
91
- // Handle markdown rendering
92
- if (msg instanceof RenderMarkdownMsg) {
93
- // Apply width for word wrapping and left-align to pad lines to consistent width
94
- // Viewport handles height/scrolling
95
- const styled = new Style()
96
- .width(this.viewport.width)
97
- .alignHorizontal('left')
98
- .render(msg.content);
99
- const updatedViewport = this.viewport.setContent(styled);
100
- return [this.with({ viewport: updatedViewport }), null];
101
- }
102
- // Handle errors
103
- if (msg instanceof ErrorMsg) {
104
- const errorContent = msg.error.message;
105
- const updatedViewport = this.viewport.setContent(errorContent);
106
- return [
107
- this.with({
108
- fileName: "",
109
- viewport: updatedViewport,
110
- }),
111
- null,
112
- ];
113
- }
114
- // Handle viewport updates if active
115
- if (this.active) {
116
- const [updatedViewport, cmd] = this.viewport.update(msg);
117
- if (updatedViewport !== this.viewport) {
118
- return [this.with({ viewport: updatedViewport }), cmd];
119
- }
120
- }
121
- return [this, null];
122
- }
123
- /**
124
- * Render the markdown viewport.
125
- */
126
- view() {
127
- return this.viewport.view();
128
- }
129
- with(patch) {
130
- return new MarkdownModel({
131
- viewport: patch.viewport ?? this.viewport,
132
- active: patch.active ?? this.active,
133
- fileName: patch.fileName ?? this.fileName,
134
- });
135
- }
136
- }
137
- /**
138
- * Command to read and render a markdown file.
139
- */
140
- function renderMarkdownCmd(width, fileName) {
141
- return async () => {
142
- try {
143
- const content = await readFileContent(fileName);
144
- const rendered = renderMarkdown(content, { width });
145
- return new RenderMarkdownMsg(rendered);
146
- }
147
- catch (error) {
148
- return new ErrorMsg(error instanceof Error ? error : new Error(String(error)));
149
- }
150
- };
151
- }
152
- //# sourceMappingURL=model.js.map
package/dist/model.js.map DELETED
@@ -1 +0,0 @@
1
- {"version":3,"file":"model.js","sourceRoot":"","sources":["../src/model.ts"],"names":[],"mappings":"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"}
@@ -1,24 +0,0 @@
1
- /**
2
- * Markdown rendering utilities.
3
- */
4
- /**
5
- * Options for rendering markdown.
6
- * @public
7
- */
8
- export interface RenderMarkdownOptions {
9
- /**
10
- * Width for word wrapping. Defaults to 80.
11
- */
12
- width?: number;
13
- }
14
- /**
15
- * Renders markdown content with terminal styling.
16
- * Detects terminal background (light/dark) and applies appropriate styling.
17
- *
18
- * @param content - The markdown string to render
19
- * @param options - Rendering options
20
- * @returns The styled markdown output
21
- * @public
22
- */
23
- export declare function renderMarkdown(content: string, options?: RenderMarkdownOptions): string;
24
- //# sourceMappingURL=renderer.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"renderer.d.ts","sourceRoot":"","sources":["../src/renderer.ts"],"names":[],"mappings":"AAAA;;GAEG;AAOH;;;GAGG;AACH,MAAM,WAAW,qBAAqB;IACpC;;OAEG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;;;;;;;GAQG;AACH,wBAAgB,cAAc,CAC5B,OAAO,EAAE,MAAM,EACf,OAAO,GAAE,qBAA0B,GAClC,MAAM,CAsCR"}
package/dist/renderer.js DELETED
@@ -1,52 +0,0 @@
1
- /**
2
- * Markdown rendering utilities.
3
- */
4
- import { Marked } from "marked";
5
- import { markedTerminal } from "marked-terminal";
6
- import { getTerminalBackground } from "@suds-cli/chapstick";
7
- import chalk from "chalk";
8
- /**
9
- * Renders markdown content with terminal styling.
10
- * Detects terminal background (light/dark) and applies appropriate styling.
11
- *
12
- * @param content - The markdown string to render
13
- * @param options - Rendering options
14
- * @returns The styled markdown output
15
- * @public
16
- */
17
- export function renderMarkdown(content, options = {}) {
18
- const width = options.width ?? 80;
19
- const background = getTerminalBackground();
20
- // Use appropriate colors for terminal background
21
- const isDark = background !== 'light';
22
- // Create marked instance with terminal renderer
23
- const marked = new Marked(markedTerminal({
24
- // Wrap text at specified width
25
- width,
26
- reflowText: true,
27
- // Headings - brighter on dark backgrounds
28
- firstHeading: isDark ? chalk.cyan.bold : chalk.blue.bold,
29
- heading: isDark ? chalk.cyan.bold : chalk.blue.bold,
30
- // Code blocks
31
- code: isDark ? chalk.white : chalk.gray,
32
- blockquote: isDark ? chalk.white : chalk.gray,
33
- // Emphasis
34
- strong: chalk.bold,
35
- em: chalk.italic,
36
- // Lists
37
- listitem: chalk.reset,
38
- // Links
39
- link: isDark ? chalk.blueBright : chalk.blue,
40
- // Other elements
41
- hr: chalk.gray,
42
- paragraph: chalk.reset,
43
- }));
44
- try {
45
- const rendered = marked.parse(content);
46
- return rendered.trim();
47
- }
48
- catch (error) {
49
- throw new Error(`Failed to render markdown: ${error instanceof Error ? error.message : String(error)}`);
50
- }
51
- }
52
- //# sourceMappingURL=renderer.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"renderer.js","sourceRoot":"","sources":["../src/renderer.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAChC,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AACjD,OAAO,EAAE,qBAAqB,EAAE,MAAM,qBAAqB,CAAC;AAC5D,OAAO,KAAK,MAAM,OAAO,CAAC;AAa1B;;;;;;;;GAQG;AACH,MAAM,UAAU,cAAc,CAC5B,OAAe,EACf,UAAiC,EAAE;IAEnC,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,IAAI,EAAE,CAAC;IAClC,MAAM,UAAU,GAAG,qBAAqB,EAAE,CAAC;IAE3C,iDAAiD;IACjD,MAAM,MAAM,GAAG,UAAU,KAAK,OAAO,CAAC;IAEtC,gDAAgD;IAChD,MAAM,MAAM,GAAG,IAAI,MAAM,CACvB,cAAc,CAAC;QACb,+BAA+B;QAC/B,KAAK;QACL,UAAU,EAAE,IAAI;QAChB,0CAA0C;QAC1C,YAAY,EAAE,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI;QACxD,OAAO,EAAE,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI;QACnD,cAAc;QACd,IAAI,EAAE,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI;QACvC,UAAU,EAAE,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI;QAC7C,WAAW;QACX,MAAM,EAAE,KAAK,CAAC,IAAI;QAClB,EAAE,EAAE,KAAK,CAAC,MAAM;QAChB,QAAQ;QACR,QAAQ,EAAE,KAAK,CAAC,KAAK;QACrB,QAAQ;QACR,IAAI,EAAE,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI;QAC5C,iBAAiB;QACjB,EAAE,EAAE,KAAK,CAAC,IAAI;QACd,SAAS,EAAE,KAAK,CAAC,KAAK;KACvB,CAAC,CACH,CAAC;IAEF,IAAI,CAAC;QACH,MAAM,QAAQ,GAAG,MAAM,CAAC,KAAK,CAAC,OAAO,CAAW,CAAC;QACjD,OAAO,QAAQ,CAAC,IAAI,EAAE,CAAC;IACzB,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,IAAI,KAAK,CAAC,8BAA8B,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;IAC1G,CAAC;AACH,CAAC"}