@zikojs/notebook 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.
@@ -0,0 +1,46 @@
1
+ import van from "vanjs-core";
2
+
3
+ import {
4
+ FileText,
5
+ Plus,
6
+ Trash2,
7
+ PlayForward,
8
+ Eraser,
9
+ Download,
10
+ Loader2
11
+ } from '../icons/index.js';
12
+
13
+ const { button } = van.tags;
14
+
15
+ import { tags } from 'ziko/dom'
16
+
17
+ const { div, span } = tags
18
+
19
+ export const Button = ({ class: className = "", title = "", disabled = false, onclick }, ...children) =>
20
+ button({ class: `btn ${className}`, title, disabled, onclick }, ...children);
21
+
22
+ export function Header({ store, actions }) {
23
+ const statusBadge = span({ class: "status-badge" }, Loader2(), span('Loading ...'));
24
+ const runAllBtn = Button({ class: "btn-primary", onclick: actions.runAll }, PlayForward(), "Run All");
25
+ const clearBtn = Button({ onclick: actions.clearOutputs }, Eraser(), "Clear Outputs");
26
+ const addCodeBtn = Button({ onclick: actions.addCode }, Plus(), "Add Code");
27
+ const addMdBtn = Button({ onclick: actions.addMarkdown }, FileText(), "Add Markdown");
28
+ const deleteActiveBtn = Button({ onclick: actions.deleteActive }, Trash2(), "Delete Active");
29
+ const exportBtn = Button({ onclick: actions.exportData }, Download(), "Export Data");
30
+
31
+ store.on('change:executionQueueState', (q) => {
32
+ statusBadge.style({display :q.isRunning ? "inline-flex" : "none" })
33
+ statusBadge.element.lastChild.textContent = `Executing Queue (${q.currentIndex + 1}/${q.total})`;
34
+ [runAllBtn, clearBtn, deleteActiveBtn].forEach(b => { b.disabled = q.isRunning; });
35
+ const maxReached = store.get('cells').length >= store.get('maxCells');
36
+ addCodeBtn.disabled = q.isRunning || maxReached;
37
+ addMdBtn.disabled = q.isRunning || maxReached;
38
+ });
39
+
40
+ return div({ class: "header" },
41
+ div({ class: "brand" }, "</>", statusBadge),
42
+ div({ class: "toolbar" },
43
+ runAllBtn, clearBtn, addCodeBtn, addMdBtn, deleteActiveBtn, exportBtn
44
+ )
45
+ ).element;
46
+ }
@@ -0,0 +1,90 @@
1
+ /* --- MARKDOWN --- */
2
+
3
+ .ziko-notebook .markdown-rendered-cell {
4
+ grid-column: 1 / 3;
5
+ padding: 8px 12px;
6
+ cursor: pointer;
7
+ min-height: 38px;
8
+ border: 1px dashed transparent;
9
+ border-radius: 4px;
10
+ transition: background 0.15s ease, border-color 0.15s ease;
11
+ }
12
+
13
+ .ziko-notebook .markdown-rendered-cell:hover {
14
+ background: #f8fafc;
15
+ border-color: #cbd5e1;
16
+ }
17
+
18
+ .ziko-notebook .cell.active .markdown-rendered-cell {
19
+ border-color: #94a3b8;
20
+ }
21
+
22
+ .ziko-notebook .markdown-body {
23
+ line-height: 1.6;
24
+ }
25
+
26
+ .ziko-notebook .markdown-body h1,
27
+ .ziko-notebook .markdown-body h2,
28
+ .ziko-notebook .markdown-body h3 {
29
+ margin-top: 8px;
30
+ margin-bottom: 8px;
31
+ font-weight: 600;
32
+ }
33
+
34
+ .ziko-notebook .markdown-body h1 {
35
+ font-size: 1.5em;
36
+ border-bottom: 1px solid var(--border-color);
37
+ padding-bottom: 4px;
38
+ }
39
+
40
+ .ziko-notebook .markdown-body h2 {
41
+ font-size: 1.25em;
42
+ }
43
+
44
+ .ziko-notebook .markdown-body h3 {
45
+ font-size: 1.1em;
46
+ }
47
+
48
+ .ziko-notebook .markdown-body p {
49
+ margin-bottom: 8px;
50
+ }
51
+
52
+ .ziko-notebook .markdown-body p:last-child {
53
+ margin-bottom: 0;
54
+ }
55
+
56
+ .ziko-notebook .markdown-body ul,
57
+ .ziko-notebook .markdown-body ol {
58
+ padding-left: 20px;
59
+ margin-bottom: 8px;
60
+ }
61
+
62
+ .ziko-notebook .markdown-body code {
63
+ background: #f1f5f9;
64
+ padding: 2px 4px;
65
+ border-radius: 4px;
66
+ font-family: ui-monospace, SFMono-Regular, monospace;
67
+ font-size: 0.9em;
68
+ }
69
+
70
+ .ziko-notebook .markdown-body pre {
71
+ background: #1e293b;
72
+ color: #f8fafc;
73
+ padding: 10px;
74
+ border-radius: 6px;
75
+ overflow-x: auto;
76
+ margin: 8px 0;
77
+ }
78
+
79
+ .ziko-notebook .markdown-body pre code {
80
+ background: transparent;
81
+ color: inherit;
82
+ padding: 0;
83
+ }
84
+
85
+ .ziko-notebook .markdown-body blockquote {
86
+ border-left: 4px solid var(--primary);
87
+ padding-left: 10px;
88
+ color: #64748b;
89
+ margin: 8px 0;
90
+ }
@@ -0,0 +1,28 @@
1
+ import { remark } from "remark";
2
+ import remarkRehype from "remark-rehype";
3
+ import rehypeStringify from "rehype-stringify";
4
+
5
+
6
+ import { tags } from 'ziko/dom'
7
+
8
+ const { div } = tags
9
+
10
+ export function MarkdownView({ cellData, actions }) {
11
+ const content = div();
12
+
13
+ // Process Markdown asynchronously using Remark + Rehype
14
+ remark()
15
+ .use(remarkRehype, { allowDangerousHtml: true })
16
+ .use(rehypeStringify)
17
+ .process(cellData.code || "*Empty Markdown Cell*")
18
+ .then((file) => {
19
+ content.element.innerHTML = String(file);
20
+ })
21
+ .catch((err) => {
22
+ content.element.innerHTML = `<div class="error-output">Markdown rendering error: ${err.message}</div>`;
23
+ });
24
+
25
+ const wrap = div({ class: "markdown-rendered-cell markdown-body" }, content);
26
+ wrap.onClick(() => actions.editMarkdown())
27
+ return wrap.element;
28
+ }
@@ -0,0 +1,4 @@
1
+ @import url('./ErrorOutput/index.css');
2
+ @import url('./Button/index.css');
3
+ @import url('./Header/index.css');
4
+ @import url('./MarkdownView//index.css');
@@ -0,0 +1,4 @@
1
+ export * from './MarkdownView/index.js'
2
+ export * from './CellItem/index.js'
3
+ export * from './Header/index.js'
4
+ export * from './ErrorOutput/index.js'
package/src/index.css CHANGED
@@ -1,3 +1,5 @@
1
+ @import url('./components/index.css');
2
+
1
3
  .ziko-notebook {
2
4
  --primary: #0284c7;
3
5
  --primary-hover: #0369a1;
@@ -10,53 +12,6 @@
10
12
  color: #1e293b;
11
13
  }
