retold-content-system 1.0.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/LICENSE +21 -0
- package/README.md +107 -0
- package/build-codejar-bundle.js +29 -0
- package/build-codemirror-bundle.js +29 -0
- package/codejar-entry.js +10 -0
- package/codemirror-entry.js +16 -0
- package/content/Dogs.txt.md +2 -0
- package/content/README.md +35 -0
- package/content/_sidebar.md +3 -0
- package/content/_topbar.md +1 -0
- package/content/cover.md +12 -0
- package/content/getting-started.md +73 -0
- package/css/content-system.css +42 -0
- package/css/github.css +118 -0
- package/docs/.nojekyll +0 -0
- package/docs/README.md +24 -0
- package/docs/_sidebar.md +16 -0
- package/docs/_topbar.md +6 -0
- package/docs/cli.md +119 -0
- package/docs/cover.md +16 -0
- package/docs/css/docuserve.css +73 -0
- package/docs/editor-guide.md +137 -0
- package/docs/getting-started.md +73 -0
- package/docs/index.html +39 -0
- package/docs/keyboard-shortcuts.md +40 -0
- package/docs/retold-catalog.json +81 -0
- package/docs/retold-keyword-index.json +19 -0
- package/docs/topics.md +83 -0
- package/html/codejar-bundle.js +16 -0
- package/html/codemirror-bundle.js +29982 -0
- package/html/edit.html +25 -0
- package/html/index.html +25 -0
- package/html/preview.html +19 -0
- package/package.json +70 -0
- package/server.js +43 -0
- package/source/Pict-Application-ContentEditor-Configuration.json +15 -0
- package/source/Pict-Application-ContentEditor.js +1361 -0
- package/source/Pict-Application-ContentReader-Configuration.json +15 -0
- package/source/Pict-Application-ContentReader.js +91 -0
- package/source/Pict-ContentSystem-Bundle.js +21 -0
- package/source/cli/ContentSystem-CLI-Program.js +15 -0
- package/source/cli/ContentSystem-CLI-Run.js +3 -0
- package/source/cli/ContentSystem-Server-Setup.js +405 -0
- package/source/cli/commands/ContentSystem-Command-Serve.js +104 -0
- package/source/providers/Pict-Provider-ContentEditor.js +198 -0
- package/source/views/PictView-Editor-CodeEditor.js +271 -0
- package/source/views/PictView-Editor-Layout.js +1194 -0
- package/source/views/PictView-Editor-MarkdownEditor.js +115 -0
- package/source/views/PictView-Editor-MarkdownReference.js +801 -0
- package/source/views/PictView-Editor-SettingsPanel.js +563 -0
- package/source/views/PictView-Editor-TopBar.js +366 -0
- package/source/views/PictView-Editor-Topics.js +1025 -0
|
@@ -0,0 +1,801 @@
|
|
|
1
|
+
const libPictView = require('pict-view');
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Built-in Markdown reference content.
|
|
5
|
+
*
|
|
6
|
+
* Pre-rendered HTML covering GitHub-Flavored Markdown, KaTeX math,
|
|
7
|
+
* and Mermaid diagrams. All angle brackets and ampersands inside
|
|
8
|
+
* code blocks are entity-encoded so they render as literal text.
|
|
9
|
+
*/
|
|
10
|
+
const _MarkdownReferenceContent = /*html*/`
|
|
11
|
+
<h2>Headings</h2>
|
|
12
|
+
<p>Use <code>#</code> through <code>######</code> for heading levels 1–6.</p>
|
|
13
|
+
<pre><code># Heading 1
|
|
14
|
+
## Heading 2
|
|
15
|
+
### Heading 3
|
|
16
|
+
#### Heading 4
|
|
17
|
+
##### Heading 5
|
|
18
|
+
###### Heading 6</code><button class="md-ref-copy-btn" onclick="pict.views['ContentEditor-MarkdownReference'].copyCodeBlock(this)">Copy</button></pre>
|
|
19
|
+
|
|
20
|
+
<h2>Emphasis</h2>
|
|
21
|
+
<p>Bold, italic, strikethrough, and combinations.</p>
|
|
22
|
+
<pre><code>**bold text**
|
|
23
|
+
*italic text*
|
|
24
|
+
***bold and italic***
|
|
25
|
+
~~strikethrough~~
|
|
26
|
+
**bold and ~~strikethrough~~**</code><button class="md-ref-copy-btn" onclick="pict.views['ContentEditor-MarkdownReference'].copyCodeBlock(this)">Copy</button></pre>
|
|
27
|
+
|
|
28
|
+
<h2>Inline Code</h2>
|
|
29
|
+
<p>Wrap text in backticks for inline code.</p>
|
|
30
|
+
<pre><code>Use \`console.log()\` to debug.
|
|
31
|
+
Use \`\`double backticks for \`literal\` backticks\`\`.</code><button class="md-ref-copy-btn" onclick="pict.views['ContentEditor-MarkdownReference'].copyCodeBlock(this)">Copy</button></pre>
|
|
32
|
+
|
|
33
|
+
<h2>Links</h2>
|
|
34
|
+
<p>Inline links, reference links, and autolinks.</p>
|
|
35
|
+
<pre><code>[Link text](https://example.com)
|
|
36
|
+
[Link with title](https://example.com "Title text")
|
|
37
|
+
|
|
38
|
+
[Reference link][1]
|
|
39
|
+
[1]: https://example.com
|
|
40
|
+
|
|
41
|
+
Autolink: https://example.com
|
|
42
|
+
Email: user@example.com</code><button class="md-ref-copy-btn" onclick="pict.views['ContentEditor-MarkdownReference'].copyCodeBlock(this)">Copy</button></pre>
|
|
43
|
+
|
|
44
|
+
<h2>Images</h2>
|
|
45
|
+
<pre><code>
|
|
46
|
+

|
|
47
|
+
|
|
48
|
+
[](https://example.com)</code><button class="md-ref-copy-btn" onclick="pict.views['ContentEditor-MarkdownReference'].copyCodeBlock(this)">Copy</button></pre>
|
|
49
|
+
|
|
50
|
+
<h2>Unordered Lists</h2>
|
|
51
|
+
<p>Use <code>-</code>, <code>*</code>, or <code>+</code> for bullet items.</p>
|
|
52
|
+
<pre><code>- Item one
|
|
53
|
+
- Item two
|
|
54
|
+
- Nested item
|
|
55
|
+
- Another nested
|
|
56
|
+
- Deeply nested
|
|
57
|
+
- Item three</code><button class="md-ref-copy-btn" onclick="pict.views['ContentEditor-MarkdownReference'].copyCodeBlock(this)">Copy</button></pre>
|
|
58
|
+
|
|
59
|
+
<h2>Ordered Lists</h2>
|
|
60
|
+
<pre><code>1. First item
|
|
61
|
+
2. Second item
|
|
62
|
+
3. Third item
|
|
63
|
+
1. Sub-item A
|
|
64
|
+
2. Sub-item B</code><button class="md-ref-copy-btn" onclick="pict.views['ContentEditor-MarkdownReference'].copyCodeBlock(this)">Copy</button></pre>
|
|
65
|
+
|
|
66
|
+
<h2>Task Lists</h2>
|
|
67
|
+
<pre><code>- [x] Completed task
|
|
68
|
+
- [ ] Incomplete task
|
|
69
|
+
- [ ] Another task</code><button class="md-ref-copy-btn" onclick="pict.views['ContentEditor-MarkdownReference'].copyCodeBlock(this)">Copy</button></pre>
|
|
70
|
+
|
|
71
|
+
<h2>Blockquotes</h2>
|
|
72
|
+
<pre><code>> This is a blockquote.
|
|
73
|
+
>
|
|
74
|
+
> It can span multiple paragraphs.
|
|
75
|
+
>
|
|
76
|
+
>> Nested blockquote.</code><button class="md-ref-copy-btn" onclick="pict.views['ContentEditor-MarkdownReference'].copyCodeBlock(this)">Copy</button></pre>
|
|
77
|
+
|
|
78
|
+
<h2>Code Blocks</h2>
|
|
79
|
+
<p>Use triple backticks with an optional language identifier.</p>
|
|
80
|
+
<pre><code>\`\`\`javascript
|
|
81
|
+
function hello()
|
|
82
|
+
{
|
|
83
|
+
console.log("Hello, world!");
|
|
84
|
+
}
|
|
85
|
+
\`\`\`
|
|
86
|
+
|
|
87
|
+
\`\`\`python
|
|
88
|
+
def hello():
|
|
89
|
+
print("Hello, world!")
|
|
90
|
+
\`\`\`
|
|
91
|
+
|
|
92
|
+
\`\`\`css
|
|
93
|
+
.container {
|
|
94
|
+
display: flex;
|
|
95
|
+
gap: 16px;
|
|
96
|
+
}
|
|
97
|
+
\`\`\`</code><button class="md-ref-copy-btn" onclick="pict.views['ContentEditor-MarkdownReference'].copyCodeBlock(this)">Copy</button></pre>
|
|
98
|
+
|
|
99
|
+
<h2>Tables</h2>
|
|
100
|
+
<p>Use pipes and hyphens. Colons control alignment.</p>
|
|
101
|
+
<pre><code>| Left Align | Center Align | Right Align |
|
|
102
|
+
|:-----------|:------------:|------------:|
|
|
103
|
+
| Cell 1 | Cell 2 | Cell 3 |
|
|
104
|
+
| Cell 4 | Cell 5 | Cell 6 |</code><button class="md-ref-copy-btn" onclick="pict.views['ContentEditor-MarkdownReference'].copyCodeBlock(this)">Copy</button></pre>
|
|
105
|
+
|
|
106
|
+
<h2>Horizontal Rules</h2>
|
|
107
|
+
<p>Three or more hyphens, asterisks, or underscores.</p>
|
|
108
|
+
<pre><code>---
|
|
109
|
+
|
|
110
|
+
***
|
|
111
|
+
|
|
112
|
+
___</code><button class="md-ref-copy-btn" onclick="pict.views['ContentEditor-MarkdownReference'].copyCodeBlock(this)">Copy</button></pre>
|
|
113
|
+
|
|
114
|
+
<h2>Footnotes</h2>
|
|
115
|
+
<pre><code>Here is a footnote reference[^1] and another[^note].
|
|
116
|
+
|
|
117
|
+
[^1]: This is the footnote content.
|
|
118
|
+
[^note]: Footnotes can have any label.</code><button class="md-ref-copy-btn" onclick="pict.views['ContentEditor-MarkdownReference'].copyCodeBlock(this)">Copy</button></pre>
|
|
119
|
+
|
|
120
|
+
<h2>HTML in Markdown</h2>
|
|
121
|
+
<p>Raw HTML is allowed in GitHub-Flavored Markdown.</p>
|
|
122
|
+
<pre><code><details>
|
|
123
|
+
<summary>Click to expand</summary>
|
|
124
|
+
|
|
125
|
+
Hidden content here.
|
|
126
|
+
|
|
127
|
+
</details>
|
|
128
|
+
|
|
129
|
+
<kbd>Ctrl</kbd> + <kbd>S</kbd> to save.
|
|
130
|
+
|
|
131
|
+
<mark>Highlighted text</mark>
|
|
132
|
+
|
|
133
|
+
<sup>superscript</sup> and <sub>subscript</sub></code><button class="md-ref-copy-btn" onclick="pict.views['ContentEditor-MarkdownReference'].copyCodeBlock(this)">Copy</button></pre>
|
|
134
|
+
|
|
135
|
+
<h2>Escaping Characters</h2>
|
|
136
|
+
<p>Backslash-escape special markdown characters.</p>
|
|
137
|
+
<pre><code>\\*not italic\\*
|
|
138
|
+
\\# not a heading
|
|
139
|
+
\\[not a link\\](url)
|
|
140
|
+
\\\`not code\\\`</code><button class="md-ref-copy-btn" onclick="pict.views['ContentEditor-MarkdownReference'].copyCodeBlock(this)">Copy</button></pre>
|
|
141
|
+
|
|
142
|
+
<h2>Line Breaks</h2>
|
|
143
|
+
<pre><code>End a line with two spaces
|
|
144
|
+
to create a line break.
|
|
145
|
+
|
|
146
|
+
Or use a blank line
|
|
147
|
+
|
|
148
|
+
for a new paragraph.</code><button class="md-ref-copy-btn" onclick="pict.views['ContentEditor-MarkdownReference'].copyCodeBlock(this)">Copy</button></pre>
|
|
149
|
+
|
|
150
|
+
<h2>KaTeX — Inline Math</h2>
|
|
151
|
+
<p>Wrap expressions with single dollar signs for inline math.</p>
|
|
152
|
+
<pre><code>The equation $E = mc^2$ is famous.
|
|
153
|
+
|
|
154
|
+
The quadratic formula is $x = \\frac{-b \\pm \\sqrt{b^2 - 4ac}}{2a}$.
|
|
155
|
+
|
|
156
|
+
Greek letters: $\\alpha$, $\\beta$, $\\gamma$, $\\delta$, $\\theta$, $\\pi$.
|
|
157
|
+
|
|
158
|
+
Subscripts and superscripts: $x_i^2$ and $a_{n+1}$.</code><button class="md-ref-copy-btn" onclick="pict.views['ContentEditor-MarkdownReference'].copyCodeBlock(this)">Copy</button></pre>
|
|
159
|
+
|
|
160
|
+
<h2>KaTeX — Display Math</h2>
|
|
161
|
+
<p>Use double dollar signs on their own lines for display (block) math.</p>
|
|
162
|
+
|
|
163
|
+
<h3>Integral</h3>
|
|
164
|
+
<pre><code>$$
|
|
165
|
+
\\int_{-\\infty}^{\\infty} e^{-x^2} \\, dx = \\sqrt{\\pi}
|
|
166
|
+
$$</code><button class="md-ref-copy-btn" onclick="pict.views['ContentEditor-MarkdownReference'].copyCodeBlock(this)">Copy</button></pre>
|
|
167
|
+
|
|
168
|
+
<h3>Summation</h3>
|
|
169
|
+
<pre><code>$$
|
|
170
|
+
\\sum_{n=1}^{\\infty} \\frac{1}{n^2} = \\frac{\\pi^2}{6}
|
|
171
|
+
$$</code><button class="md-ref-copy-btn" onclick="pict.views['ContentEditor-MarkdownReference'].copyCodeBlock(this)">Copy</button></pre>
|
|
172
|
+
|
|
173
|
+
<h3>Matrix</h3>
|
|
174
|
+
<pre><code>$$
|
|
175
|
+
\\begin{bmatrix}
|
|
176
|
+
a & b \\\\
|
|
177
|
+
c & d
|
|
178
|
+
\\end{bmatrix}
|
|
179
|
+
\\begin{bmatrix}
|
|
180
|
+
x \\\\
|
|
181
|
+
y
|
|
182
|
+
\\end{bmatrix}
|
|
183
|
+
=
|
|
184
|
+
\\begin{bmatrix}
|
|
185
|
+
ax + by \\\\
|
|
186
|
+
cx + dy
|
|
187
|
+
\\end{bmatrix}
|
|
188
|
+
$$</code><button class="md-ref-copy-btn" onclick="pict.views['ContentEditor-MarkdownReference'].copyCodeBlock(this)">Copy</button></pre>
|
|
189
|
+
|
|
190
|
+
<h3>Aligned Equations</h3>
|
|
191
|
+
<pre><code>$$
|
|
192
|
+
\\begin{aligned}
|
|
193
|
+
f(x) &= x^2 + 2x + 1 \\\\
|
|
194
|
+
&= (x + 1)^2
|
|
195
|
+
\\end{aligned}
|
|
196
|
+
$$</code><button class="md-ref-copy-btn" onclick="pict.views['ContentEditor-MarkdownReference'].copyCodeBlock(this)">Copy</button></pre>
|
|
197
|
+
|
|
198
|
+
<h3>Cases (Piecewise)</h3>
|
|
199
|
+
<pre><code>$$
|
|
200
|
+
f(x) = \\begin{cases}
|
|
201
|
+
x^2 & \\text{if } x \\geq 0 \\\\
|
|
202
|
+
-x^2 & \\text{if } x < 0
|
|
203
|
+
\\end{cases}
|
|
204
|
+
$$</code><button class="md-ref-copy-btn" onclick="pict.views['ContentEditor-MarkdownReference'].copyCodeBlock(this)">Copy</button></pre>
|
|
205
|
+
|
|
206
|
+
<h3>Fractions & Limits</h3>
|
|
207
|
+
<pre><code>$$
|
|
208
|
+
\\lim_{x \\to 0} \\frac{\\sin x}{x} = 1
|
|
209
|
+
$$</code><button class="md-ref-copy-btn" onclick="pict.views['ContentEditor-MarkdownReference'].copyCodeBlock(this)">Copy</button></pre>
|
|
210
|
+
|
|
211
|
+
<h2>Mermaid — Flowchart</h2>
|
|
212
|
+
<pre><code>\`\`\`mermaid
|
|
213
|
+
graph TD
|
|
214
|
+
A[Start] --> B{Decision}
|
|
215
|
+
B -->|Yes| C[Do something]
|
|
216
|
+
B -->|No| D[Do something else]
|
|
217
|
+
C --> E[End]
|
|
218
|
+
D --> E
|
|
219
|
+
\`\`\`</code><button class="md-ref-copy-btn" onclick="pict.views['ContentEditor-MarkdownReference'].copyCodeBlock(this)">Copy</button></pre>
|
|
220
|
+
|
|
221
|
+
<h2>Mermaid — Sequence Diagram</h2>
|
|
222
|
+
<pre><code>\`\`\`mermaid
|
|
223
|
+
sequenceDiagram
|
|
224
|
+
participant A as Alice
|
|
225
|
+
participant B as Bob
|
|
226
|
+
A->>B: Hello Bob
|
|
227
|
+
B-->>A: Hi Alice
|
|
228
|
+
A->>B: How are you?
|
|
229
|
+
B-->>A: Great!
|
|
230
|
+
\`\`\`</code><button class="md-ref-copy-btn" onclick="pict.views['ContentEditor-MarkdownReference'].copyCodeBlock(this)">Copy</button></pre>
|
|
231
|
+
|
|
232
|
+
<h2>Mermaid — Gantt Chart</h2>
|
|
233
|
+
<pre><code>\`\`\`mermaid
|
|
234
|
+
gantt
|
|
235
|
+
title Project Timeline
|
|
236
|
+
dateFormat YYYY-MM-DD
|
|
237
|
+
section Phase 1
|
|
238
|
+
Research :a1, 2024-01-01, 30d
|
|
239
|
+
Design :a2, after a1, 20d
|
|
240
|
+
section Phase 2
|
|
241
|
+
Development :b1, after a2, 40d
|
|
242
|
+
Testing :b2, after b1, 15d
|
|
243
|
+
\`\`\`</code><button class="md-ref-copy-btn" onclick="pict.views['ContentEditor-MarkdownReference'].copyCodeBlock(this)">Copy</button></pre>
|
|
244
|
+
|
|
245
|
+
<h2>Mermaid — Class Diagram</h2>
|
|
246
|
+
<pre><code>\`\`\`mermaid
|
|
247
|
+
classDiagram
|
|
248
|
+
Animal <|-- Duck
|
|
249
|
+
Animal <|-- Fish
|
|
250
|
+
Animal : +int age
|
|
251
|
+
Animal : +String gender
|
|
252
|
+
Animal : +swim()
|
|
253
|
+
Duck : +String beakColor
|
|
254
|
+
Duck : +quack()
|
|
255
|
+
Fish : +int sizeInFeet
|
|
256
|
+
Fish : +canEat()
|
|
257
|
+
\`\`\`</code><button class="md-ref-copy-btn" onclick="pict.views['ContentEditor-MarkdownReference'].copyCodeBlock(this)">Copy</button></pre>
|
|
258
|
+
|
|
259
|
+
<h2>Mermaid — State Diagram</h2>
|
|
260
|
+
<pre><code>\`\`\`mermaid
|
|
261
|
+
stateDiagram-v2
|
|
262
|
+
[*] --> Idle
|
|
263
|
+
Idle --> Processing : Start
|
|
264
|
+
Processing --> Done : Complete
|
|
265
|
+
Processing --> Error : Fail
|
|
266
|
+
Error --> Idle : Reset
|
|
267
|
+
Done --> [*]
|
|
268
|
+
\`\`\`</code><button class="md-ref-copy-btn" onclick="pict.views['ContentEditor-MarkdownReference'].copyCodeBlock(this)">Copy</button></pre>
|
|
269
|
+
`;
|
|
270
|
+
|
|
271
|
+
const _ViewConfiguration =
|
|
272
|
+
{
|
|
273
|
+
ViewIdentifier: "ContentEditor-MarkdownReference",
|
|
274
|
+
|
|
275
|
+
DefaultRenderable: "ContentEditor-MarkdownReference-Display",
|
|
276
|
+
DefaultDestinationAddress: "#ContentEditor-SidebarReference-Container",
|
|
277
|
+
|
|
278
|
+
AutoRender: false,
|
|
279
|
+
|
|
280
|
+
CSS: /*css*/`
|
|
281
|
+
.md-ref-container
|
|
282
|
+
{
|
|
283
|
+
display: flex;
|
|
284
|
+
flex-direction: column;
|
|
285
|
+
height: 100%;
|
|
286
|
+
background: #FAF8F4;
|
|
287
|
+
}
|
|
288
|
+
.md-ref-search-bar
|
|
289
|
+
{
|
|
290
|
+
position: sticky;
|
|
291
|
+
top: 0;
|
|
292
|
+
z-index: 5;
|
|
293
|
+
display: flex;
|
|
294
|
+
align-items: center;
|
|
295
|
+
gap: 4px;
|
|
296
|
+
padding: 6px 8px;
|
|
297
|
+
background: #F5F0EA;
|
|
298
|
+
border-bottom: 1px solid #DDD6CA;
|
|
299
|
+
flex-shrink: 0;
|
|
300
|
+
}
|
|
301
|
+
.md-ref-search-input
|
|
302
|
+
{
|
|
303
|
+
flex: 1;
|
|
304
|
+
min-width: 0;
|
|
305
|
+
padding: 5px 8px;
|
|
306
|
+
border: 1px solid #DDD6CA;
|
|
307
|
+
border-radius: 4px;
|
|
308
|
+
font-size: 0.8rem;
|
|
309
|
+
background: #FFF;
|
|
310
|
+
color: #3D3229;
|
|
311
|
+
outline: none;
|
|
312
|
+
}
|
|
313
|
+
.md-ref-search-input:focus
|
|
314
|
+
{
|
|
315
|
+
border-color: #2E7D74;
|
|
316
|
+
}
|
|
317
|
+
.md-ref-search-nav
|
|
318
|
+
{
|
|
319
|
+
background: transparent;
|
|
320
|
+
border: 1px solid #DDD6CA;
|
|
321
|
+
border-radius: 4px;
|
|
322
|
+
width: 26px;
|
|
323
|
+
height: 26px;
|
|
324
|
+
cursor: pointer;
|
|
325
|
+
color: #5E5549;
|
|
326
|
+
font-size: 0.7rem;
|
|
327
|
+
display: flex;
|
|
328
|
+
align-items: center;
|
|
329
|
+
justify-content: center;
|
|
330
|
+
flex-shrink: 0;
|
|
331
|
+
}
|
|
332
|
+
.md-ref-search-nav:hover
|
|
333
|
+
{
|
|
334
|
+
background: #EDE9E3;
|
|
335
|
+
}
|
|
336
|
+
.md-ref-search-nav:disabled
|
|
337
|
+
{
|
|
338
|
+
opacity: 0.3;
|
|
339
|
+
cursor: not-allowed;
|
|
340
|
+
}
|
|
341
|
+
.md-ref-search-count
|
|
342
|
+
{
|
|
343
|
+
font-size: 0.7rem;
|
|
344
|
+
color: #8A7F72;
|
|
345
|
+
white-space: nowrap;
|
|
346
|
+
min-width: 32px;
|
|
347
|
+
text-align: center;
|
|
348
|
+
flex-shrink: 0;
|
|
349
|
+
}
|
|
350
|
+
.md-ref-content
|
|
351
|
+
{
|
|
352
|
+
flex: 1;
|
|
353
|
+
overflow-y: auto;
|
|
354
|
+
padding: 12px;
|
|
355
|
+
font-size: 0.82rem;
|
|
356
|
+
line-height: 1.6;
|
|
357
|
+
color: #3D3229;
|
|
358
|
+
}
|
|
359
|
+
.md-ref-content h2
|
|
360
|
+
{
|
|
361
|
+
font-size: 0.95rem;
|
|
362
|
+
margin: 20px 0 8px 0;
|
|
363
|
+
padding-bottom: 4px;
|
|
364
|
+
border-bottom: 1px solid #EDE9E3;
|
|
365
|
+
color: #2E7D74;
|
|
366
|
+
}
|
|
367
|
+
.md-ref-content h2:first-child
|
|
368
|
+
{
|
|
369
|
+
margin-top: 0;
|
|
370
|
+
}
|
|
371
|
+
.md-ref-content h3
|
|
372
|
+
{
|
|
373
|
+
font-size: 0.85rem;
|
|
374
|
+
margin: 14px 0 6px 0;
|
|
375
|
+
color: #5E5549;
|
|
376
|
+
}
|
|
377
|
+
.md-ref-content p
|
|
378
|
+
{
|
|
379
|
+
margin: 6px 0;
|
|
380
|
+
}
|
|
381
|
+
.md-ref-content code
|
|
382
|
+
{
|
|
383
|
+
background: #F0EDE8;
|
|
384
|
+
padding: 1px 4px;
|
|
385
|
+
border-radius: 3px;
|
|
386
|
+
font-size: 0.78rem;
|
|
387
|
+
font-family: monospace;
|
|
388
|
+
}
|
|
389
|
+
.md-ref-content pre
|
|
390
|
+
{
|
|
391
|
+
background: #F0EDE8;
|
|
392
|
+
border: 1px solid #DDD6CA;
|
|
393
|
+
border-radius: 4px;
|
|
394
|
+
padding: 8px 10px;
|
|
395
|
+
overflow-x: auto;
|
|
396
|
+
font-size: 0.76rem;
|
|
397
|
+
line-height: 1.5;
|
|
398
|
+
margin: 6px 0;
|
|
399
|
+
position: relative;
|
|
400
|
+
}
|
|
401
|
+
.md-ref-content pre code
|
|
402
|
+
{
|
|
403
|
+
background: none;
|
|
404
|
+
padding: 0;
|
|
405
|
+
font-size: inherit;
|
|
406
|
+
}
|
|
407
|
+
.md-ref-copy-btn
|
|
408
|
+
{
|
|
409
|
+
position: absolute;
|
|
410
|
+
top: 4px;
|
|
411
|
+
right: 4px;
|
|
412
|
+
background: #FAF8F4;
|
|
413
|
+
border: 1px solid #DDD6CA;
|
|
414
|
+
border-radius: 3px;
|
|
415
|
+
padding: 2px 6px;
|
|
416
|
+
font-size: 0.65rem;
|
|
417
|
+
color: #8A7F72;
|
|
418
|
+
cursor: pointer;
|
|
419
|
+
opacity: 0;
|
|
420
|
+
transition: opacity 0.15s;
|
|
421
|
+
}
|
|
422
|
+
.md-ref-content pre:hover .md-ref-copy-btn
|
|
423
|
+
{
|
|
424
|
+
opacity: 1;
|
|
425
|
+
}
|
|
426
|
+
.md-ref-copy-btn:hover
|
|
427
|
+
{
|
|
428
|
+
background: #EDE9E3;
|
|
429
|
+
color: #3D3229;
|
|
430
|
+
}
|
|
431
|
+
/* Search highlight */
|
|
432
|
+
mark.md-ref-highlight
|
|
433
|
+
{
|
|
434
|
+
background: #FFEAA7;
|
|
435
|
+
color: inherit;
|
|
436
|
+
padding: 0;
|
|
437
|
+
border-radius: 2px;
|
|
438
|
+
}
|
|
439
|
+
mark.md-ref-highlight-active
|
|
440
|
+
{
|
|
441
|
+
background: #E8A94D;
|
|
442
|
+
color: #FFF;
|
|
443
|
+
}
|
|
444
|
+
`,
|
|
445
|
+
|
|
446
|
+
Templates:
|
|
447
|
+
[
|
|
448
|
+
{
|
|
449
|
+
Hash: "ContentEditor-MarkdownReference-Template",
|
|
450
|
+
Template: /*html*/`
|
|
451
|
+
<div class="md-ref-container">
|
|
452
|
+
<div class="md-ref-search-bar">
|
|
453
|
+
<input type="text" class="md-ref-search-input"
|
|
454
|
+
id="ContentEditor-MdRef-SearchInput"
|
|
455
|
+
placeholder="Search reference..."
|
|
456
|
+
oninput="{~P~}.views['ContentEditor-MarkdownReference'].onSearchInput(this.value)"
|
|
457
|
+
onkeydown="{~P~}.views['ContentEditor-MarkdownReference'].onSearchKeydown(event)">
|
|
458
|
+
<span class="md-ref-search-count" id="ContentEditor-MdRef-SearchCount"></span>
|
|
459
|
+
<button class="md-ref-search-nav" id="ContentEditor-MdRef-SearchPrev"
|
|
460
|
+
onclick="{~P~}.views['ContentEditor-MarkdownReference'].navigateMatch(-1)" disabled>▲</button>
|
|
461
|
+
<button class="md-ref-search-nav" id="ContentEditor-MdRef-SearchNext"
|
|
462
|
+
onclick="{~P~}.views['ContentEditor-MarkdownReference'].navigateMatch(1)" disabled>▼</button>
|
|
463
|
+
</div>
|
|
464
|
+
<div class="md-ref-content" id="ContentEditor-MdRef-Content">
|
|
465
|
+
` + _MarkdownReferenceContent + `
|
|
466
|
+
</div>
|
|
467
|
+
</div>
|
|
468
|
+
`
|
|
469
|
+
}
|
|
470
|
+
],
|
|
471
|
+
|
|
472
|
+
Renderables:
|
|
473
|
+
[
|
|
474
|
+
{
|
|
475
|
+
RenderableHash: "ContentEditor-MarkdownReference-Display",
|
|
476
|
+
TemplateHash: "ContentEditor-MarkdownReference-Template",
|
|
477
|
+
DestinationAddress: "#ContentEditor-SidebarReference-Container",
|
|
478
|
+
RenderMethod: "replace"
|
|
479
|
+
}
|
|
480
|
+
]
|
|
481
|
+
};
|
|
482
|
+
|
|
483
|
+
/**
|
|
484
|
+
* Markdown Reference View
|
|
485
|
+
*
|
|
486
|
+
* A built-in, read-only reference for GitHub-Flavored Markdown, KaTeX
|
|
487
|
+
* math, and Mermaid diagrams. Provides a fixed search bar with
|
|
488
|
+
* prev/next navigation and copy-to-clipboard buttons on all code blocks.
|
|
489
|
+
*/
|
|
490
|
+
class ContentEditorMarkdownReferenceView extends libPictView
|
|
491
|
+
{
|
|
492
|
+
constructor(pFable, pOptions, pServiceHash)
|
|
493
|
+
{
|
|
494
|
+
super(pFable, pOptions, pServiceHash);
|
|
495
|
+
|
|
496
|
+
this._hasRendered = false;
|
|
497
|
+
this._searchMatches = [];
|
|
498
|
+
this._currentMatchIndex = -1;
|
|
499
|
+
this._originalContent = '';
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
onAfterRender(pRenderable, pRenderDestinationAddress, pRecord, pContent)
|
|
503
|
+
{
|
|
504
|
+
this._hasRendered = true;
|
|
505
|
+
|
|
506
|
+
// Cache the original innerHTML for search restoration
|
|
507
|
+
let tmpContentEl = document.getElementById('ContentEditor-MdRef-Content');
|
|
508
|
+
if (tmpContentEl)
|
|
509
|
+
{
|
|
510
|
+
this._originalContent = tmpContentEl.innerHTML;
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
// Inject CSS
|
|
514
|
+
this.pict.CSSMap.injectCSS();
|
|
515
|
+
|
|
516
|
+
return super.onAfterRender(pRenderable, pRenderDestinationAddress, pRecord, pContent);
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
/**
|
|
520
|
+
* Copy the text content of a code block to the clipboard.
|
|
521
|
+
*
|
|
522
|
+
* @param {HTMLElement} pButton - The copy button element
|
|
523
|
+
*/
|
|
524
|
+
copyCodeBlock(pButton)
|
|
525
|
+
{
|
|
526
|
+
let tmpPre = pButton.closest('pre');
|
|
527
|
+
if (!tmpPre)
|
|
528
|
+
{
|
|
529
|
+
return;
|
|
530
|
+
}
|
|
531
|
+
let tmpCode = tmpPre.querySelector('code');
|
|
532
|
+
if (!tmpCode)
|
|
533
|
+
{
|
|
534
|
+
return;
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
let tmpText = tmpCode.textContent;
|
|
538
|
+
|
|
539
|
+
if (navigator.clipboard)
|
|
540
|
+
{
|
|
541
|
+
navigator.clipboard.writeText(tmpText).then(() =>
|
|
542
|
+
{
|
|
543
|
+
pButton.textContent = 'Copied!';
|
|
544
|
+
setTimeout(() =>
|
|
545
|
+
{
|
|
546
|
+
pButton.textContent = 'Copy';
|
|
547
|
+
}, 1500);
|
|
548
|
+
});
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
/**
|
|
553
|
+
* Handle search input changes.
|
|
554
|
+
*
|
|
555
|
+
* @param {string} pQuery - The search query
|
|
556
|
+
*/
|
|
557
|
+
onSearchInput(pQuery)
|
|
558
|
+
{
|
|
559
|
+
this._performSearch(pQuery);
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
/**
|
|
563
|
+
* Handle keyboard navigation in the search input.
|
|
564
|
+
*
|
|
565
|
+
* @param {KeyboardEvent} pEvent
|
|
566
|
+
*/
|
|
567
|
+
onSearchKeydown(pEvent)
|
|
568
|
+
{
|
|
569
|
+
if (pEvent.key === 'Enter')
|
|
570
|
+
{
|
|
571
|
+
pEvent.preventDefault();
|
|
572
|
+
if (pEvent.shiftKey)
|
|
573
|
+
{
|
|
574
|
+
this.navigateMatch(-1);
|
|
575
|
+
}
|
|
576
|
+
else
|
|
577
|
+
{
|
|
578
|
+
this.navigateMatch(1);
|
|
579
|
+
}
|
|
580
|
+
}
|
|
581
|
+
if (pEvent.key === 'Escape')
|
|
582
|
+
{
|
|
583
|
+
pEvent.target.value = '';
|
|
584
|
+
this._clearSearch();
|
|
585
|
+
}
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
/**
|
|
589
|
+
* Navigate to the previous or next search match.
|
|
590
|
+
*
|
|
591
|
+
* @param {number} pDirection - -1 for previous, 1 for next
|
|
592
|
+
*/
|
|
593
|
+
navigateMatch(pDirection)
|
|
594
|
+
{
|
|
595
|
+
if (this._searchMatches.length === 0)
|
|
596
|
+
{
|
|
597
|
+
return;
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
// Remove active highlight from current
|
|
601
|
+
if (this._currentMatchIndex >= 0 && this._searchMatches[this._currentMatchIndex])
|
|
602
|
+
{
|
|
603
|
+
this._searchMatches[this._currentMatchIndex].classList.remove('md-ref-highlight-active');
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
this._currentMatchIndex += pDirection;
|
|
607
|
+
|
|
608
|
+
// Wrap around
|
|
609
|
+
if (this._currentMatchIndex >= this._searchMatches.length)
|
|
610
|
+
{
|
|
611
|
+
this._currentMatchIndex = 0;
|
|
612
|
+
}
|
|
613
|
+
if (this._currentMatchIndex < 0)
|
|
614
|
+
{
|
|
615
|
+
this._currentMatchIndex = this._searchMatches.length - 1;
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
// Activate and scroll into view
|
|
619
|
+
let tmpMatch = this._searchMatches[this._currentMatchIndex];
|
|
620
|
+
tmpMatch.classList.add('md-ref-highlight-active');
|
|
621
|
+
tmpMatch.scrollIntoView({ behavior: 'smooth', block: 'center' });
|
|
622
|
+
|
|
623
|
+
this._updateSearchCount();
|
|
624
|
+
}
|
|
625
|
+
|
|
626
|
+
/**
|
|
627
|
+
* Perform text search using TreeWalker to find and highlight matches.
|
|
628
|
+
*
|
|
629
|
+
* @param {string} pQuery - The search term
|
|
630
|
+
*/
|
|
631
|
+
_performSearch(pQuery)
|
|
632
|
+
{
|
|
633
|
+
let tmpContentEl = document.getElementById('ContentEditor-MdRef-Content');
|
|
634
|
+
if (!tmpContentEl)
|
|
635
|
+
{
|
|
636
|
+
return;
|
|
637
|
+
}
|
|
638
|
+
|
|
639
|
+
// Restore original content to clear previous highlights
|
|
640
|
+
tmpContentEl.innerHTML = this._originalContent;
|
|
641
|
+
|
|
642
|
+
this._searchMatches = [];
|
|
643
|
+
this._currentMatchIndex = -1;
|
|
644
|
+
|
|
645
|
+
if (!pQuery || pQuery.length < 2)
|
|
646
|
+
{
|
|
647
|
+
this._updateSearchCount();
|
|
648
|
+
this._updateNavButtons();
|
|
649
|
+
return;
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
let tmpQueryLower = pQuery.toLowerCase();
|
|
653
|
+
|
|
654
|
+
// Walk all text nodes and wrap matches in <mark>
|
|
655
|
+
let tmpWalker = document.createTreeWalker(
|
|
656
|
+
tmpContentEl,
|
|
657
|
+
NodeFilter.SHOW_TEXT,
|
|
658
|
+
null,
|
|
659
|
+
false
|
|
660
|
+
);
|
|
661
|
+
|
|
662
|
+
let tmpNodesToProcess = [];
|
|
663
|
+
let tmpNode;
|
|
664
|
+
while ((tmpNode = tmpWalker.nextNode()))
|
|
665
|
+
{
|
|
666
|
+
if (tmpNode.nodeValue.toLowerCase().indexOf(tmpQueryLower) >= 0)
|
|
667
|
+
{
|
|
668
|
+
tmpNodesToProcess.push(tmpNode);
|
|
669
|
+
}
|
|
670
|
+
}
|
|
671
|
+
|
|
672
|
+
for (let i = 0; i < tmpNodesToProcess.length; i++)
|
|
673
|
+
{
|
|
674
|
+
this._highlightTextNode(tmpNodesToProcess[i], tmpQueryLower);
|
|
675
|
+
}
|
|
676
|
+
|
|
677
|
+
// Collect all mark elements
|
|
678
|
+
this._searchMatches = Array.from(
|
|
679
|
+
tmpContentEl.querySelectorAll('mark.md-ref-highlight')
|
|
680
|
+
);
|
|
681
|
+
|
|
682
|
+
// Auto-navigate to first match
|
|
683
|
+
if (this._searchMatches.length > 0)
|
|
684
|
+
{
|
|
685
|
+
this._currentMatchIndex = 0;
|
|
686
|
+
this._searchMatches[0].classList.add('md-ref-highlight-active');
|
|
687
|
+
this._searchMatches[0].scrollIntoView({ behavior: 'smooth', block: 'center' });
|
|
688
|
+
}
|
|
689
|
+
|
|
690
|
+
this._updateSearchCount();
|
|
691
|
+
this._updateNavButtons();
|
|
692
|
+
}
|
|
693
|
+
|
|
694
|
+
/**
|
|
695
|
+
* Replace matching text in a text node with <mark> elements.
|
|
696
|
+
*
|
|
697
|
+
* @param {Text} pTextNode - The text node to process
|
|
698
|
+
* @param {string} pQueryLower - The lowercased search query
|
|
699
|
+
*/
|
|
700
|
+
_highlightTextNode(pTextNode, pQueryLower)
|
|
701
|
+
{
|
|
702
|
+
let tmpText = pTextNode.nodeValue;
|
|
703
|
+
let tmpTextLower = tmpText.toLowerCase();
|
|
704
|
+
let tmpParent = pTextNode.parentNode;
|
|
705
|
+
let tmpFragment = document.createDocumentFragment();
|
|
706
|
+
let tmpLastIndex = 0;
|
|
707
|
+
let tmpIndex = tmpTextLower.indexOf(pQueryLower);
|
|
708
|
+
|
|
709
|
+
while (tmpIndex >= 0)
|
|
710
|
+
{
|
|
711
|
+
// Text before match
|
|
712
|
+
if (tmpIndex > tmpLastIndex)
|
|
713
|
+
{
|
|
714
|
+
tmpFragment.appendChild(
|
|
715
|
+
document.createTextNode(tmpText.substring(tmpLastIndex, tmpIndex))
|
|
716
|
+
);
|
|
717
|
+
}
|
|
718
|
+
|
|
719
|
+
// The match
|
|
720
|
+
let tmpMark = document.createElement('mark');
|
|
721
|
+
tmpMark.className = 'md-ref-highlight';
|
|
722
|
+
tmpMark.textContent = tmpText.substring(tmpIndex, tmpIndex + pQueryLower.length);
|
|
723
|
+
tmpFragment.appendChild(tmpMark);
|
|
724
|
+
|
|
725
|
+
tmpLastIndex = tmpIndex + pQueryLower.length;
|
|
726
|
+
tmpIndex = tmpTextLower.indexOf(pQueryLower, tmpLastIndex);
|
|
727
|
+
}
|
|
728
|
+
|
|
729
|
+
// Remaining text after last match
|
|
730
|
+
if (tmpLastIndex < tmpText.length)
|
|
731
|
+
{
|
|
732
|
+
tmpFragment.appendChild(
|
|
733
|
+
document.createTextNode(tmpText.substring(tmpLastIndex))
|
|
734
|
+
);
|
|
735
|
+
}
|
|
736
|
+
|
|
737
|
+
tmpParent.replaceChild(tmpFragment, pTextNode);
|
|
738
|
+
}
|
|
739
|
+
|
|
740
|
+
/**
|
|
741
|
+
* Update the match count display.
|
|
742
|
+
*/
|
|
743
|
+
_updateSearchCount()
|
|
744
|
+
{
|
|
745
|
+
let tmpCountEl = document.getElementById('ContentEditor-MdRef-SearchCount');
|
|
746
|
+
if (!tmpCountEl)
|
|
747
|
+
{
|
|
748
|
+
return;
|
|
749
|
+
}
|
|
750
|
+
|
|
751
|
+
if (this._searchMatches.length === 0)
|
|
752
|
+
{
|
|
753
|
+
let tmpInput = document.getElementById('ContentEditor-MdRef-SearchInput');
|
|
754
|
+
if (tmpInput && tmpInput.value && tmpInput.value.length >= 2)
|
|
755
|
+
{
|
|
756
|
+
tmpCountEl.textContent = '0';
|
|
757
|
+
}
|
|
758
|
+
else
|
|
759
|
+
{
|
|
760
|
+
tmpCountEl.textContent = '';
|
|
761
|
+
}
|
|
762
|
+
}
|
|
763
|
+
else
|
|
764
|
+
{
|
|
765
|
+
tmpCountEl.textContent = (this._currentMatchIndex + 1) + '/' + this._searchMatches.length;
|
|
766
|
+
}
|
|
767
|
+
}
|
|
768
|
+
|
|
769
|
+
/**
|
|
770
|
+
* Enable/disable navigation buttons based on match count.
|
|
771
|
+
*/
|
|
772
|
+
_updateNavButtons()
|
|
773
|
+
{
|
|
774
|
+
let tmpPrev = document.getElementById('ContentEditor-MdRef-SearchPrev');
|
|
775
|
+
let tmpNext = document.getElementById('ContentEditor-MdRef-SearchNext');
|
|
776
|
+
let tmpHasMatches = this._searchMatches.length > 0;
|
|
777
|
+
|
|
778
|
+
if (tmpPrev) tmpPrev.disabled = !tmpHasMatches;
|
|
779
|
+
if (tmpNext) tmpNext.disabled = !tmpHasMatches;
|
|
780
|
+
}
|
|
781
|
+
|
|
782
|
+
/**
|
|
783
|
+
* Clear search highlights and reset state.
|
|
784
|
+
*/
|
|
785
|
+
_clearSearch()
|
|
786
|
+
{
|
|
787
|
+
let tmpContentEl = document.getElementById('ContentEditor-MdRef-Content');
|
|
788
|
+
if (tmpContentEl && this._originalContent)
|
|
789
|
+
{
|
|
790
|
+
tmpContentEl.innerHTML = this._originalContent;
|
|
791
|
+
}
|
|
792
|
+
this._searchMatches = [];
|
|
793
|
+
this._currentMatchIndex = -1;
|
|
794
|
+
this._updateSearchCount();
|
|
795
|
+
this._updateNavButtons();
|
|
796
|
+
}
|
|
797
|
+
}
|
|
798
|
+
|
|
799
|
+
module.exports = ContentEditorMarkdownReferenceView;
|
|
800
|
+
|
|
801
|
+
module.exports.default_configuration = _ViewConfiguration;
|