@open-press/cli 0.7.0 → 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.
Files changed (132) hide show
  1. package/README.md +6 -1
  2. package/package.json +1 -1
  3. package/template/core/AGENTS.md +126 -0
  4. package/template/core/CHANGELOG.md +65 -0
  5. package/template/core/engine/commands/dev.mjs +2 -2
  6. package/template/core/engine/commands/upgrade.mjs +47 -5
  7. package/template/core/engine/output/chrome-pdf.mjs +18 -3
  8. package/template/core/engine/output/static-server.mjs +39 -0
  9. package/template/core/engine/react/comment-endpoint.mjs +13 -39
  10. package/template/core/engine/react/comment-marker.mjs +30 -6
  11. package/template/core/engine/react/document-entry.mjs +11 -0
  12. package/template/core/engine/react/document-export.mjs +45 -5
  13. package/template/core/engine/react/http-json.mjs +24 -0
  14. package/template/core/engine/react/mdx-compile.mjs +187 -3
  15. package/template/core/engine/react/measurement-css.mjs +93 -1
  16. package/template/core/engine/react/object-entities.mjs +119 -0
  17. package/template/core/engine/react/pipeline/allocate.mjs +10 -7
  18. package/template/core/engine/react/pipeline/frame-measurement.mjs +40 -9
  19. package/template/core/engine/react/project-asset-endpoint.mjs +6 -24
  20. package/template/core/engine/react/source-edit-endpoint.d.mts +10 -0
  21. package/template/core/engine/react/source-edit-endpoint.mjs +75 -0
  22. package/template/core/engine/react/sources/mdx-resolver.mjs +12 -14
  23. package/template/core/engine/react/style-discovery.mjs +1 -4
  24. package/template/core/engine/runtime/file-walk.mjs +22 -0
  25. package/template/core/engine/runtime/inspection.mjs +1 -20
  26. package/template/core/engine/runtime/path-utils.mjs +20 -0
  27. package/template/core/engine/runtime/source-text-tools.d.mts +102 -0
  28. package/template/core/engine/runtime/source-text-tools.mjs +551 -16
  29. package/template/core/engine/runtime/source-workspace.mjs +4 -31
  30. package/template/core/package.json +1 -1
  31. package/template/core/src/main.tsx +2 -2
  32. package/template/core/src/openpress/{App.tsx → app/OpenPressApp.tsx} +25 -12
  33. package/template/core/src/openpress/{renderer.tsx → app/OpenPressRuntime.tsx} +10 -7
  34. package/template/core/src/openpress/app/index.ts +2 -0
  35. package/template/core/src/openpress/core/Frame.tsx +9 -11
  36. package/template/core/src/openpress/core/FrameContext.tsx +8 -3
  37. package/template/core/src/openpress/core/MdxArea.tsx +11 -12
  38. package/template/core/src/openpress/core/cn.ts +4 -0
  39. package/template/core/src/openpress/core/index.tsx +2 -1
  40. package/template/core/src/openpress/core/primitives.tsx +29 -8
  41. package/template/core/src/openpress/core/types.ts +8 -0
  42. package/template/core/src/openpress/{anchorMap.ts → document-model/anchorMapModel.ts} +1 -1
  43. package/template/core/src/openpress/{indexes.ts → document-model/documentIndexes.ts} +1 -1
  44. package/template/core/src/openpress/{types.ts → document-model/documentTypes.ts} +42 -0
  45. package/template/core/src/openpress/document-model/index.ts +6 -0
  46. package/template/core/src/openpress/document-model/objectEntityModel.ts +51 -0
  47. package/template/core/src/openpress/{projectIdentity.ts → document-model/projectIdentityModel.ts} +1 -1
  48. package/template/core/src/openpress/{reactDocumentMetadata.ts → document-model/reactDocumentMetadataModel.ts} +1 -1
  49. package/template/core/src/openpress/manuscript/index.tsx +49 -7
  50. package/template/core/src/openpress/{publicPage.tsx → reader/PublicReaderPage.tsx} +31 -51
  51. package/template/core/src/openpress/{workbenchPanels.tsx → reader/ReaderNavigationPanel.tsx} +6 -5
  52. package/template/core/src/openpress/reader/index.ts +10 -0
  53. package/template/core/src/openpress/reader/pageViewportScaleModel.ts +73 -0
  54. package/template/core/src/openpress/reader/readerTypes.ts +4 -0
  55. package/template/core/src/openpress/reader/usePageViewportScale.ts +119 -0
  56. package/template/core/src/openpress/reader/usePanelState.ts +56 -0
  57. package/template/core/src/openpress/reader/useReaderHashSync.ts +61 -0
  58. package/template/core/src/openpress/reader/useReaderKeyboardNav.ts +48 -0
  59. package/template/core/src/openpress/reader/useReaderRuntime.ts +146 -0
  60. package/template/core/src/openpress/reader/useReaderScrollAnchor.ts +64 -0
  61. package/template/core/src/openpress/shared/Panel.tsx +77 -0
  62. package/template/core/src/openpress/shared/index.ts +4 -0
  63. package/template/core/src/openpress/shared/numberUtils.ts +3 -0
  64. package/template/core/src/openpress/{runtimeMode.ts → shared/runtimeMode.ts} +0 -11
  65. package/template/core/src/openpress/workbench/Workbench.tsx +407 -0
  66. package/template/core/src/openpress/workbench/actions/DeploymentControl.tsx +157 -0
  67. package/template/core/src/openpress/workbench/actions/PageZoomControl.tsx +182 -0
  68. package/template/core/src/openpress/workbench/actions/SearchControl.tsx +345 -0
  69. package/template/core/src/openpress/workbench/actions/deploymentStatusModel.ts +112 -0
  70. package/template/core/src/openpress/workbench/actions/index.ts +5 -0
  71. package/template/core/src/openpress/workbench/actions/useDeploymentWorkbench.ts +136 -0
  72. package/template/core/src/openpress/workbench/dialog/WorkbenchDialog.tsx +72 -0
  73. package/template/core/src/openpress/workbench/dialog/index.ts +1 -0
  74. package/template/core/src/openpress/workbench/document/components/DocumentPanel.tsx +127 -0
  75. package/template/core/src/openpress/workbench/document/components/InlineSourceEditorLayer.tsx +207 -0
  76. package/template/core/src/openpress/workbench/document/components/ReaderStage.tsx +9 -0
  77. package/template/core/src/openpress/workbench/document/hooks/useDocumentWorkbenchModel.ts +34 -0
  78. package/template/core/src/openpress/workbench/document/hooks/useInlineDocumentEditor.ts +525 -0
  79. package/template/core/src/openpress/workbench/document/index.ts +10 -0
  80. package/template/core/src/openpress/workbench/index.ts +2 -0
  81. package/template/core/src/openpress/workbench/inspector/InlineInspectorLayer.tsx +459 -0
  82. package/template/core/src/openpress/workbench/inspector/index.ts +5 -0
  83. package/template/core/src/openpress/workbench/inspector/inlineCommentModel.ts +125 -0
  84. package/template/core/src/openpress/workbench/inspector/inspectorGeometryModel.ts +160 -0
  85. package/template/core/src/openpress/workbench/inspector/inspectorModel.ts +408 -0
  86. package/template/core/src/openpress/workbench/inspector/useInspectorComments.ts +248 -0
  87. package/template/core/src/openpress/workbench/mentions/MentionSuggestionList.tsx +41 -0
  88. package/template/core/src/openpress/workbench/mentions/index.ts +2 -0
  89. package/template/core/src/openpress/{composerMentions.ts → workbench/mentions/useComposerMentions.ts} +1 -4
  90. package/template/core/src/openpress/workbench/panels/Panel.tsx +1 -0
  91. package/template/core/src/openpress/workbench/panels/PendingCommentsPanel.tsx +76 -0
  92. package/template/core/src/openpress/workbench/panels/WorkbenchControlPanel.tsx +29 -0
  93. package/template/core/src/openpress/workbench/panels/index.ts +3 -0
  94. package/template/core/src/openpress/workbench/project/ProjectEntryPanel.tsx +523 -0
  95. package/template/core/src/openpress/workbench/project/ProjectPreviewDialog.tsx +35 -0
  96. package/template/core/src/openpress/workbench/project/index.ts +2 -0
  97. package/template/core/src/openpress/workbench/project/projectPreviewTypes.ts +11 -0
  98. package/template/core/src/openpress/workbench/shell/WorkbenchShell.tsx +167 -0
  99. package/template/core/src/openpress/workbench/shell/index.ts +1 -0
  100. package/template/core/src/openpress/workbench/workbenchFormatters.ts +120 -0
  101. package/template/core/src/openpress/workbench/workbenchTypes.ts +35 -0
  102. package/template/core/src/styles/openpress/print-route.css +0 -2
  103. package/template/core/src/styles/openpress/{project-workspace.css → project-preview-panel.css} +13 -407
  104. package/template/core/src/styles/openpress/public-viewer.css +25 -320
  105. package/template/core/src/styles/openpress/reader-runtime.css +243 -55
  106. package/template/core/src/styles/openpress/responsive.css +145 -270
  107. package/template/core/src/styles/openpress/workbench-panels.css +214 -178
  108. package/template/core/src/styles/openpress/workbench.css +986 -451
  109. package/template/core/src/styles/openpress.css +1 -1
  110. package/template/core/vite.config.ts +50 -0
  111. package/template/packs/academic-paper/document/chapters/01-introduction/content/01-introduction.mdx +26 -12
  112. package/template/packs/academic-paper/document/chapters/02-methods/content/01-methods.mdx +37 -17
  113. package/template/packs/academic-paper/document/chapters/03-results-and-discussion/content/01-results.mdx +34 -16
  114. package/template/packs/academic-paper/document/chapters/04-acknowledgment/content/01-acknowledgment.mdx +22 -8
  115. package/template/packs/academic-paper/document/chapters/05-references/content/01-references.mdx +20 -15
  116. package/template/packs/academic-paper/document/components/Page.tsx +26 -3
  117. package/template/packs/academic-paper/document/index.tsx +51 -59
  118. package/template/packs/academic-paper/document/media/figure-placeholder.svg +9 -0
  119. package/template/packs/academic-paper/document/theme/base/page-contract.css +30 -13
  120. package/template/packs/academic-paper/document/theme/base/typography.css +30 -33
  121. package/template/packs/academic-paper/document/theme/page-surfaces/cover.css +74 -47
  122. package/template/core/src/openpress/inspector.ts +0 -282
  123. package/template/core/src/openpress/projectWorkspace.tsx +0 -919
  124. package/template/core/src/openpress/readerRuntime.ts +0 -230
  125. package/template/core/src/openpress/workbench.tsx +0 -1265
  126. package/template/core/src/openpress/workbenchTypes.ts +0 -4
  127. /package/template/core/src/openpress/{readerPageRegistry.ts → reader/readerPageRegistry.ts} +0 -0
  128. /package/template/core/src/openpress/{pageRoute.ts → reader/readerPageRoute.ts} +0 -0
  129. /package/template/core/src/openpress/{readerScroll.ts → reader/readerScroll.ts} +0 -0
  130. /package/template/core/src/openpress/{readerState.ts → reader/readerStateModel.ts} +0 -0
  131. /package/template/core/src/openpress/{frameScheduler.ts → shared/frameScheduler.ts} +0 -0
  132. /package/template/core/src/openpress/{projectSources.ts → workbench/project/projectSourceModel.ts} +0 -0
