@puredesktop/platform-editor 1.0.0-beta.1 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. package/package.json +1 -1
  2. package/src/CollapsePanel.tsx +35 -35
  3. package/src/DocumentDiffView.tsx +130 -130
  4. package/src/DocumentEditor.test.ts +118 -118
  5. package/src/DocumentEditor.tsx +2651 -2631
  6. package/src/alignedLineDiff.test.ts +52 -52
  7. package/src/alignedLineDiff.ts +67 -67
  8. package/src/collectionAssetSrc.ts +2 -2
  9. package/src/commentUtils.test.ts +350 -350
  10. package/src/commentUtils.ts +546 -546
  11. package/src/constants/toolKeys.ts +14 -14
  12. package/src/contentFormat.test.ts +27 -27
  13. package/src/contentFormat.ts +18 -18
  14. package/src/editorExtensions.ts +155 -155
  15. package/src/extensions/appliedChangeMark.ts +115 -115
  16. package/src/extensions/autoReviewPrompts.test.ts +529 -529
  17. package/src/extensions/autoReviewPrompts.ts +1442 -1442
  18. package/src/extensions/collectionImage.ts +26 -26
  19. package/src/extensions/collectionImagePaste.ts +215 -215
  20. package/src/extensions/commentMark.ts +223 -223
  21. package/src/extensions/documentAssetIdentity.ts +54 -54
  22. package/src/extensions/figure.ts +92 -92
  23. package/src/extensions/footnote.ts +186 -186
  24. package/src/extensions/indexMarker.ts +88 -88
  25. package/src/extensions/mathEditing.ts +239 -239
  26. package/src/extensions/mermaidBlock.tsx +297 -297
  27. package/src/extensions/slashCommands.test.ts +170 -170
  28. package/src/extensions/slashCommands.ts +746 -746
  29. package/src/extensions/smartTypography.test.ts +74 -74
  30. package/src/extensions/smartTypography.ts +120 -120
  31. package/src/index.ts +138 -137
  32. package/src/insertCollectionImage.test.ts +60 -60
  33. package/src/insertCollectionImage.ts +63 -63
  34. package/src/insertInlineAssetKind.test.ts +67 -67
  35. package/src/insertInlineAssetKind.ts +49 -49
  36. package/src/mermaidPreview.test.ts +54 -54
  37. package/src/mermaidPreview.ts +115 -115
  38. package/src/styled.ts +697 -676
  39. package/src/toolbar/ToolBtn.tsx +77 -63
  40. package/src/toolbar/Toolbar.test.tsx +325 -325
  41. package/src/toolbar/Toolbar.tsx +662 -624
  42. package/src/toolbar/groups/HeadingGroup.tsx +175 -153
  43. package/src/toolbar/overlayTypes.ts +28 -28
  44. package/src/useEditorExtensions.ts +22 -22
  45. package/src/utils/markdownUtils.ts +331 -331