12
14
 
13
- /* --- HEADER & TOOLBAR --- */
14
-
15
- .ziko-notebook .header {
16
- border-bottom: 1px solid var(--border-color);
17
- background: #ffffff;
18
- padding: 10px 16px;
19
- display: flex;
20
- align-items: center;
21
- justify-content: space-between;
22
- gap: 12px;
23
- flex-wrap: wrap;
24
- position: sticky;
25
- top: 0;
26
- z-index: 100;
27
- box-shadow: 0 1px 2px rgba(0, 0, 0, 0.03);
28
- width: 100%;
29
- }
30
-
31
- .ziko-notebook .brand {
32
- font-weight: 600;
33
- font-size: 16px;
34
- color: #0f172a;
35
- display: flex;
36
- align-items: center;
37
- gap: 12px;
38
- flex-shrink: 0;
39
- }
40
-
41
- .ziko-notebook .toolbar {
42
- display: flex;
43
- gap: 6px;
44
- align-items: center;
45
- flex-wrap: wrap;
46
- max-width: 100%;
47
- }
48
-
49
- .ziko-notebook .status-badge {
50
- font-size: 12px;
51
- font-weight: 500;
52
- color: var(--primary);
53
- background: #e0f2fe;
54
- padding: 4px 8px;
55
- border-radius: 4px;
56
- display: inline-flex;
57
- align-items: center;
58
- gap: 6px;
59
- }
60
15
 