@@ -383,12 +383,12 @@ button:focus-visible {
383
383
  position: relative;
384
384
  width: var(--reader-page-width, var(--openpress-page-width));
385
385
  height: calc(var(--reader-page-width, var(--openpress-page-width)) * var(--openpress-page-height) / var(--openpress-page-width));
386
- --page-margin-x: clamp(28px, 5cqw, 48px);
387
- --page-margin-top: clamp(28px, 5cqw, 48px);
388
- --page-margin-bottom: clamp(18px, 3cqw, 28px);
389
- --page-header-height: clamp(12px, 1.9cqw, 20px);
390
- --page-footer-height: clamp(18px, 2.6cqw, 26px);
391
- --page-frame-gap: clamp(8px, 1.4cqw, 14px);
386
+ --page-margin-x: clamp(20px, 3.8cqw, 34px);
387
+ --page-margin-top: clamp(18px, 3.5cqw, 30px);
388
+ --page-margin-bottom: clamp(14px, 2.4cqw, 24px);
389
+ --page-header-height: clamp(10px, 1.6cqw, 16px);
390
+ --page-footer-height: clamp(16px, 2.2cqw, 22px);
391
+ --page-frame-gap: clamp(5px, 1.1cqw, 11px);
392
392
  overflow: hidden;
393
393
  color: var(--openpress-color-ink);
394
394
  background: var(--openpress-color-document);
@@ -429,12 +429,29 @@ button:focus-visible {
429
429
 
430
430
  .page-header {
431
431
  display: flex;
432
- align-items: flex-start;
432
+ align-items: baseline;
433
+ justify-content: space-between;
434
+ gap: var(--openpress-space-3);
433
435
  color: var(--openpress-color-muted);
434
- font-size: clamp(7pt, 1.2cqw, 8pt);
435
- letter-spacing: 0.1em;
436
- opacity: 0.62;
436
+ font-family: var(--openpress-font-body);
437
+ font-size: clamp(8px, 1.2cqw, 9px);
438
+ letter-spacing: normal;
439
+ opacity: 0.82;
437
440
  pointer-events: none;
441
+ line-height: 1.2;
442
+ }
443
+
444
+ .running-head-left,
445
+ .running-head-right {
446
+ min-width: 0;
447
+ overflow: hidden;
448
+ text-overflow: ellipsis;
449
+ white-space: nowrap;
450
+ }
451
+
452
+ .running-head-right {
453
+ max-width: 45%;
454
+ text-align: right;
438
455
  }
439
456
 
440
457
  .page-body {
@@ -448,9 +465,9 @@ button:focus-visible {
448
465
  justify-content: space-between;
449
466
  align-items: baseline;
450
467
  gap: 12px;
451
- font-size: clamp(7pt, 1.25cqw, 8pt);
468
+ font-size: clamp(8px, 1.2cqw, 9px);
452
469
  color: var(--openpress-color-muted);
453
- letter-spacing: 0.1em;
470
+ letter-spacing: normal;
454
471
  opacity: 0.7;
455
472
  pointer-events: none;
456
473
  }
@@ -465,7 +482,7 @@ button:focus-visible {
465
482
 
466
483
  .page-footer .footer-right {
467
484
  font-variant-numeric: tabular-nums;
468
- letter-spacing: 0.14em;
485
+ letter-spacing: 0.02em;
469
486
  flex-shrink: 0;
470
487
  }
471
488
 
@@ -14,50 +14,45 @@ h3,
14
14
  h4 {
15
15
  break-after: avoid;
16
16
  color: var(--openpress-color-ink);
17
- letter-spacing: 0.04em;
17
+ letter-spacing: normal;
18
18
  }
19
19
 
20
20
  h2 {
21
- margin: 0 0 var(--openpress-space-4);
21
+ margin: 0 0 var(--openpress-space-3);
22
22
  padding: 0;
23
23
  font-family: var(--openpress-font-serif);
24
- font-size: clamp(5.7pt, 3.4cqw, 17pt);
24
+ font-size: clamp(15px, 2.4cqw, 20px);
25
25
  line-height: 1.45;
26
- font-weight: 300;
26
+ font-weight: 700;
27
27
  border: 0;
28
28
  }
29
29
 
30
30
  h2::before {
31
- content: "";
32
- display: block;
33
- width: 36px;
34
- height: 1px;
35
- background: var(--openpress-color-ink);
36
- margin-bottom: var(--openpress-space-4);
31
+ content: none;
37
32
  }
38
33
 
39
- .reader-page--content .page-body > h2:first-child {
34
+ .reader-page--content .page-body .openpress-mdx-area h2 {
40
35
  display: flex;
41
36
  align-items: baseline;
42
37
  gap: 0.55em;
43
38
  margin: 0 0 var(--openpress-space-4);
44
39
  }
45
40
 
46
- .reader-page--content .page-body > h2:first-child::before {
41
+ .reader-page--content .page-body .openpress-mdx-area h2::before {
47
42
  content: attr(data-chapter);
48
43
  display: inline-block;
49
44
  flex-shrink: 0;
50
45
  font-family: var(--openpress-font-serif);
51
- font-size: 1.55em;
52
- font-weight: 300;
53
- color: var(--openpress-color-muted);
46
+ font-size: 0.88em;
47
+ font-weight: 700;
48
+ color: var(--openpress-color-ink);
54
49
  line-height: 1;
55
- letter-spacing: 0.06em;
50
+ letter-spacing: normal;
56
51
  width: auto;
57
52
  min-width: 0;
58
53
  height: auto;
59
54
  background: transparent;
60
- padding: 0;
55
+ padding-right: 0.22em;
61
56
  margin: 0;
62
57
  border: 0;
63
58
  }
@@ -72,26 +67,26 @@ h2::before {
72
67
  content: attr(data-section);
73
68
  flex-shrink: 0;
74
69
  font-family: var(--openpress-font-serif);
75
- font-size: 0.95em;
76
- font-weight: 300;
77
- color: var(--openpress-color-muted);
78
- letter-spacing: 0.08em;
70
+ font-size: 0.98em;
71
+ font-weight: 500;
72
+ color: var(--openpress-color-ink);
73
+ letter-spacing: normal;
79
74
  line-height: inherit;
80
75
  }
81
76
 
82
77
  h2 + p,
83
78
  h2 + h3 {
84
- margin-top: var(--openpress-space-3);
79
+ margin-top: var(--openpress-space-2);
85
80
  }
86
81
 
87
82
  h3 {
88
83
  margin: var(--openpress-space-3) 0 var(--openpress-space-2);
89
84
  font-family: var(--openpress-font-serif);
90
- font-size: clamp(4.8pt, 2.4cqw, 13pt);
91
- line-height: 1.55;
92
- font-weight: 400;
85
+ font-size: clamp(12px, 2cqw, 16px);
86
+ line-height: 1.5;
87
+ font-weight: 500;
93
88
  color: var(--openpress-color-ink);
94
- letter-spacing: 0.03em;
89
+ letter-spacing: normal;
95
90
  }
96
91
 
97
92
  .reader-page--content .page-body > h3:first-child {
@@ -100,17 +95,19 @@ h3 {
100
95
 
101
96
  h4 {
102
97
  margin: var(--openpress-space-3) 0 var(--openpress-space-1);
103
- font-family: var(--openpress-font-body);
104
- font-size: clamp(4.4pt, 1.9cqw, 11pt);
98
+ font-family: var(--openpress-font-serif);
99
+ font-size: clamp(10px, 1.75cqw, 14px);
105
100
  font-weight: 500;
106
101
  color: var(--openpress-color-muted);
107
- letter-spacing: 0.04em;
102
+ letter-spacing: 0.01em;
108
103
  }
109
104
 
110
105
  p {
111
106
  margin: 0 0 var(--openpress-space-2);
112
- font-size: clamp(4.2pt, 1.85cqw, 10.5pt);
113
- line-height: 1.85;
107
+ font-size: clamp(10px, 1.8cqw, 11px);
108
+ line-height: 1.65;
109
+ text-align: justify;
110
+ text-justify: inter-word;
114
111
  }
115
112
 
116
113
  .reader-page--content p,
@@ -206,8 +203,8 @@ ol,
206
203
  ul {
207
204
  margin: 0 0 var(--openpress-space-3);
208
205
  padding-left: 7mm;
209
- font-size: clamp(4.2pt, 1.85cqw, 10.5pt);
210
- line-height: 1.85;
206
+ font-size: clamp(10px, 1.75cqw, 11px);
207
+ line-height: 1.65;
211
208
  }
212
209
 
213
210
  ol {
@@ -146,99 +146,126 @@
146
146
  color: var(--openpress-color-ink);
147
147
  }
148
148
 
149
- /* ─ academic-paper title block ──────────────────────────────────────────── */
150
- /* IEEE-style title page: large serif title, optional subtitle, 3-column */
151
- /* author grid, abstract band, index terms band. Single-column body picks */
152
- /* up after this surface. Two-column body arrives with the v0.8 paged.js */
153
- /* migration. */
149
+ /* ─ academic-paper title block for the journal-style template ───────────── */
154
150
 
155
151
  .paper-cover {
156
152
  display: flex;
157
153
  flex-direction: column;
158
- gap: clamp(18px, 2.4cqw, 32px);
159
- padding-block: clamp(24px, 3cqw, 40px) 0;
154
+ gap: clamp(11px, 2.2cqw, 20px);
155
+ padding-block: clamp(18px, 2.5cqw, 30px) 0;
160
156
  font-family: var(--openpress-font-serif);
161
157
  color: var(--openpress-color-ink);
162
158
  }
163
159
 
160
+ .paper-cover-lead,
161
+ .paper-cover-short-title {
162
+ margin: 0;
163
+ text-align: center;
164
+ color: var(--openpress-color-muted);
165
+ font-family: var(--openpress-font-body);
166
+ font-size: clamp(8.5pt, 1.55cqw, 9.5pt);
167
+ letter-spacing: 0.01em;
168
+ line-height: 1.3;
169
+ }
170
+
171
+ .paper-cover-date {
172
+ margin: 0;
173
+ text-align: center;
174
+ color: var(--openpress-color-muted);
175
+ font-family: var(--openpress-font-body);
176
+ font-size: clamp(7.8pt, 1.3cqw, 8.7pt);
177
+ letter-spacing: 0;
178
+ line-height: 1.25;
179
+ }
180
+
181
+ .paper-cover-lead {
182
+ margin-top: 2mm;
183
+ }
184
+
185
+ .paper-cover-short-title {
186
+ margin-bottom: clamp(6px, 1.2cqw, 10px);
187
+ }
188
+
164
189
  .paper-title {
165
190
  font-family: var(--openpress-font-serif);
166
- font-size: clamp(28px, 4.4cqw, 36px);
191
+ font-size: clamp(32px, 4.9cqw, 45px);
167
192
  line-height: 1.2;
168
193
  font-weight: 400;
169
194
  text-align: center;
170
195
  margin: 0;
171
- letter-spacing: 0;
196
+ letter-spacing: -0.01em;
172
197
  }
173
198
 
174
- .paper-subtitle {
175
- font-size: clamp(11px, 1.4cqw, 12px);
176
- font-style: italic;
199
+ .paper-author-contact-note {
177
200
  text-align: center;
201
+ margin: 0;
202
+ font-size: clamp(8.5pt, 1.45cqw, 9.5pt);
178
203
  color: var(--openpress-color-muted);
204
+ line-height: 1.35;
205
+ }
206
+
207
+ .paper-author-line {
179
208
  margin: 0;
209
+ text-align: center;
210
+ font-size: clamp(11px, 2cqw, 13px);
211
+ line-height: 1.45;
212
+ font-weight: 500;
180
213
  }
181
214
 
182
- .paper-authors {
215
+ .paper-author-affiliations {
216
+ margin: 0;
183
217
  list-style: none;
184
218
  padding: 0;
185
- margin: clamp(8px, 1.6cqw, 16px) 0 0;
186
- display: grid;
187
- grid-template-columns: repeat(3, 1fr);
188
- gap: clamp(10px, 1.4cqw, 16px) clamp(8px, 1.2cqw, 14px);
189
- }
190
-
191
- .paper-author {
192
219
  display: flex;
193
220
  flex-direction: column;
194
- align-items: center;
221
+ gap: 1mm;
195
222
  text-align: center;
196
- font-size: clamp(10.5px, 1.3cqw, 11.5px);
197
- line-height: 1.35;
198
- gap: 0;
199
223
  }
200
224
 
201
- .paper-author-name {
202
- font-weight: 500;
203
- margin: 0 0 0.25em 0;
225
+ .paper-author {
226
+ display: flex;
227
+ flex-direction: column;
204
228
  }
205
229
 
206
230
  .paper-author-affiliation {
207
- font-style: italic;
208
- margin: 0;
209
- color: var(--openpress-color-ink);
210
- }
211
-
212
- .paper-author-location,
213
- .paper-author-contact {
214
231
  margin: 0;
215
232
  color: var(--openpress-color-ink);
233
+ font-family: var(--openpress-font-body);
234
+ font-size: clamp(9px, 1.6cqw, 10px);
235
+ line-height: 1.42;
216
236
  }
217
237
 
218
238
  .paper-abstract,
219
- .paper-index-terms {
239
+ .paper-nontechnical-summary,
240
+ .paper-contributions,
241
+ .paper-abstract > p,
242
+ .paper-nontechnical-summary > p,
243
+ .paper-contributions > p {
220
244
  margin-top: clamp(8px, 1.6cqw, 14px);
221
- font-size: clamp(10.5px, 1.3cqw, 11.5px);
245
+ font-family: var(--openpress-font-body);
246
+ font-size: clamp(10px, 1.7cqw, 11px);
222
247
  line-height: 1.45;
248
+ text-align: justify;
249
+ text-justify: inter-word;
223
250
  }
224
251
 
225
- .paper-abstract p,
226
- .paper-index-terms p {
252
+ .paper-abstract > p,
253
+ .paper-nontechnical-summary > p,
254
+ .paper-contributions > p {
227
255
  margin: 0;
228
- text-indent: 1.2em;
229
- font-style: italic;
256
+ text-indent: 0.9em;
230
257
  }
231
258
 
232
259
  .paper-abstract-label {
233
- font-style: normal;
234
- font-weight: 700;
260
+ display: inline-block;
261
+ font-weight: 500;
262
+ text-transform: none;
235
263
  }
236
264
 
237
- /* Single-column flush body (v0.6). The v0.8 paged.js migration replaces */
238
- /* this declaration with `column-count: 2; column-gap: 1rem;` and adds */
239
- /* `column-span: all` for the title block + abstract above. */
240
- .reader-page--content .page-body {
241
- max-width: none;
265
+ .paper-section-label {
266
+ font-family: var(--openpress-font-serif);
267
+ font-size: 0.95em;
268
+ font-weight: 500;
242
269
  }
243
270
 
244
271
  /* ─ academic-paper back cover ───────────────────────────────────────────── */
@@ -1,282 +0,0 @@
1
- import { useCallback, useEffect, useMemo, useState, type MouseEvent as ReactMouseEvent } from "react";
2
- import { getSourceBlockMap } from "./reactDocumentMetadata";
3
- import type { ReaderDocument, SourceBlock } from "./types";
4
-
5
- const DEFAULT_INSPECTOR_STORAGE_KEY = "openpress:inspector-mode";
6
-
7
- export type InspectorIntent = "edit" | "delete" | "add";
8
- export type InspectorPlacement = "block" | "before";
9
-
10
- export interface InspectorTarget {
11
- blockId: string;
12
- placement: InspectorPlacement;
13
- }
14
-
15
- export interface InspectorState {
16
- enabled: boolean;
17
- inspectorMode: boolean;
18
- selectedBlockId: string | null;
19
- selectedBlock: SourceBlock | null;
20
- selectedTarget: InspectorTarget | null;
21
- commentIntent: InspectorIntent;
22
- setInspectorMode: (enabled: boolean) => void;
23
- toggleInspectorMode: () => void;
24
- setCommentIntent: (intent: InspectorIntent) => void;
25
- selectTarget: (target: InspectorTarget | null) => SourceBlock | null;
26
- inspectTarget: (target: EventTarget | null) => SourceBlock | null;
27
- handleClick: (event: ReactMouseEvent) => boolean;
28
- }
29
-
30
- export interface InspectorCommentResult {
31
- ok: boolean;
32
- comment?: {
33
- id?: string;
34
- timestamp?: string;
35
- path?: string;
36
- line?: number;
37
- note?: string;
38
- hint?: string;
39
- };
40
- message?: string;
41
- }
42
-
43
- export interface PendingComment {
44
- id: string;
45
- timestamp?: string;
46
- path: string;
47
- absolutePath?: string;
48
- line: number;
49
- marker?: string;
50
- note: string;
51
- hint?: string;
52
- }
53
-
54
- export interface CommentListResult {
55
- ok: boolean;
56
- comments?: PendingComment[];
57
- message?: string;
58
- }
59
-
60
- export interface CommentClearResult {
61
- ok: boolean;
62
- removedCount: number;
63
- comments?: PendingComment[];
64
- message?: string;
65
- }
66
-
67
- export async function submitInspectorComment({
68
- block,
69
- note,
70
- intent,
71
- placement,
72
- endpoint = "/__openpress/comment",
73
- fetchImpl = globalThis.fetch?.bind(globalThis),
74
- }: {
75
- block: SourceBlock | null;
76
- note: string;
77
- intent?: InspectorIntent;
78
- placement?: InspectorPlacement;
79
- endpoint?: string;
80
- fetchImpl?: typeof fetch;
81
- }): Promise<InspectorCommentResult> {
82
- if (!block) throw new Error("OpenPress inspector comment requires a selected block.");
83
- const normalizedNote = note.trim();
84
- if (!normalizedNote) throw new Error("OpenPress inspector comment note must not be empty.");
85
- if (!block.path || !block.source?.line) throw new Error("OpenPress inspector selected block has no editable source location.");
86
- if (typeof fetchImpl !== "function") throw new Error("OpenPress inspector comment endpoint is unavailable.");
87
-
88
- const response = await fetchImpl(endpoint, {
89
- method: "POST",
90
- headers: { "Content-Type": "application/json" },
91
- body: JSON.stringify({
92
- target: {
93
- blockId: block.id,
94
- path: block.path,
95
- source: block.source,
96
- },
97
- note: normalizedNote,
98
- hint: formatInspectorHint({ intent, placement }),
99
- }),
100
- });
101
- const result = await response.json().catch(() => null) as InspectorCommentResult | null;
102
- if (!response.ok) {
103
- throw new Error(result?.message ?? `OpenPress inspector comment failed with status ${response.status}`);
104
- }
105
- return result ?? { ok: true };
106
- }
107
-
108
- export async function fetchInspectorComments({
109
- endpoint = "/__openpress/comment",
110
- fetchImpl = globalThis.fetch?.bind(globalThis),
111
- }: {
112
- endpoint?: string;
113
- fetchImpl?: typeof fetch;
114
- } = {}): Promise<PendingComment[]> {
115
- if (typeof fetchImpl !== "function") throw new Error("OpenPress inspector comment endpoint is unavailable.");
116
-
117
- const response = await fetchImpl(endpoint, { method: "GET" });
118
- const result = await response.json().catch(() => null) as CommentListResult | null;
119
- if (!response.ok) {
120
- throw new Error(result?.message ?? `OpenPress inspector comment list failed with status ${response.status}`);
121
- }
122
- return Array.isArray(result?.comments) ? result.comments : [];
123
- }
124
-
125
- export async function clearInspectorComment({
126
- id,
127
- all = false,
128
- endpoint = "/__openpress/comment",
129
- fetchImpl = globalThis.fetch?.bind(globalThis),
130
- }: {
131
- id?: string;
132
- all?: boolean;
133
- endpoint?: string;
134
- fetchImpl?: typeof fetch;
135
- } = {}): Promise<CommentClearResult> {
136
- if (typeof fetchImpl !== "function") throw new Error("OpenPress inspector comment endpoint is unavailable.");
137
- if (!all && !id) throw new Error("OpenPress inspector comment clear requires an id or all=true.");
138
-
139
- const response = await fetchImpl(endpoint, {
140
- method: "DELETE",
141
- headers: { "Content-Type": "application/json" },
142
- body: JSON.stringify(all ? { all: true } : { id }),
143
- });
144
- const result = await response.json().catch(() => null) as CommentClearResult | null;
145
- if (!response.ok) {
146
- throw new Error(result?.message ?? `OpenPress inspector comment clear failed with status ${response.status}`);
147
- }
148
- return result ?? { ok: true, removedCount: 0 };
149
- }
150
-
151
- export async function updateInspectorComment({
152
- id,
153
- note,
154
- intent,
155
- placement,
156
- endpoint = "/__openpress/comment",
157
- fetchImpl = globalThis.fetch?.bind(globalThis),
158
- }: {
159
- id: string;
160
- note: string;
161
- intent?: InspectorIntent;
162
- placement?: InspectorPlacement;
163
- endpoint?: string;
164
- fetchImpl?: typeof fetch;
165
- }): Promise<InspectorCommentResult> {
166
- const normalizedNote = note.trim();
167
- if (!id.trim()) throw new Error("OpenPress inspector comment update requires an id.");
168
- if (!normalizedNote) throw new Error("OpenPress inspector comment note must not be empty.");
169
- if (typeof fetchImpl !== "function") throw new Error("OpenPress inspector comment endpoint is unavailable.");
170
-
171
- const response = await fetchImpl(endpoint, {
172
- method: "PATCH",
173
- headers: { "Content-Type": "application/json" },
174
- body: JSON.stringify({
175
- id,
176
- note: normalizedNote,
177
- hint: formatInspectorHint({ intent, placement }),
178
- }),
179
- });
180
- const result = await response.json().catch(() => null) as InspectorCommentResult | null;
181
- if (!response.ok) {
182
- throw new Error(result?.message ?? `OpenPress inspector comment update failed with status ${response.status}`);
183
- }
184
- return result ?? { ok: true };
185
- }
186
-
187
- export function useInspector(
188
- document: ReaderDocument,
189
- {
190
- enabled = false,
191
- storageKey = DEFAULT_INSPECTOR_STORAGE_KEY,
192
- }: {
193
- enabled?: boolean;
194
- storageKey?: string;
195
- } = {},
196
- ): InspectorState {
197
- const blockMap = useMemo(() => getSourceBlockMap(document), [document]);
198
- const [inspectorMode, setInspectorModeState] = useState(() => {
199
- if (!enabled || typeof window === "undefined") return false;
200
- return window.localStorage.getItem(storageKey) === "on";
201
- });
202
- const [selectedTarget, setSelectedTarget] = useState<InspectorTarget | null>(null);
203
- const [commentIntent, setCommentIntent] = useState<InspectorIntent>("edit");
204
-
205
- useEffect(() => {
206
- if (!enabled && inspectorMode) setInspectorModeState(false);
207
- }, [enabled, inspectorMode]);
208
-
209
- const setInspectorMode = useCallback((nextEnabled: boolean) => {
210
- setInspectorModeState(nextEnabled);
211
- if (typeof window !== "undefined") {
212
- window.localStorage.setItem(storageKey, nextEnabled ? "on" : "off");
213
- }
214
- if (!nextEnabled) setSelectedTarget(null);
215
- }, [storageKey]);
216
-
217
- const selectTarget = useCallback((target: InspectorTarget | null) => {
218
- setSelectedTarget(target);
219
- if (!target) return null;
220
- setCommentIntent(target.placement === "before" ? "add" : "edit");
221
- const sourceBlock = blockMap[target.blockId] ?? null;
222
- return sourceBlock;
223
- }, [blockMap]);
224
-
225
- const inspectTarget = useCallback((target: EventTarget | null) => {
226
- const inspectorTarget = findInspectorTarget(target);
227
- return selectTarget(inspectorTarget);
228
- }, [selectTarget]);
229
-
230
- const handleClick = useCallback((event: ReactMouseEvent) => {
231
- if (!enabled || !inspectorMode) return false;
232
- const inspectorTarget = findInspectorTarget(event.target);
233
- if (!inspectorTarget) return false;
234
- selectTarget(inspectorTarget);
235
- event.preventDefault();
236
- event.stopPropagation();
237
- return true;
238
- }, [enabled, inspectorMode, selectTarget]);
239
-
240
- const selectedBlockId = selectedTarget?.blockId ?? null;
241
- const selectedBlock = selectedBlockId ? (blockMap[selectedBlockId] ?? null) : null;
242
-
243
- return {
244
- enabled,
245
- inspectorMode: enabled && inspectorMode,
246
- selectedBlockId,
247
- selectedBlock,
248
- selectedTarget,
249
- commentIntent,
250
- setInspectorMode,
251
- toggleInspectorMode: () => setInspectorMode(!inspectorMode),
252
- setCommentIntent,
253
- selectTarget,
254
- inspectTarget,
255
- handleClick,
256
- };
257
- }
258
-
259
- export function findInspectorTarget(target: EventTarget | null): InspectorTarget | null {
260
- if (typeof Element === "undefined") return null;
261
- if (!(target instanceof Element)) return null;
262
- const insertTarget = target.closest<HTMLElement>("[data-openpress-insert-before-block-id]");
263
- const insertBeforeBlockId = insertTarget?.dataset.openpressInsertBeforeBlockId;
264
- if (insertBeforeBlockId) return { blockId: insertBeforeBlockId, placement: "before" };
265
-
266
- const element = target.closest<HTMLElement>("[data-openpress-block-id]");
267
- const blockId = element?.dataset.openpressBlockId;
268
- return blockId ? { blockId, placement: "block" } : null;
269
- }
270
-
271
- export function formatInspectorHint({
272
- intent,
273
- placement,
274
- }: {
275
- intent?: InspectorIntent;
276
- placement?: InspectorPlacement;
277
- } = {}) {
278
- const parts = ["openpress-react-inspector"];
279
- if (intent) parts.push(`intent=${intent}`);
280
- if (placement) parts.push(`placement=${placement}`);
281
- return parts.join(" ");
282
- }