package/src/styled.ts CHANGED
@@ -1,676 +1,697 @@
1
- import { styled } from 'styled-components'
2
-
3
- // ---- Wrapper ----
4
- //
5
- // Editor surface redesign (#237): the wrapper renders the gray
6
- // editor *field* behind the white writing column. No floating
7
- // page-card treatment — just a flat white column on a soft
8
- // neutral field, iA-Writer style.
9
-
10
- // ---- Content area (prose + column layout for DocumentEditor) ----
11
-
12
- export const ContentArea = styled.div`
13
- --vs-column-width: 800px;
14
- --h1-font-size: 2.2em;
15
- --page-pad-inline: 88px;
16
- --comments-lane-width: 320px;
17
- --outer-lane-padding: 72px;
18
- --paper-width: calc(var(--vs-column-width) + (var(--page-pad-inline) * 2));
19
- background: var(--platform-colors-app-viewport);
20
- display: flex;
21
- flex: 1;
22
- flex-direction: column;
23
- overflow: auto;
24
- padding: 0 calc(var(--outer-lane-padding) + var(--comments-lane-width)) 88px
25
- var(--outer-lane-padding);
26
- position: relative;
27
-
28
- .tiptap {
29
- background: var(--platform-colors-surface);
30
- border-left: var(--vs-column-rule, none);
31
- border-right: var(--vs-column-rule, none);
32
- border-top: none;
33
- border-bottom: none;
34
- border-radius: 0;
35
- box-shadow: none;
36
- box-sizing: border-box;
37
- caret-color: var(--platform-colors-accent);
38
- color: var(--platform-colors-text);
39
- flex-shrink: 0;
40
- font-family: var(
41
- --vs-body-font,
42
- var(
43
- --platform-typography-font-family-content,
44
- var(--platform-typography-font-family)
45
- )
46
- );
47
- font-size: var(--vs-font-size, 17px);
48
- line-height: var(--vs-line-height, 1.7);
49
- margin: 0 auto;
50
- max-width: none;
51
- min-height: calc(100vh - 88px);
52
- min-width: var(--paper-width);
53
- outline: none;
54
- padding: 20px var(--page-pad-inline) 120px;
55
- width: var(--paper-width);
56
-
57
- > * + * {
58
- margin-top: 0.7em;
59
- }
60
-
61
- p {
62
- margin: 0;
63
- }
64
-
65
- h1 {
66
- color: var(--platform-colors-text);
67
- font-size: var(--h1-font-size);
68
- font-weight: 400;
69
- letter-spacing: -0.03em;
70
- line-height: 1.08;
71
- margin-top: 1.22em;
72
- }
73
-
74
- h2 {
75
- color: var(--platform-colors-text);
76
- font-size: 1.55em;
77
- font-weight: 400;
78
- letter-spacing: -0.02em;
79
- line-height: 1.22;
80
- margin-top: 1em;
81
- }
82
-
83
- h3 {
84
- color: var(--platform-colors-text-secondary);
85
- font-size: 1.08em;
86
- font-weight: 500;
87
- line-height: 1.35;
88
- margin-top: 0.88em;
89
- letter-spacing: 0.08em;
90
- text-transform: uppercase;
91
- }
92
-
93
- p {
94
- margin: 0;
95
- hanging-punctuation: first last;
96
- }
97
-
98
- ul,
99
- ol {
100
- padding-left: 1.9em;
101
- }
102
-
103
- ul[data-type='taskList'] {
104
- list-style: none;
105
- padding-left: 0;
106
-
107
- li {
108
- align-items: flex-start;
109
- display: flex;
110
- gap: 10px;
111
-
112
- > label {
113
- flex-shrink: 0;
114
- margin-top: 4px;
115
-
116
- input[type='checkbox'] {
117
- accent-color: var(--platform-colors-accent);
118
- cursor: pointer;
119
- height: 14px;
120
- width: 14px;
121
- }
122
- }
123
- }
124
- }
125
-
126
- blockquote {
127
- border-left: 1px solid rgb(from var(--platform-colors-text) r g b / 0.18);
128
- color: var(--platform-colors-text-secondary);
129
- font-style: italic;
130
- margin: 0.9em 0;
131
- padding: 4px 0 4px 16px;
132
- }
133
-
134
- code {
135
- background: rgb(from var(--platform-colors-surface-hover) r g b / 0.9);
136
- border: 1px solid rgb(from var(--platform-colors-border) r g b / 0.82);
137
- border-radius: 4px;
138
- font-family: var(--platform-typography-font-family-mono);
139
- font-size: 0.9em;
140
- padding: 0.1em 0.4em;
141
- }
142
-
143
- pre {
144
- background: rgb(from var(--platform-colors-surface-hover) r g b / 0.94);
145
- border: 1px solid rgb(from var(--platform-colors-border) r g b / 0.82);
146
- border-radius: 8px;
147
- font-family: var(--platform-typography-font-family-mono);
148
- margin: 0.75em 0;
149
- overflow-x: auto;
150
- padding: 14px 18px;
151
-
152
- code {
153
- background: none;
154
- border: none;
155
- font-size: 0.875em;
156
- padding: 0;
157
- }
158
-
159
- .hljs-comment,
160
- .hljs-quote {
161
- color: var(--platform-colors-text-secondary);
162
- }
163
-
164
- .hljs-keyword,
165
- .hljs-selector-tag,
166
- .hljs-literal,
167
- .hljs-section,
168
- .hljs-link {
169
- color: #8f3f71;
170
- }
171
-
172
- .hljs-string,
173
- .hljs-title,
174
- .hljs-name,
175
- .hljs-type,
176
- .hljs-attribute,
177
- .hljs-symbol,
178
- .hljs-bullet,
179
- .hljs-addition {
180
- color: #1e6f5c;
181
- }
182
-
183
- .hljs-number,
184
- .hljs-variable,
185
- .hljs-template-variable,
186
- .hljs-deletion {
187
- color: #b35c1e;
188
- }
189
- }
190
-
191
- .tiptap-mathematics-render {
192
- display: inline-flex;
193
- align-items: center;
194
- min-height: 1.4em;
195
- padding: 0.08em 0.22em;
196
- border-radius: 4px;
197
- }
198
-
199
- .tiptap-mathematics-render[data-type='block-math'] {
200
- display: flex;
201
- justify-content: center;
202
- margin: 1.1em 0;
203
- padding: 1em 1.2em;
204
- background: rgb(from var(--platform-colors-surface-hover) r g b / 0.72);
205
- border: 1px solid rgb(from var(--platform-colors-border) r g b / 0.82);
206
- }
207
-
208
- .tiptap-mathematics-render--editable {
209
- cursor: pointer;
210
- }
211
-
212
- .inline-math-error,
213
- .block-math-error {
214
- color: var(--platform-colors-text-danger);
215
- font-family: var(--platform-typography-font-family-mono);
216
- }
217
-
218
- a {
219
- color: var(--platform-colors-accent);
220
- text-decoration: underline;
221
- text-underline-offset: 2px;
222
-
223
- &:hover {
224
- opacity: 0.85;
225
- }
226
- }
227
-
228
- a.ix {
229
- color: inherit;
230
- text-decoration: underline dotted var(--platform-colors-text-disabled);
231
- text-underline-offset: 3px;
232
- }
233
-
234
- sup.footnote-ref {
235
- color: var(--platform-colors-accent);
236
- cursor: pointer;
237
- font-weight: 600;
238
- padding: 0 1px;
239
- }
240
-
241
- mark {
242
- background: rgb(from var(--platform-colors-warning) r g b / 35%);
243
- border-radius: 2px;
244
- color: inherit;
245
- padding: 0.05em 0.25em;
246
- }
247
-
248
- span.comment-mark {
249
- background: rgb(from var(--platform-colors-accent) r g b / 12%);
250
- border-bottom: 2px solid
251
- rgb(from var(--platform-colors-accent) r g b / 52%);
252
- border-radius: 2px;
253
- box-decoration-break: clone;
254
- }
255
-
256
- table {
257
- border-collapse: collapse;
258
- margin: 1em 0;
259
- overflow: hidden;
260
- table-layout: fixed;
261
- width: 100%;
262
-
263
- td,
264
- th {
265
- border: 1px solid var(--platform-colors-border);
266
- box-sizing: border-box;
267
- min-width: 1em;
268
- padding: 8px 12px;
269
- position: relative;
270
- vertical-align: top;
271
-
272
- > * {
273
- margin-bottom: 0;
274
- }
275
- }
276
-
277
- th {
278
- background: var(--platform-colors-surface-hover);
279
- font-weight: 600;
280
- text-align: left;
281
- }
282
-
283
- .selectedCell::after {
284
- background: rgb(from var(--platform-colors-accent) r g b / 15%);
285
- content: '';
286
- inset: 0;
287
- pointer-events: none;
288
- position: absolute;
289
- z-index: 2;
290
- }
291
- }
292
-
293
- hr {
294
- border: none;
295
- border-top: 1px solid rgb(from var(--platform-colors-border) r g b / 0.95);
296
- margin: 1.7em 0;
297
- }
298
-
299
- .is-editor-empty::before,
300
- .is-empty::before {
301
- color: var(--platform-colors-text-disabled);
302
- content: attr(data-placeholder);
303
- font-style: italic;
304
- float: left;
305
- height: 0;
306
- pointer-events: none;
307
- }
308
- }
309
-
310
- @media (max-width: 1560px) {
311
- --comments-lane-width: 0px;
312
- }
313
-
314
- @media (max-width: 1200px) {
315
- --outer-lane-padding: 24px;
316
- }
317
- `
318
-
319
- // ---- DocumentEditor layout ----
320
-
321
- export const DocumentEditorRoot = styled.div<{ $pageMode?: boolean }>`
322
- display: flex;
323
- flex-direction: column;
324
- flex: 1;
325
- min-height: 0;
326
- min-width: 0;
327
- background: ${({ $pageMode }) =>
328
- $pageMode
329
- ? 'var(--platform-colors-surface)'
330
- : 'var(--platform-colors-bg)'};
331
- overflow: visible;
332
- position: relative;
333
- `
334
-
335
- export const DocumentEditorToolbarWrap = styled.div`
336
- position: relative;
337
- z-index: 40;
338
- display: flex;
339
- align-items: flex-start;
340
- justify-content: center;
341
- gap: 12px;
342
- background: var(--platform-colors-bg);
343
- padding: 0 12px;
344
-
345
- > div {
346
- padding: 8px 10px;
347
- overflow: visible;
348
- }
349
-
350
- > div:first-child {
351
- width: auto;
352
- margin: 0;
353
- }
354
- `
355
-
356
- export const DocumentEditorToolbarActions = styled.div`
357
- display: flex;
358
- align-items: center;
359
- gap: 6px;
360
- height: 100%;
361
- `
362
-
363
- /**
364
- * Writer scroll lane + paper column (ps-book `StyledEditorContent` layout).
365
- * Wraps plain `EditorContent` — do not `styled(EditorContent)` (TipTap v3 root is a fragment).
366
- */
367
- export const DocumentEditorViewport = styled.div<{ $pageMode?: boolean }>`
368
- --vs-column-width: 800px;
369
- --h1-font-size: 2.2em;
370
- --page-pad-inline: 88px;
371
- --comments-lane-width: 0px;
372
- --outer-lane-padding: 72px;
373
- --paper-width: calc(var(--vs-column-width) + (var(--page-pad-inline) * 2));
374
- flex: 1;
375
- min-height: 0;
376
- overflow: auto;
377
- background: ${({ $pageMode }) =>
378
- $pageMode
379
- ? 'var(--platform-colors-surface)'
380
- : 'var(--platform-colors-bg)'};
381
- padding: ${({ $pageMode }) =>
382
- $pageMode ? '0' : '0 var(--outer-lane-padding)'};
383
- position: relative;
384
- z-index: 1;
385
- transition: padding-right 160ms ease;
386
-
387
- &.document-editor-viewport--annotation-rail-open {
388
- padding-right: calc(var(--outer-lane-padding) + 430px);
389
- }
390
-
391
- & > div {
392
- min-height: 100%;
393
- }
394
-
395
- .tiptap,
396
- .ProseMirror {
397
- background: var(--platform-colors-surface);
398
- border-left: var(--vs-column-rule, none);
399
- border-right: var(--vs-column-rule, none);
400
- box-sizing: border-box;
401
- caret-color: var(--platform-colors-accent);
402
-
403
- outline: none;
404
- }
405
-
406
- .tiptap > *:first-child {
407
- margin-top: 0;
408
- }
409
-
410
- .tiptap h1 {
411
- margin-top: 1.22em;
412
- color: var(--platform-colors-text);
413
- font-size: var(--h1-font-size);
414
- font-weight: 400;
415
- letter-spacing: -0.03em;
416
- line-height: 1.08;
417
- }
418
-
419
- .tiptap > h1:first-child,
420
- .tiptap > h2:first-child,
421
- .tiptap > h3:first-child {
422
- margin-top: 0;
423
- }
424
-
425
- .tiptap h2 {
426
- margin-top: 1em;
427
- color: var(--platform-colors-text);
428
- font-size: 1.55em;
429
- font-weight: 400;
430
- letter-spacing: -0.02em;
431
- line-height: 1.22;
432
- }
433
-
434
- .tiptap h3 {
435
- margin-top: 0.88em;
436
- color: var(--platform-colors-text-secondary);
437
- font-size: 1.08em;
438
- font-weight: 500;
439
- letter-spacing: 0.08em;
440
- line-height: 1.35;
441
- text-transform: uppercase;
442
- }
443
-
444
- .tiptap p,
445
- .tiptap ul,
446
- .tiptap ol,
447
- .tiptap blockquote,
448
- .tiptap pre {
449
- margin: 0;
450
- }
451
-
452
- .tiptap > * + * {
453
- margin-top: 0.7em;
454
- }
455
-
456
- .tiptap h1 + *,
457
- .tiptap h2 + * {
458
- margin-top: 1.25em;
459
- }
460
-
461
- .tiptap p {
462
- hanging-punctuation: first last;
463
- }
464
-
465
- .tiptap ul,
466
- .tiptap ol {
467
- padding-left: 1.9em;
468
- }
469
-
470
- .tiptap blockquote {
471
- margin: 0.9em 0;
472
- padding: 4px 0 4px 16px;
473
- border-left: 1px solid rgb(from var(--platform-colors-text) r g b / 0.18);
474
- color: var(--platform-colors-text-secondary);
475
- font-style: italic;
476
- }
477
-
478
- .tiptap table {
479
- width: 100%;
480
- margin: 1em 0;
481
- border-collapse: collapse;
482
- table-layout: fixed;
483
- }
484
-
485
- .tiptap th,
486
- .tiptap td {
487
- padding: 8px 12px;
488
- border: 1px solid var(--platform-colors-border);
489
- vertical-align: top;
490
- }
491
-
492
- .tiptap th {
493
- background: var(--platform-colors-surface);
494
- font-weight: 600;
495
- }
496
-
497
- .tiptap img {
498
- display: block;
499
- max-width: 100%;
500
- height: auto;
501
- margin: 1em auto;
502
- }
503
-
504
- .tiptap figure {
505
- margin: 1em 0;
506
- }
507
-
508
- .tiptap figcaption {
509
- margin-top: 0.55em;
510
- color: var(--platform-colors-text-secondary);
511
- font-size: 0.84em;
512
- text-align: center;
513
- }
514
-
515
- .tiptap span.comment-mark {
516
- background: transparent;
517
- border-bottom: 1px solid
518
- var(
519
- --comment-mark-border,
520
- rgb(from var(--platform-colors-accent) r g b / 46%)
521
- );
522
- border-radius: 2px;
523
- box-decoration-break: clone;
524
- cursor: pointer;
525
- padding: 0 1px;
526
- }
527
-
528
- .tiptap span.comment-mark:hover,
529
- .tiptap span.comment-mark:focus,
530
- .tiptap span.comment-mark--active,
531
- .tiptap span.comment-mark--spotlight {
532
- background: linear-gradient(
533
- to bottom,
534
- transparent 56%,
535
- var(
536
- --comment-mark-bg,
537
- rgb(from var(--platform-colors-accent) r g b / 14%)
538
- )
539
- 56%
540
- );
541
- }
542
-
543
- .tiptap span.comment-mark--agent-review {
544
- --comment-mark-bg: rgb(199 210 254 / 0.22);
545
- --comment-mark-border: rgb(99 102 241 / 0.44);
546
- }
547
-
548
- .tiptap span.comment-mark--formatting {
549
- --comment-mark-bg: rgb(221 214 254 / 0.22);
550
- --comment-mark-border: rgb(124 58 237 / 0.44);
551
- }
552
-
553
- .tiptap span.comment-mark--copy-editing {
554
- --comment-mark-bg: rgb(187 247 208 / 0.22);
555
- --comment-mark-border: rgb(5 150 105 / 0.42);
556
- }
557
-
558
- .tiptap span.comment-mark--copyedit {
559
- --comment-mark-bg: rgb(187 247 208 / 0.22);
560
- --comment-mark-border: rgb(22 163 74 / 0.58);
561
- }
562
-
563
- .tiptap span.comment-mark--voice {
564
- --comment-mark-bg: rgb(251 207 232 / 0.2);
565
- --comment-mark-border: rgb(219 39 119 / 0.52);
566
- }
567
-
568
- .tiptap span.comment-mark--claim {
569
- --comment-mark-bg: rgb(251 191 36 / 0.24);
570
- --comment-mark-border: rgb(217 119 6 / 0.74);
571
- background: linear-gradient(
572
- to bottom,
573
- transparent 58%,
574
- rgb(251 191 36 / 0.18) 58%
575
- );
576
- border-bottom-width: 2px;
577
- }
578
-
579
- .tiptap span.comment-mark--fact-check {
580
- --comment-mark-bg: rgb(254 202 202 / 0.28);
581
- --comment-mark-border: rgb(220 38 38 / 0.78);
582
- background: linear-gradient(
583
- to bottom,
584
- transparent 55%,
585
- rgb(254 202 202 / 0.24) 55%
586
- );
587
- border-bottom-width: 2px;
588
- }
589
-
590
- .tiptap span.comment-mark--source {
591
- --comment-mark-bg: rgb(191 219 254 / 0.22);
592
- --comment-mark-border: rgb(37 99 235 / 0.58);
593
- }
594
-
595
- .tiptap span.comment-mark--thread {
596
- --comment-mark-bg: rgb(221 214 254 / 0.22);
597
- --comment-mark-border: rgb(124 58 237 / 0.5);
598
- }
599
-
600
- .tiptap span.comment-mark--conflict,
601
- .tiptap span.comment-mark--logic {
602
- --comment-mark-bg: rgb(226 232 240 / 0.34);
603
- --comment-mark-border: rgb(71 85 105 / 0.52);
604
- }
605
-
606
- .tiptap span.comment-mark--question {
607
- --comment-mark-bg: rgb(153 246 228 / 0.24);
608
- --comment-mark-border: rgb(13 148 136 / 0.46);
609
- }
610
-
611
- .tiptap span.comment-mark--citation {
612
- --comment-mark-bg: rgb(191 219 254 / 0.22);
613
- --comment-mark-border: rgb(37 99 235 / 0.44);
614
- }
615
-
616
- .tiptap span.comment-mark--spotlight-muted {
617
- background: transparent;
618
- border-bottom-color: rgb(from var(--platform-colors-text) r g b / 9%);
619
- color: rgb(from var(--platform-colors-text) r g b / 0.78);
620
- transition:
621
- background 160ms ease,
622
- border-color 160ms ease,
623
- color 160ms ease;
624
- }
625
-
626
- .tiptap span.comment-mark--resolved {
627
- background: transparent;
628
- border-bottom-color: rgb(
629
- from var(--platform-colors-text-disabled) r g b / 32%
630
- );
631
- border-bottom-width: 1px;
632
- color: rgb(from var(--platform-colors-text) r g b / 0.72);
633
- }
634
-
635
- .tiptap .is-empty::before {
636
- float: left;
637
- height: 0;
638
- color: var(--platform-colors-text-disabled);
639
- content: attr(data-placeholder);
640
- pointer-events: none;
641
- }
642
-
643
- @media (max-width: 1200px) {
644
- --outer-lane-padding: 24px;
645
- }
646
- `
647
-
648
- export const EditorWrapper = styled.div<{ $pageMode?: boolean }>`
649
- background: var(--platform-colors-surface);
650
- border-left: var(--vs-column-rule, none);
651
- border-right: var(--vs-column-rule, none);
652
- box-sizing: border-box;
653
- caret-color: var(--platform-colors-accent);
654
- flex-shrink: 0;
655
- min-height: calc(100vh - 88px);
656
- min-width: var(--paper-width);
657
- width: var(--paper-width);
658
- max-width: none;
659
- margin: 0 auto;
660
- padding: 56px var(--page-pad-inline) 120px;
661
- color: var(--platform-colors-text);
662
- font-family: var(--vs-body-font, var(--platform-typography-font-family));
663
- font-size: var(--vs-font-size, 17px);
664
- line-height: var(--vs-line-height, 1.7);
665
- outline: none;
666
-
667
- ${({ $pageMode }) =>
668
- $pageMode
669
- ? `
670
- min-width: min(100%, var(--paper-width));
671
- width: min(100%, var(--paper-width));
672
- min-height: 100vh;
673
- padding-top: clamp(56px, 8vh, 104px);
674
- `
675
- : ''}
676
- `
1
+ import { styled } from 'styled-components'
2
+
3
+ // ---- Wrapper ----
4
+ //
5
+ // Editor surface redesign (#237): the wrapper renders the gray
6
+ // editor *field* behind the white writing column. No floating
7
+ // page-card treatment — just a flat white column on a soft
8
+ // neutral field, iA-Writer style.
9
+
10
+ // ---- Content area (prose + column layout for DocumentEditor) ----
11
+
12
+ export const ContentArea = styled.div`
13
+ --vs-column-width: 800px;
14
+ --h1-font-size: 2.2em;
15
+ --page-pad-inline: 88px;
16
+ --comments-lane-width: 320px;
17
+ --outer-lane-padding: 72px;
18
+ --paper-width: calc(var(--vs-column-width) + (var(--page-pad-inline) * 2));
19
+ --editor-paper: var(--platform-colors-surface);
20
+ --editor-paper-text: var(--platform-colors-text);
21
+ --editor-paper-muted: var(--platform-colors-text-secondary);
22
+ --editor-paper-border: var(--platform-colors-border);
23
+ --editor-paper-subtle: var(--platform-colors-surface-hover);
24
+ background: var(--platform-colors-app-viewport);
25
+ display: flex;
26
+ flex: 1;
27
+ flex-direction: column;
28
+ overflow: auto;
29
+ padding: 0 calc(var(--outer-lane-padding) + var(--comments-lane-width)) 88px
30
+ var(--outer-lane-padding);
31
+ position: relative;
32
+
33
+ .tiptap {
34
+ background: var(--editor-paper);
35
+ border-left: var(--vs-column-rule, none);
36
+ border-right: var(--vs-column-rule, none);
37
+ border-top: none;
38
+ border-bottom: none;
39
+ border-radius: 0;
40
+ box-shadow: none;
41
+ box-sizing: border-box;
42
+ caret-color: var(--platform-colors-accent);
43
+ color: var(--editor-paper-text);
44
+ flex-shrink: 0;
45
+ font-family: var(
46
+ --vs-body-font,
47
+ var(
48
+ --platform-typography-font-family-content,
49
+ var(--platform-typography-font-family)
50
+ )
51
+ );
52
+ font-size: var(--vs-font-size, 17px);
53
+ line-height: var(--vs-line-height, 1.7);
54
+ margin: 0 auto;
55
+ max-width: none;
56
+ min-height: calc(100vh - 88px);
57
+ min-width: var(--paper-width);
58
+ outline: none;
59
+ padding: 20px var(--page-pad-inline) 120px;
60
+ width: var(--paper-width);
61
+
62
+ > * + * {
63
+ margin-top: 0.7em;
64
+ }
65
+
66
+ p {
67
+ margin: 0;
68
+ }
69
+
70
+ h1 {
71
+ color: var(--editor-paper-text);
72
+ font-size: var(--h1-font-size);
73
+ font-weight: 400;
74
+ letter-spacing: -0.03em;
75
+ line-height: 1.08;
76
+ margin-top: 1.22em;
77
+ }
78
+
79
+ h2 {
80
+ color: var(--editor-paper-text);
81
+ font-size: 1.55em;
82
+ font-weight: 400;
83
+ letter-spacing: -0.02em;
84
+ line-height: 1.22;
85
+ margin-top: 1em;
86
+ }
87
+
88
+ h3 {
89
+ color: var(--editor-paper-muted);
90
+ font-size: 1.08em;
91
+ font-weight: 500;
92
+ line-height: 1.35;
93
+ margin-top: 0.88em;
94
+ letter-spacing: 0.08em;
95
+ text-transform: uppercase;
96
+ }
97
+
98
+ p {
99
+ margin: 0;
100
+ hanging-punctuation: first last;
101
+ }
102
+
103
+ ul,
104
+ ol {
105
+ padding-left: 1.9em;
106
+ }
107
+
108
+ ul[data-type='taskList'] {
109
+ list-style: none;
110
+ padding-left: 0;
111
+
112
+ li {
113
+ align-items: flex-start;
114
+ display: flex;
115
+ gap: 10px;
116
+
117
+ > label {
118
+ flex-shrink: 0;
119
+ margin-top: 4px;
120
+
121
+ input[type='checkbox'] {
122
+ accent-color: var(--platform-colors-accent);
123
+ cursor: pointer;
124
+ height: 14px;
125
+ width: 14px;
126
+ }
127
+ }
128
+ }
129
+ }
130
+
131
+ blockquote {
132
+ border-left: 1px solid rgb(from var(--editor-paper-text) r g b / 0.18);
133
+ color: var(--editor-paper-muted);
134
+ font-style: italic;
135
+ margin: 0.9em 0;
136
+ padding: 4px 0 4px 16px;
137
+ }
138
+
139
+ code {
140
+ background: rgb(from var(--editor-paper-subtle) r g b / 0.9);
141
+ border: 1px solid rgb(from var(--editor-paper-border) r g b / 0.82);
142
+ border-radius: 4px;
143
+ font-family: var(--platform-typography-font-family-mono);
144
+ font-size: 0.9em;
145
+ padding: 0.1em 0.4em;
146
+ }
147
+
148
+ pre {
149
+ background: rgb(from var(--editor-paper-subtle) r g b / 0.94);
150
+ border: 1px solid rgb(from var(--editor-paper-border) r g b / 0.82);
151
+ border-radius: 8px;
152
+ font-family: var(--platform-typography-font-family-mono);
153
+ margin: 0.75em 0;
154
+ overflow-x: auto;
155
+ padding: 14px 18px;
156
+
157
+ code {
158
+ background: none;
159
+ border: none;
160
+ font-size: 0.875em;
161
+ padding: 0;
162
+ }
163
+
164
+ .hljs-comment,
165
+ .hljs-quote {
166
+ color: var(--editor-paper-muted);
167
+ }
168
+
169
+ .hljs-keyword,
170
+ .hljs-selector-tag,
171
+ .hljs-literal,
172
+ .hljs-section,
173
+ .hljs-link {
174
+ color: #8f3f71;
175
+ }
176
+
177
+ .hljs-string,
178
+ .hljs-title,
179
+ .hljs-name,
180
+ .hljs-type,
181
+ .hljs-attribute,
182
+ .hljs-symbol,
183
+ .hljs-bullet,
184
+ .hljs-addition {
185
+ color: #1e6f5c;
186
+ }
187
+
188
+ .hljs-number,
189
+ .hljs-variable,
190
+ .hljs-template-variable,
191
+ .hljs-deletion {
192
+ color: #b35c1e;
193
+ }
194
+ }
195
+
196
+ .tiptap-mathematics-render {
197
+ display: inline-flex;
198
+ align-items: center;
199
+ min-height: 1.4em;
200
+ padding: 0.08em 0.22em;
201
+ border-radius: 4px;
202
+ }
203
+
204
+ .tiptap-mathematics-render[data-type='block-math'] {
205
+ display: flex;
206
+ justify-content: center;
207
+ margin: 1.1em 0;
208
+ padding: 1em 1.2em;
209
+ background: rgb(from var(--editor-paper-subtle) r g b / 0.72);
210
+ border: 1px solid rgb(from var(--editor-paper-border) r g b / 0.82);
211
+ }
212
+
213
+ .tiptap-mathematics-render--editable {
214
+ cursor: pointer;
215
+ }
216
+
217
+ .inline-math-error,
218
+ .block-math-error {
219
+ color: var(--platform-colors-text-danger);
220
+ font-family: var(--platform-typography-font-family-mono);
221
+ }
222
+
223
+ a {
224
+ color: var(--platform-colors-accent);
225
+ text-decoration: underline;
226
+ text-underline-offset: 2px;
227
+
228
+ &:hover {
229
+ opacity: 0.85;
230
+ }
231
+ }
232
+
233
+ a.ix {
234
+ color: inherit;
235
+ text-decoration: underline dotted var(--platform-colors-text-disabled);
236
+ text-underline-offset: 3px;
237
+ }
238
+
239
+ sup.footnote-ref {
240
+ color: var(--platform-colors-accent);
241
+ cursor: pointer;
242
+ font-weight: 600;
243
+ padding: 0 1px;
244
+ }
245
+
246
+ mark {
247
+ background: rgb(from var(--platform-colors-warning) r g b / 35%);
248
+ border-radius: 2px;
249
+ color: inherit;
250
+ padding: 0.05em 0.25em;
251
+ }
252
+
253
+ span.comment-mark {
254
+ background: rgb(from var(--platform-colors-accent) r g b / 12%);
255
+ border-bottom: 2px solid
256
+ rgb(from var(--platform-colors-accent) r g b / 52%);
257
+ border-radius: 2px;
258
+ box-decoration-break: clone;
259
+ }
260
+
261
+ table {
262
+ border-collapse: collapse;
263
+ margin: 1em 0;
264
+ overflow: hidden;
265
+ table-layout: fixed;
266
+ width: 100%;
267
+
268
+ td,
269
+ th {
270
+ border: 1px solid var(--editor-paper-border);
271
+ box-sizing: border-box;
272
+ min-width: 1em;
273
+ padding: 8px 12px;
274
+ position: relative;
275
+ vertical-align: top;
276
+
277
+ > * {
278
+ margin-bottom: 0;
279
+ }
280
+ }
281
+
282
+ th {
283
+ background: var(--editor-paper-subtle);
284
+ font-weight: 600;
285
+ text-align: left;
286
+ }
287
+
288
+ .selectedCell::after {
289
+ background: rgb(from var(--platform-colors-accent) r g b / 15%);
290
+ content: '';
291
+ inset: 0;
292
+ pointer-events: none;
293
+ position: absolute;
294
+ z-index: 2;
295
+ }
296
+ }
297
+
298
+ hr {
299
+ border: none;
300
+ border-top: 1px solid rgb(from var(--editor-paper-border) r g b / 0.95);
301
+ margin: 1.7em 0;
302
+ }
303
+
304
+ .is-editor-empty::before,
305
+ .is-empty::before {
306
+ color: var(--editor-paper-muted);
307
+ content: attr(data-placeholder);
308
+ font-style: italic;
309
+ float: left;
310
+ height: 0;
311
+ pointer-events: none;
312
+ }
313
+ }
314
+
315
+ @media (max-width: 1560px) {
316
+ --comments-lane-width: 0px;
317
+ }
318
+
319
+ @media (max-width: 1200px) {
320
+ --outer-lane-padding: 24px;
321
+ }
322
+ `
323
+
324
+ // ---- DocumentEditor layout ----
325
+
326
+ export const DocumentEditorRoot = styled.div<{ $pageMode?: boolean }>`
327
+ display: flex;
328
+ flex-direction: column;
329
+ flex: 1;
330
+ min-height: 0;
331
+ min-width: 0;
332
+ background: ${({ $pageMode }) =>
333
+ $pageMode
334
+ ? 'var(--editor-content-bg, var(--platform-colors-surface))'
335
+ : 'var(--editor-content-bg, var(--platform-colors-bg))'};
336
+ overflow: visible;
337
+ position: relative;
338
+ `
339
+
340
+ export const DocumentEditorToolbarWrap = styled.div`
341
+ position: relative;
342
+ z-index: 40;
343
+ display: flex;
344
+ flex-wrap: nowrap;
345
+ align-items: center;
346
+ justify-content: flex-start;
347
+ gap: 0;
348
+ min-width: 0;
349
+ width: 100%;
350
+ overflow-x: auto;
351
+ overflow-y: visible;
352
+ background: var(
353
+ --editor-toolbar-bg,
354
+ var(--editor-paper-subtle, var(--platform-colors-surface-hover))
355
+ );
356
+ padding: 0;
357
+
358
+ > div {
359
+ padding: 4px 10px;
360
+ overflow: visible;
361
+ background: transparent;
362
+ }
363
+
364
+ > div:first-child {
365
+ flex: 0 0 auto;
366
+ margin: 0;
367
+ max-width: none;
368
+ min-width: 0;
369
+ width: auto;
370
+ }
371
+ `
372
+
373
+ export const DocumentEditorToolbarActions = styled.div`
374
+ display: flex;
375
+ flex: 0 0 auto;
376
+ flex-wrap: nowrap;
377
+ align-items: center;
378
+ justify-content: flex-start;
379
+ gap: 6px;
380
+ min-width: 0;
381
+ overflow: visible;
382
+ padding: 4px 10px;
383
+ `
384
+
385
+ /**
386
+ * Writer scroll lane + paper column (ps-book `StyledEditorContent` layout).
387
+ * Wraps plain `EditorContent` — do not `styled(EditorContent)` (TipTap v3 root is a fragment).
388
+ */
389
+ export const DocumentEditorViewport = styled.div<{ $pageMode?: boolean }>`
390
+ --vs-column-width: 800px;
391
+ --h1-font-size: 2.2em;
392
+ --page-pad-inline: 88px;
393
+ --comments-lane-width: 0px;
394
+ --outer-lane-padding: 72px;
395
+ --paper-width: calc(var(--vs-column-width) + (var(--page-pad-inline) * 2));
396
+ --editor-paper: var(--platform-colors-surface);
397
+ --editor-paper-text: var(--platform-colors-text);
398
+ --editor-paper-muted: var(--platform-colors-text-secondary);
399
+ --editor-paper-border: var(--platform-colors-border);
400
+ --editor-paper-subtle: var(--platform-colors-surface-hover);
401
+ flex: 1;
402
+ min-height: 0;
403
+ overflow: auto;
404
+ background: ${({ $pageMode }) =>
405
+ $pageMode ? 'var(--platform-colors-surface)' : 'var(--platform-colors-bg)'};
406
+ padding: ${({ $pageMode }) =>
407
+ $pageMode ? '0' : '0 var(--outer-lane-padding)'};
408
+ position: relative;
409
+ z-index: 1;
410
+ transition: padding-right 160ms ease;
411
+
412
+ &.document-editor-viewport--annotation-rail-open {
413
+ padding-right: calc(var(--outer-lane-padding) + 430px);
414
+ }
415
+
416
+ & > div {
417
+ min-height: 100%;
418
+ }
419
+
420
+ .tiptap,
421
+ .ProseMirror {
422
+ background: var(--editor-paper, var(--platform-colors-surface));
423
+ border-left: var(--vs-column-rule, none);
424
+ border-right: var(--vs-column-rule, none);
425
+ box-sizing: border-box;
426
+ caret-color: var(--platform-colors-accent);
427
+
428
+ outline: none;
429
+ color: var(--editor-paper-text);
430
+ }
431
+
432
+ .tiptap > *:first-child {
433
+ margin-top: 0;
434
+ }
435
+
436
+ .tiptap h1 {
437
+ margin-top: 1.22em;
438
+ color: var(--editor-paper-text);
439
+ font-size: var(--h1-font-size);
440
+ font-weight: 400;
441
+ letter-spacing: -0.03em;
442
+ line-height: 1.08;
443
+ }
444
+
445
+ .tiptap > h1:first-child,
446
+ .tiptap > h2:first-child,
447
+ .tiptap > h3:first-child {
448
+ margin-top: 0;
449
+ }
450
+
451
+ .tiptap h2 {
452
+ margin-top: 1em;
453
+ color: var(--editor-paper-text);
454
+ font-size: 1.55em;
455
+ font-weight: 400;
456
+ letter-spacing: -0.02em;
457
+ line-height: 1.22;
458
+ }
459
+
460
+ .tiptap h3 {
461
+ margin-top: 0.88em;
462
+ color: var(--editor-paper-muted);
463
+ font-size: 1.08em;
464
+ font-weight: 500;
465
+ letter-spacing: 0.08em;
466
+ line-height: 1.35;
467
+ text-transform: uppercase;
468
+ }
469
+
470
+ .tiptap p,
471
+ .tiptap ul,
472
+ .tiptap ol,
473
+ .tiptap blockquote,
474
+ .tiptap pre {
475
+ margin: 0;
476
+ }
477
+
478
+ .tiptap > * + * {
479
+ margin-top: 0.7em;
480
+ }
481
+
482
+ .tiptap h1 + *,
483
+ .tiptap h2 + * {
484
+ margin-top: 1.25em;
485
+ }
486
+
487
+ .tiptap p {
488
+ hanging-punctuation: first last;
489
+ }
490
+
491
+ .tiptap ul,
492
+ .tiptap ol {
493
+ padding-left: 1.9em;
494
+ }
495
+
496
+ .tiptap blockquote {
497
+ margin: 0.9em 0;
498
+ padding: 4px 0 4px 16px;
499
+ border-left: 1px solid rgb(from var(--editor-paper-text) r g b / 0.18);
500
+ color: var(--editor-paper-muted);
501
+ font-style: italic;
502
+ }
503
+
504
+ .tiptap table {
505
+ width: 100%;
506
+ margin: 1em 0;
507
+ border-collapse: collapse;
508
+ table-layout: fixed;
509
+ }
510
+
511
+ .tiptap th,
512
+ .tiptap td {
513
+ padding: 8px 12px;
514
+ border: 1px solid var(--editor-paper-border);
515
+ vertical-align: top;
516
+ }
517
+
518
+ .tiptap th {
519
+ background: var(--editor-paper-subtle);
520
+ font-weight: 600;
521
+ }
522
+
523
+ .tiptap img {
524
+ display: block;
525
+ max-width: 100%;
526
+ height: auto;
527
+ margin: 1em auto;
528
+ }
529
+
530
+ .tiptap figure {
531
+ margin: 1em 0;
532
+ }
533
+
534
+ .tiptap figcaption {
535
+ margin-top: 0.55em;
536
+ color: var(--editor-paper-muted);
537
+ font-size: 0.84em;
538
+ text-align: center;
539
+ }
540
+
541
+ .tiptap span.comment-mark {
542
+ background: transparent;
543
+ border-bottom: 1px solid
544
+ var(
545
+ --comment-mark-border,
546
+ rgb(from var(--platform-colors-accent) r g b / 46%)
547
+ );
548
+ border-radius: 2px;
549
+ box-decoration-break: clone;
550
+ cursor: pointer;
551
+ padding: 0 1px;
552
+ }
553
+
554
+ .tiptap span.comment-mark:hover,
555
+ .tiptap span.comment-mark:focus,
556
+ .tiptap span.comment-mark--active,
557
+ .tiptap span.comment-mark--spotlight {
558
+ background: linear-gradient(
559
+ to bottom,
560
+ transparent 56%,
561
+ var(
562
+ --comment-mark-bg,
563
+ rgb(from var(--platform-colors-accent) r g b / 14%)
564
+ )
565
+ 56%
566
+ );
567
+ }
568
+
569
+ .tiptap span.comment-mark--agent-review {
570
+ --comment-mark-bg: rgb(199 210 254 / 0.22);
571
+ --comment-mark-border: rgb(99 102 241 / 0.44);
572
+ }
573
+
574
+ .tiptap span.comment-mark--formatting {
575
+ --comment-mark-bg: rgb(221 214 254 / 0.22);
576
+ --comment-mark-border: rgb(124 58 237 / 0.44);
577
+ }
578
+
579
+ .tiptap span.comment-mark--copy-editing {
580
+ --comment-mark-bg: rgb(187 247 208 / 0.22);
581
+ --comment-mark-border: rgb(5 150 105 / 0.42);
582
+ }
583
+
584
+ .tiptap span.comment-mark--copyedit {
585
+ --comment-mark-bg: rgb(187 247 208 / 0.22);
586
+ --comment-mark-border: rgb(22 163 74 / 0.58);
587
+ }
588
+
589
+ .tiptap span.comment-mark--voice {
590
+ --comment-mark-bg: rgb(251 207 232 / 0.2);
591
+ --comment-mark-border: rgb(219 39 119 / 0.52);
592
+ }
593
+
594
+ .tiptap span.comment-mark--claim {
595
+ --comment-mark-bg: rgb(251 191 36 / 0.24);
596
+ --comment-mark-border: rgb(217 119 6 / 0.74);
597
+ background: linear-gradient(
598
+ to bottom,
599
+ transparent 58%,
600
+ rgb(251 191 36 / 0.18) 58%
601
+ );
602
+ border-bottom-width: 2px;
603
+ }
604
+
605
+ .tiptap span.comment-mark--fact-check {
606
+ --comment-mark-bg: rgb(254 202 202 / 0.28);
607
+ --comment-mark-border: rgb(220 38 38 / 0.78);
608
+ background: linear-gradient(
609
+ to bottom,
610
+ transparent 55%,
611
+ rgb(254 202 202 / 0.24) 55%
612
+ );
613
+ border-bottom-width: 2px;
614
+ }
615
+
616
+ .tiptap span.comment-mark--source {
617
+ --comment-mark-bg: rgb(191 219 254 / 0.22);
618
+ --comment-mark-border: rgb(37 99 235 / 0.58);
619
+ }
620
+
621
+ .tiptap span.comment-mark--thread {
622
+ --comment-mark-bg: rgb(221 214 254 / 0.22);
623
+ --comment-mark-border: rgb(124 58 237 / 0.5);
624
+ }
625
+
626
+ .tiptap span.comment-mark--conflict,
627
+ .tiptap span.comment-mark--logic {
628
+ --comment-mark-bg: rgb(226 232 240 / 0.34);
629
+ --comment-mark-border: rgb(71 85 105 / 0.52);
630
+ }
631
+
632
+ .tiptap span.comment-mark--question {
633
+ --comment-mark-bg: rgb(153 246 228 / 0.24);
634
+ --comment-mark-border: rgb(13 148 136 / 0.46);
635
+ }
636
+
637
+ .tiptap span.comment-mark--citation {
638
+ --comment-mark-bg: rgb(191 219 254 / 0.22);
639
+ --comment-mark-border: rgb(37 99 235 / 0.44);
640
+ }
641
+
642
+ .tiptap span.comment-mark--spotlight-muted {
643
+ background: transparent;
644
+ border-bottom-color: rgb(from var(--editor-paper-text) r g b / 9%);
645
+ color: rgb(from var(--editor-paper-text) r g b / 0.78);
646
+ transition: background 160ms ease, border-color 160ms ease, color 160ms ease;
647
+ }
648
+
649
+ .tiptap span.comment-mark--resolved {
650
+ background: transparent;
651
+ border-bottom-color: rgb(from var(--editor-paper-muted) r g b / 32%);
652
+ border-bottom-width: 1px;
653
+ color: rgb(from var(--editor-paper-text) r g b / 0.72);
654
+ }
655
+
656
+ .tiptap .is-empty::before {
657
+ float: left;
658
+ height: 0;
659
+ color: var(--editor-paper-muted);
660
+ content: attr(data-placeholder);
661
+ pointer-events: none;
662
+ }
663
+
664
+ @media (max-width: 1200px) {
665
+ --outer-lane-padding: 24px;
666
+ }
667
+ `
668
+
669
+ export const EditorWrapper = styled.div<{ $pageMode?: boolean }>`
670
+ background: var(--editor-paper, var(--platform-colors-surface));
671
+ border-left: var(--vs-column-rule, none);
672
+ border-right: var(--vs-column-rule, none);
673
+ box-sizing: border-box;
674
+ caret-color: var(--platform-colors-accent);
675
+ flex-shrink: 0;
676
+ min-height: calc(100vh - 88px);
677
+ min-width: var(--paper-width);
678
+ width: var(--paper-width);
679
+ max-width: none;
680
+ margin: 0 auto;
681
+ padding: 56px var(--page-pad-inline) 120px;
682
+ color: var(--editor-paper-text, var(--platform-colors-text));
683
+ font-family: var(--vs-body-font, var(--platform-typography-font-family));
684
+ font-size: var(--vs-font-size, 17px);
685
+ line-height: var(--vs-line-height, 1.7);
686
+ outline: none;
687
+
688
+ ${({ $pageMode }) =>
689
+ $pageMode
690
+ ? `
691
+ min-width: min(100%, var(--paper-width));
692
+ width: min(100%, var(--paper-width));
693
+ min-height: 100vh;
694
+ padding-top: clamp(56px, 8vh, 104px);
695
+ `
696
+ : ''}
697
+ `