@zuilib/text-editor 0.0.2 → 0.2.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/styles.css CHANGED
@@ -5,12 +5,153 @@
5
5
  overflow-y: auto;
6
6
  }
7
7
 
8
+ .zui-text-editor-body {
9
+ display: flex;
10
+ align-items: stretch;
11
+ min-height: 100%;
12
+ }
13
+
14
+ .zui-text-editor-main {
15
+ position: relative;
16
+ display: flex;
17
+ flex-direction: column;
18
+ flex: 1;
19
+ min-width: 0;
20
+ }
21
+
8
22
  .zui-text-editor-content {
23
+ flex: 1;
9
24
  min-height: 100%;
10
25
  padding: 1.5rem 2rem;
11
26
  outline: none;
12
27
  }
13
28
 
29
+ /* Outline (table of contents) sidebar */
30
+ .zui-text-editor-outline {
31
+ position: sticky;
32
+ top: 0.75rem;
33
+ align-self: flex-start;
34
+ flex-shrink: 0;
35
+ width: 220px;
36
+ max-height: min(70vh, 100%);
37
+ padding: 0.5rem 0.75rem 0.75rem 0.5rem;
38
+ overflow-y: auto;
39
+ }
40
+
41
+ .zui-text-editor:has(.zui-text-editor-toolbar) .zui-text-editor-outline {
42
+ top: 3.25rem;
43
+ }
44
+
45
+ .zui-text-editor-outline.is-collapsed {
46
+ width: auto;
47
+ }
48
+
49
+ .zui-text-editor-outline-toggle {
50
+ display: inline-flex;
51
+ align-items: center;
52
+ justify-content: center;
53
+ width: 1.5rem;
54
+ height: 1.5rem;
55
+ margin-bottom: 0.25rem;
56
+ border: none;
57
+ border-radius: 0.375rem;
58
+ background: transparent;
59
+ color: inherit;
60
+ opacity: 0.55;
61
+ cursor: pointer;
62
+ }
63
+
64
+ .zui-text-editor-outline-toggle:hover {
65
+ opacity: 1;
66
+ background: color-mix(in srgb, currentColor 8%, transparent);
67
+ }
68
+
69
+ .zui-text-editor-outline-list {
70
+ display: flex;
71
+ flex-direction: column;
72
+ gap: 1px;
73
+ border-left: 1px solid color-mix(in srgb, currentColor 12%, transparent);
74
+ }
75
+
76
+ .zui-text-editor-outline-item {
77
+ display: block;
78
+ width: 100%;
79
+ padding: 0.2rem 0.5rem;
80
+ border: none;
81
+ background: transparent;
82
+ color: inherit;
83
+ opacity: 0.6;
84
+ font-size: 0.8125rem;
85
+ line-height: 1.35;
86
+ text-align: left;
87
+ cursor: pointer;
88
+ overflow: hidden;
89
+ text-overflow: ellipsis;
90
+ white-space: nowrap;
91
+ }
92
+
93
+ .zui-text-editor-outline-item:hover {
94
+ opacity: 1;
95
+ }
96
+
97
+ .zui-text-editor-outline-item.is-active {
98
+ opacity: 1;
99
+ font-weight: 600;
100
+ box-shadow: inset 2px 0 0 #6965db;
101
+ }
102
+
103
+ .zui-text-editor-outline-empty {
104
+ padding: 0.2rem 0.5rem;
105
+ font-size: 0.8125rem;
106
+ opacity: 0.45;
107
+ }
108
+
109
+ /* Section folding */
110
+ .zui-text-editor-fold-gutter {
111
+ position: absolute;
112
+ inset: 0;
113
+ pointer-events: none;
114
+ }
115
+
116
+ .zui-text-editor-fold {
117
+ position: absolute;
118
+ left: 0.45rem;
119
+ display: inline-flex;
120
+ align-items: center;
121
+ justify-content: center;
122
+ width: 1.125rem;
123
+ height: 1.125rem;
124
+ border: none;
125
+ border-radius: 0.25rem;
126
+ background: transparent;
127
+ color: inherit;
128
+ opacity: 0;
129
+ cursor: pointer;
130
+ pointer-events: auto;
131
+ transition: opacity 120ms, transform 120ms;
132
+ }
133
+
134
+ .zui-text-editor-main:hover .zui-text-editor-fold {
135
+ opacity: 0.45;
136
+ }
137
+
138
+ .zui-text-editor-fold:hover {
139
+ opacity: 1 !important;
140
+ background: color-mix(in srgb, currentColor 8%, transparent);
141
+ }
142
+
143
+ .zui-text-editor-fold.is-folded {
144
+ opacity: 0.7;
145
+ transform: rotate(-90deg);
146
+ }
147
+
148
+ /* Folded heading: trailing ellipsis affordance */
149
+ .zui-heading-folded::after {
150
+ content: ' …';
151
+ opacity: 0.4;
152
+ font-weight: 400;
153
+ }
154
+
14
155
  .zui-text-editor-placeholder {
15
156
  position: absolute;
16
157
  top: 1.5rem;
@@ -80,6 +221,285 @@
80
221
  opacity: 0.7;
81
222
  }