61
16
  /* --- NOTEBOOK & CELLS --- */
62
17
 
@@ -133,12 +88,6 @@
133
88
  border-color: #cbd5e1;
134
89
  }
135
90
 
136
- /*
137
- * CodeMirror is an external component.
138
- *
139
- * Don't globally style .CodeMirror.
140
- * Scope only the CodeMirror instances owned by the notebook.
141
- */
142
91
  .ziko-notebook .input-wrapper .CodeMirror {
143
92
  font-family: "Fira Code", SFMono-Regular, Consolas, monospace;
144
93
  font-size: 13px;
@@ -162,7 +111,11 @@
162
111
  gap: 4px;
163
112
  flex-shrink: 0;
164
113
  flex-wrap: wrap;
165
- max-width: 120px;
114
+ max-width: 60px;
115
+ }
116
+
117
+ .ziko-notebook .cm-line{
118
+ line-height: 1.3 !important;
166
119
  }
167
120
 
168
121
  /* --- OUTPUT --- */
@@ -177,163 +130,7 @@
177
130
  word-break: break-word;
178
131
  }
179
132
 
180
- .ziko-notebook .error-output {
181
- color: #e11d48;
182
- font-family: ui-monospace, SFMono-Regular, monospace;
183
- white-space: pre-wrap;
184
- font-size: 12px;
185
- background: #fff1f2;
186
- padding: 8px;
187
- border-radius: 4px;
188
- border: 1px solid #fecdd3;
189
- }
190
-
191
- /* --- MARKDOWN --- */
192
-
193
- .ziko-notebook .markdown-rendered-cell {
194
- grid-column: 1 / 3;
195
- padding: 8px 12px;
196
- cursor: pointer;
197
- min-height: 38px;
198
- border: 1px dashed transparent;
199
- border-radius: 4px;
200
- transition: background 0.15s ease, border-color 0.15s ease;
201
- }
202
-
203
- .ziko-notebook .markdown-rendered-cell:hover {
204
- background: #f8fafc;
205
- border-color: #cbd5e1;
206
- }
207
-
208
- .ziko-notebook .cell.active .markdown-rendered-cell {
209
- border-color: #94a3b8;
210
- }
211
-
212
- .ziko-notebook .markdown-body {
213
- line-height: 1.6;
214
- }
215
-
216
- .ziko-notebook .markdown-body h1,
217
- .ziko-notebook .markdown-body h2,
218
- .ziko-notebook .markdown-body h3 {
219
- margin-top: 8px;
220
- margin-bottom: 8px;
221
- font-weight: 600;
222
- }
223
-
224
- .ziko-notebook .markdown-body h1 {
225
- font-size: 1.5em;
226
- border-bottom: 1px solid var(--border-color);
227
- padding-bottom: 4px;
228
- }
229
-
230
- .ziko-notebook .markdown-body h2 {
231
- font-size: 1.25em;
232
- }
233
-
234
- .ziko-notebook .markdown-body h3 {
235
- font-size: 1.1em;
236
- }
237
-
238
- .ziko-notebook .markdown-body p {
239
- margin-bottom: 8px;
240
- }
241
-
242
- .ziko-notebook .markdown-body p:last-child {
243
- margin-bottom: 0;
244
- }
245
-
246
- .ziko-notebook .markdown-body ul,
247
- .ziko-notebook .markdown-body ol {
248
- padding-left: 20px;
249
- margin-bottom: 8px;
250
- }
251
-
252
- .ziko-notebook .markdown-body code {
253
- background: #f1f5f9;
254
- padding: 2px 4px;
255
- border-radius: 4px;
256
- font-family: ui-monospace, SFMono-Regular, monospace;
257
- font-size: 0.9em;
258
- }
259
-
260
- .ziko-notebook .markdown-body pre {
261
- background: #1e293b;
262
- color: #f8fafc;
263
- padding: 10px;
264
- border-radius: 6px;
265
- overflow-x: auto;
266
- margin: 8px 0;
267
- }
268
-
269
- .ziko-notebook .markdown-body pre code {
270
- background: transparent;
271
- color: inherit;
272
- padding: 0;
273
- }
274
-
275
- .ziko-notebook .markdown-body blockquote {
276
- border-left: 4px solid var(--primary);
277
- padding-left: 10px;
278
- color: #64748b;
279
- margin: 8px 0;
280
- }
281
-
282
- /* --- BUTTONS --- */
283
133
 