82
223
 
224
+ /* Toolbar */
225
+ .zui-text-editor-toolbar {
226
+ position: sticky;
227
+ top: 0;
228
+ z-index: 10;
229
+ display: flex;
230
+ align-items: center;
231
+ gap: 0.125rem;
232
+ padding: 0.375rem 0.75rem;
233
+ border-bottom: 1px solid color-mix(in srgb, currentColor 12%, transparent);
234
+ background: inherit;
235
+ backdrop-filter: blur(8px);
236
+ }
237
+
238
+ .zui-text-editor-toolbar-button {
239
+ display: inline-flex;
240
+ align-items: center;
241
+ justify-content: center;
242
+ width: 1.75rem;
243
+ height: 1.75rem;
244
+ border: none;
245
+ border-radius: 0.375rem;
246
+ background: transparent;
247
+ color: inherit;
248
+ opacity: 0.65;
249
+ cursor: pointer;
250
+ }
251
+
252
+ .zui-text-editor-toolbar-button:hover {
253
+ opacity: 1;
254
+ background: color-mix(in srgb, currentColor 8%, transparent);
255
+ }
256
+
257
+ .zui-text-editor-toolbar-button.is-active {
258
+ opacity: 1;
259
+ background: color-mix(in srgb, currentColor 12%, transparent);
260
+ }
261
+
262
+ .zui-text-editor-toolbar-divider {
263
+ width: 1px;
264
+ height: 1.125rem;
265
+ margin: 0 0.375rem;
266
+ background: color-mix(in srgb, currentColor 15%, transparent);
267
+ }
268
+
269
+ /* Tables — artifact-style: rounded outer border, subtle header, tight rules */
270
+ .zui-table {
271
+ width: 100%;
272
+ margin: 1rem 0;
273
+ border-collapse: separate;
274
+ border-spacing: 0;
275
+ border: 1px solid color-mix(in srgb, currentColor 15%, transparent);
276
+ border-radius: 0.625rem;
277
+ overflow: hidden;
278
+ font-size: 0.875rem;
279
+ table-layout: fixed;
280
+ }
281
+
282
+ .zui-table-cell {
283
+ padding: 0.5rem 0.75rem;
284
+ border-bottom: 1px solid color-mix(in srgb, currentColor 10%, transparent);
285
+ border-right: 1px solid color-mix(in srgb, currentColor 10%, transparent);
286
+ vertical-align: top;
287
+ text-align: left;
288
+ }
289
+
290
+ .zui-table-cell:last-child {
291
+ border-right: none;
292
+ }
293
+
294
+ .zui-table tr:last-child > .zui-table-cell {
295
+ border-bottom: none;
296
+ }
297
+
298
+ .zui-table-cell-header {
299
+ background: color-mix(in srgb, currentColor 6%, transparent);
300
+ font-weight: 600;
301
+ }
302
+
303
+ .zui-table-cell > p,
304
+ .zui-table-cell-header > p {
305
+ margin-bottom: 0;
306
+ }
307
+
308
+ .zui-table-selected,
309
+ .zui-table-cell.zui-table-selected {
310
+ outline: none;
311
+ background: color-mix(in srgb, #6965db 14%, transparent);
312
+ }
313
+
314
+ /* Drawing canvas */
315
+ .zui-drawing {
316
+ margin: 1rem 0;
317
+ }
318
+
319
+ .zui-drawing-canvas {
320
+ position: relative;
321
+ border-radius: 0.75rem;
322
+ background: #ffffff;
323
+ outline: none;
324
+ overflow: hidden;
325
+ }
326
+
327
+ .zui-drawing-surface {
328
+ display: block;
329
+ width: 100%;
330
+ touch-action: none;
331
+ background-color: #ffffff;
332
+ background-image: radial-gradient(
333
+ circle,
334
+ rgba(0, 0, 0, 0.07) 1px,
335
+ transparent 1px
336
+ );
337
+ background-size: 20px 20px;
338
+ }
339
+
340
+ /*
341
+ * Dark mode (zui `.dark` class convention). The drawing surface, the color
342
+ * swatches, and the inline text input are darkened Excalidraw-style with a
343
+ * color-inversion filter, so stored shape colors stay theme-agnostic: the
344
+ * default near-black stroke renders light on the dark canvas, and the
345
+ * swatches preview exactly what will be drawn.
346
+ */
347
+ .dark .zui-drawing-canvas {
348
+ background: #18181b;
349
+ }
350
+
351
+ .dark .zui-drawing-surface,
352
+ .dark .zui-drawing-swatch,
353
+ .dark .zui-drawing-text-input {
354
+ filter: invert(93%) hue-rotate(180deg);
355
+ }
356
+
357
+ .dark .zui-drawing-toolbar-group {
358
+ background: #232329;
359
+ border-color: rgba(255, 255, 255, 0.1);
360
+ box-shadow: 0 1px 4px rgba(0, 0, 0, 0.4);
361
+ }
362
+
363
+ .dark .zui-drawing-tool {
364
+ color: #e3e3e8;
365
+ }
366
+
367
+ .dark .zui-drawing-tool:hover {
368
+ background: rgba(255, 255, 255, 0.08);
369
+ }
370
+
371
+ .dark .zui-drawing-tool.is-active {
372
+ background: #45437a;
373
+ color: #dcdaff;
374
+ }
375
+
376
+ .dark .zui-drawing-swatch-label {
377
+ color: rgba(255, 255, 255, 0.55);
378
+ }
379
+
380
+ .dark .zui-drawing-resize {
381
+ background: rgba(255, 255, 255, 0.2);
382
+ }
383
+
384
+ .dark .zui-drawing-resize:hover {
385
+ background: rgba(255, 255, 255, 0.35);
386
+ }
387
+
388
+ .zui-drawing-toolbar {
389
+ position: absolute;
390
+ top: 0.5rem;
391
+ left: 0.5rem;
392
+ right: 0.5rem;
393
+ z-index: 5;
394
+ display: flex;
395
+ flex-wrap: wrap;
396
+ align-items: center;
397
+ gap: 0.5rem;
398
+ pointer-events: none;
399
+ }
400
+
401
+ .zui-drawing-toolbar-group {
402
+ display: flex;
403
+ align-items: center;
404
+ gap: 0.125rem;
405
+ padding: 0.25rem;
406
+ border: 1px solid rgba(0, 0, 0, 0.08);
407
+ border-radius: 0.5rem;
408
+ background: #ffffff;
409
+ box-shadow: 0 1px 4px rgba(0, 0, 0, 0.08);
410
+ pointer-events: auto;
411
+ }
412
+
413
+ .zui-drawing-tool {
414
+ display: inline-flex;
415
+ align-items: center;
416
+ justify-content: center;
417
+ width: 1.75rem;
418
+ height: 1.75rem;
419
+ border: none;
420
+ border-radius: 0.375rem;
421
+ background: transparent;
422
+ color: #1e1e1e;
423
+ cursor: pointer;
424
+ }
425
+
426
+ .zui-drawing-tool:hover {
427
+ background: rgba(0, 0, 0, 0.06);
428
+ }
429
+
430
+ .zui-drawing-tool.is-active {
431
+ background: #e0dfff;
432
+ color: #4a47b1;
433
+ }
434
+
435
+ .zui-drawing-tool-danger {
436
+ color: #e03131;
437
+ }
438
+
439
+ .zui-drawing-swatch-label {
440
+ margin: 0 0.25rem;
441
+ font-size: 0.6875rem;
442
+ color: rgba(0, 0, 0, 0.5);
443
+ user-select: none;
444
+ }
445
+
446
+ .zui-drawing-swatch {
447
+ width: 1.125rem;
448
+ height: 1.125rem;
449
+ margin: 0 0.0625rem;
450
+ border: 1px solid rgba(0, 0, 0, 0.15);
451
+ border-radius: 0.3125rem;
452
+ cursor: pointer;
453
+ padding: 0;
454
+ }
455
+
456
+ .zui-drawing-swatch.is-active {
457
+ outline: 2px solid #6965db;
458
+ outline-offset: 1px;
459
+ }
460
+
461
+ .zui-drawing-swatch.is-transparent {
462
+ background-image: linear-gradient(
463
+ 45deg,
464
+ transparent 46%,
465
+ #e03131 46%,
466
+ #e03131 54%,
467
+ transparent 54%
468
+ );
469
+ }
470
+
471
+ .zui-drawing-text-input {
472
+ position: absolute;
473
+ z-index: 6;
474
+ padding: 2px 4px;
475
+ border: 1px dashed #6965db;
476
+ border-radius: 0.25rem;
477
+ background: rgba(255, 255, 255, 0.9);
478
+ font-family: ui-sans-serif, system-ui, sans-serif;
479
+ line-height: 1.35;
480
+ resize: none;
481
+ outline: none;
482
+ overflow: hidden;
483
+ white-space: pre;
484
+ }
485
+
486
+ .zui-drawing-resize {
487
+ position: absolute;
488
+ bottom: 0.25rem;
489
+ left: 50%;
490
+ transform: translateX(-50%);
491
+ width: 3rem;
492
+ height: 0.375rem;
493
+ border-radius: 999px;
494
+ background: rgba(0, 0, 0, 0.15);
495
+ cursor: ns-resize;
496
+ touch-action: none;
497
+ }
498
+
499
+ .zui-drawing-resize:hover {
500
+ background: rgba(0, 0, 0, 0.3);
501
+ }
502
+
83
503
  .zui-text-editor-textarea {
84
504
  width: 100%;
85
505
  height: 100%;
package/package.json CHANGED
@@ -1,15 +1,16 @@
1
1
  {
2
2
  "name": "@zuilib/text-editor",
3
- "version": "0.0.2",
3
+ "version": "0.2.0",
4
4
  "description": "ZUI — A markdown editor component wrapping Lexical",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
7
7
  "types": "./dist/index.d.ts",
8
- "sideEffects": ["**/*.css"],
9
- "files": ["dist"],
10
- "scripts": {
11
- "build": "tsup"
12
- },
8
+ "sideEffects": [
9
+ "**/*.css"
10
+ ],
11
+ "files": [
12
+ "dist"
13
+ ],
13
14
  "exports": {
14
15
  ".": {
15
16
  "types": "./dist/index.d.ts",
@@ -19,31 +20,36 @@
19
20
  "./package.json": "./package.json"
20
21
  },
21
22
  "peerDependencies": {
22
- "react": "^18.0.0 || ^19.0.0",
23
- "react-dom": "^18.0.0 || ^19.0.0",
24
- "lexical": "^0.35.0",
25
- "@lexical/react": "^0.35.0",
26
- "@lexical/markdown": "^0.35.0",
27
- "@lexical/rich-text": "^0.35.0",
28
23
  "@lexical/code": "^0.35.0",
24
+ "@lexical/link": "^0.35.0",
29
25
  "@lexical/list": "^0.35.0",
30
- "@lexical/link": "^0.35.0"
26
+ "@lexical/markdown": "^0.35.0",
27
+ "@lexical/react": "^0.35.0",
28
+ "@lexical/rich-text": "^0.35.0",
29
+ "@lexical/table": "^0.35.0",
30
+ "@lexical/utils": "^0.35.0",
31
+ "lexical": "^0.35.0",
32
+ "react": "^18.0.0 || ^19.0.0",
33
+ "react-dom": "^18.0.0 || ^19.0.0"
31
34
  },
32
35
  "dependencies": {
33
36
  "@zuilib/core": "^0.0.0"
34
37
  },
35
38
  "devDependencies": {
39
+ "@lexical/code": "^0.35.0",
40
+ "@lexical/headless": "0.35.0",
41
+ "@lexical/link": "^0.35.0",
42
+ "@lexical/list": "^0.35.0",
43
+ "@lexical/markdown": "^0.35.0",
44
+ "@lexical/react": "^0.35.0",
45
+ "@lexical/rich-text": "^0.35.0",
46
+ "@lexical/table": "^0.35.0",
47
+ "@lexical/utils": "^0.35.0",
36
48
  "@types/react": "^18.0.0 || ^19.0.0",
37
49
  "@types/react-dom": "^18.0.0 || ^19.0.0",
38
- "react": "^18.0.0 || ^19.0.0",
39
- "react-dom": "^18.0.0 || ^19.0.0",
40
50
  "lexical": "^0.35.0",
41
- "@lexical/react": "^0.35.0",
42
- "@lexical/markdown": "^0.35.0",
43
- "@lexical/rich-text": "^0.35.0",
44
- "@lexical/code": "^0.35.0",
45
- "@lexical/list": "^0.35.0",
46
- "@lexical/link": "^0.35.0"
51
+ "react": "^18.0.0 || ^19.0.0",
52
+ "react-dom": "^18.0.0 || ^19.0.0"
47
53
  },
48
54
  "keywords": [
49
55
  "lexical",
@@ -53,5 +59,9 @@
53
59
  "design-system",
54
60
  "react",
55
61
  "typescript"
56
- ]
57
- }
62
+ ],
63
+ "scripts": {
64
+ "build": "tsup",
65
+ "test": "node tests/roundtrip.mjs"
66
+ }
67
+ }