284
- .ziko-notebook .btn {
285
- display: inline-flex;
286
- align-items: center;
287
- justify-content: center;
288
- gap: 6px;
289
- background: #ffffff;
290
- border: 1px solid var(--border-color);
291
- padding: 6px 10px;
292
- border-radius: 4px;
293
- font-size: 12px;
294
- font-weight: 500;
295
- color: #334155;
296
- cursor: pointer;
297
- line-height: 1;
298
- white-space: nowrap;
299
- transition: background 0.12s ease, border-color 0.12s ease;
300
- }
301
-
302
- .ziko-notebook .btn:hover {
303
- background: #f1f5f9;
304
- }
305
-
306
- .ziko-notebook .btn:disabled {
307
- opacity: 0.5;
308
- cursor: not-allowed;
309
- }
310
-
311
- .ziko-notebook .btn-icon {
312
- padding: 6px;
313
- border-radius: 4px;
314
- }
315
-
316
- .ziko-notebook .btn-primary {
317
- background: var(--primary);
318
- color: #ffffff;
319
- border-color: var(--primary);
320
- }
321
-
322
- .ziko-notebook .btn-primary:hover {
323
- background: var(--primary-hover);
324
- border-color: var(--primary-hover);
325
- }
326
-
327
- .ziko-notebook .btn-active {
328
- background: #e2e8f0;
329
- border-color: #cbd5e1;
330
- }
331
-
332
- .ziko-notebook .icon {
333
- width: 14px;
334
- height: 14px;
335
- stroke-width: 2;
336
- }
337
134
 
338
135
  /* --- MOBILE --- */
339
136
 
package/src/notebook.js CHANGED
@@ -1,8 +1,20 @@
1
1
  import { NotebookStore } from "./store.js";
2
- import { Header, CellItem, MarkdownView } from "./components.js";
3
- import { evaluateCodeAsync } from "./transform.js";
2
+ import {
3
+ Header,
4
+ CellItem,
5
+ MarkdownView,
6
+ ErrorOutput
7
+ } from "./components/index.js";
8
+ import { evaluateCodeAsync } from "./compiler/index.js";
4
9
  import { tags, UIElement } from "ziko/dom";
5
10
  import { call_with_optional_props } from 'ziko/dom/internal-utils'
11
+ import { useState, useDerived } from "ziko/hooks";
12
+ const { div } = tags
13
+ // const [st, setSt] = useState('hh')
14
+ // globalThis.mm = useDerived((n)=>n, [st])
15
+ // globalThis.pp = tags.p({}, mm).mount(document.body)
16
+ // globalThis.st = st
17
+ // globalThis.setSt = setSt
6
18
 
7
19
  export class UINotebook extends UIElement{
8
20
  constructor({
@@ -13,7 +25,8 @@ export class UINotebook extends UIElement{
13
25
  codeMirrorPlugins = [],
14
26
  minLines = 3,
15
27
  maxCells = Infinity,
16
- showHeader = false
28
+ showHeader = true,
29
+ runtimePlugins = []
17
30
  } = {}) {
18
31
  super({ element : 'div'})
19
32
  this.setAttr({class : 'ziko-notebook'})
@@ -25,6 +38,7 @@ export class UINotebook extends UIElement{
25
38
  this.resetScope();
26
39
 
27
40
  this.importMap = importMap;
41
+ this.runtimePlugins = runtimePlugins;
28
42
  this.codeMirrorConfig = codeMirrorConfig;
29
43
  this.codeMirrorPlugins = codeMirrorPlugins;
30
44
  this.minLines = minLines;
@@ -61,7 +75,7 @@ export class UINotebook extends UIElement{
61
75
  addCodeBelow: () => this.addCell("code", "", this.store.get('activeCellId'))
62
76
  };
63
77
 
64
- this.cellsContainer = tags.div({ class: "cells-container" }).element;
78
+ this.cellsContainer = div({ class: "cells-container" }).element;
65
79
  this.header = Header({
66
80
  store: this.store,
67
81
  actions: {
@@ -78,7 +92,7 @@ export class UINotebook extends UIElement{
78
92
 
79
93
  this.append(
80
94
  this.header,
81
- tags.div({ class: "notebook" },
95
+ div({ class: "notebook" },
82
96
  this.cellsContainer)
83
97
  )
84
98
 
@@ -122,10 +136,18 @@ export class UINotebook extends UIElement{
122
136
 
123
137
  try {
124
138
  const scope = app.getScope();
125
- await evaluateCodeAsync(code, cellData._outputRef, app.importMap, scope);
139
+ await evaluateCodeAsync(
140
+ code,
141
+ cellData._outputRef,
142
+ {
143
+ importMap: app.importMap,
144
+ plugins: app.runtimePlugins,
145
+ },
146
+ scope,
147
+ );
126
148
  } catch (err) {
127
149
  hasError = true;
128
- tags.div({ class: "error-output" }, err.toString()).mount(cellData._outputRef)
150
+ ErrorOutput(err).mount(cellData._outputRef)
129
151
  }
130
152
 
131
153
  const counter = this.store.get('executionCounter');
@@ -133,25 +155,23 @@ export class UINotebook extends UIElement{
133
155
  cellData.execCount = counter;
134
156
  cellData.hasDomOutput = cellData._outputRef.childNodes.length > 0 || hasError;
135
157
 
136
- const container = tags.div({ class: "output-area" }).element;
158
+ const container = div({ class: "output-area" }).element;
137
159
  while (cellData._outputRef.firstChild) container.appendChild(cellData._outputRef.firstChild);
138
160
  cellData.outputNode = container;
139
161
  cellData._outputRef.appendChild(container);
140
162
 
141
163
  if (autoNext && !hasError) app.nextCell(cellData.id, "code");
142
- else app._refreshCell(cellData);
164
+ app._refreshCell(cellData);
143
165
  },
144
166
  copyCode: async () => {
145
167
  const textToCopy = cellData._editor.getValue() || cellData.code;
146
168
  try {
147
169
  await navigator.clipboard.writeText(textToCopy);
148
170
  } catch (err) {
149
- const textArea = document.createElement("textarea");
150
- textArea.value = textToCopy;
151
- document.body.appendChild(textArea);
152
- textArea.select();
171
+ const temporary = tags.textarea(textToCopy).mount(document.body)
172
+ temporary.element.select()
153
173
  document.execCommand("copy");
154
- document.body.removeChild(textArea);
174
+ temporary.unmount()
155
175
  }
156
176
  },
157
177
  toggleReadonly: () => {