markymark 0.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.
- checksums.yaml +7 -0
- data/.rspec +3 -0
- data/CHANGELOG.md +29 -0
- data/LICENSE.txt +21 -0
- data/README.md +255 -0
- data/Rakefile +8 -0
- data/assets/.gitkeep +0 -0
- data/assets/Markymark.icns +0 -0
- data/assets/Markymark.iconset/icon_128x128.png +0 -0
- data/assets/Markymark.iconset/icon_128x128@2x.png +0 -0
- data/assets/Markymark.iconset/icon_16x16.png +0 -0
- data/assets/Markymark.iconset/icon_16x16@2x.png +0 -0
- data/assets/Markymark.iconset/icon_256x256.png +0 -0
- data/assets/Markymark.iconset/icon_256x256@2x.png +0 -0
- data/assets/Markymark.iconset/icon_32x32.png +0 -0
- data/assets/Markymark.iconset/icon_32x32@2x.png +0 -0
- data/assets/Markymark.iconset/icon_512x512.png +0 -0
- data/assets/Markymark.iconset/icon_512x512@2x.png +0 -0
- data/assets/README.md +3 -0
- data/assets/marky-mark-dj.jpg +0 -0
- data/assets/marky-mark-icon.png +0 -0
- data/assets/marky-mark-icon2.png +0 -0
- data/config.ru +19 -0
- data/docs/for_llms.md +141 -0
- data/docs/plans/2025-12-18-macos-app-installer-design.md +149 -0
- data/exe/markymark +5 -0
- data/lib/markymark/app_installer.rb +437 -0
- data/lib/markymark/cli.rb +497 -0
- data/lib/markymark/init_wizard.rb +186 -0
- data/lib/markymark/pumadev_manager.rb +194 -0
- data/lib/markymark/server_simple.rb +452 -0
- data/lib/markymark/version.rb +5 -0
- data/lib/markymark.rb +12 -0
- data/lib/public/css/style.css +350 -0
- data/lib/public/js/app.js +186 -0
- data/lib/public/js/theme.js +79 -0
- data/lib/public/js/tree.js +124 -0
- data/lib/views/browse.erb +225 -0
- data/lib/views/index.erb +37 -0
- data/lib/views/simple.erb +806 -0
- data/sig/markymark.rbs +4 -0
- metadata +242 -0
|
@@ -0,0 +1,806 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
+
<title>markymark - <%= @current_file || 'Select a file' %></title>
|
|
7
|
+
<script src="https://cdn.jsdelivr.net/npm/mermaid@10/dist/mermaid.min.js"></script>
|
|
8
|
+
<script>
|
|
9
|
+
// Initialize mermaid with theme detection
|
|
10
|
+
const isDark = localStorage.getItem('theme') === 'dark' ||
|
|
11
|
+
(!localStorage.getItem('theme') && window.matchMedia('(prefers-color-scheme: dark)').matches);
|
|
12
|
+
mermaid.initialize({ startOnLoad: true, theme: isDark ? 'dark' : 'default' });
|
|
13
|
+
</script>
|
|
14
|
+
<style>
|
|
15
|
+
:root {
|
|
16
|
+
/* Light theme colors */
|
|
17
|
+
--bg-primary: #ffffff;
|
|
18
|
+
--bg-secondary: #f6f8fa;
|
|
19
|
+
--bg-tertiary: #f6f8fa;
|
|
20
|
+
--border-color: #d0d7de;
|
|
21
|
+
--text-primary: #24292f;
|
|
22
|
+
--text-secondary: #57606a;
|
|
23
|
+
--text-link: #0969da;
|
|
24
|
+
--text-link-hover: #0860ca;
|
|
25
|
+
--bg-active: #ddf4ff;
|
|
26
|
+
--bg-hover: #eaeef2;
|
|
27
|
+
--bg-code: #f6f8fa;
|
|
28
|
+
--border-highlight: #0969da;
|
|
29
|
+
--mermaid-bg: #ffffff;
|
|
30
|
+
|
|
31
|
+
transition: background-color 0.3s ease, color 0.3s ease;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
[data-theme="dark"] {
|
|
35
|
+
/* Dark theme colors - GitHub dark dimmed palette */
|
|
36
|
+
--bg-primary: #22272e;
|
|
37
|
+
--bg-secondary: #2d333b;
|
|
38
|
+
--bg-tertiary: #373e47;
|
|
39
|
+
--border-color: #444c56;
|
|
40
|
+
--text-primary: #adbac7;
|
|
41
|
+
--text-secondary: #768390;
|
|
42
|
+
--text-link: #539bf5;
|
|
43
|
+
--text-link-hover: #6cb6ff;
|
|
44
|
+
--bg-active: #1c2128;
|
|
45
|
+
--bg-hover: #373e47;
|
|
46
|
+
--bg-code: #2d333b;
|
|
47
|
+
--border-highlight: #539bf5;
|
|
48
|
+
--mermaid-bg: #2d333b;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
* {
|
|
52
|
+
margin: 0;
|
|
53
|
+
padding: 0;
|
|
54
|
+
box-sizing: border-box;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
body {
|
|
58
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Helvetica', 'Arial', sans-serif;
|
|
59
|
+
display: flex;
|
|
60
|
+
flex-direction: column;
|
|
61
|
+
height: 100vh;
|
|
62
|
+
overflow: hidden;
|
|
63
|
+
background-color: var(--bg-primary);
|
|
64
|
+
color: var(--text-primary);
|
|
65
|
+
transition: background-color 0.3s ease, color 0.3s ease;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
.app-header {
|
|
69
|
+
display: flex;
|
|
70
|
+
justify-content: space-between;
|
|
71
|
+
align-items: center;
|
|
72
|
+
padding: 12px 16px;
|
|
73
|
+
background-color: var(--bg-primary);
|
|
74
|
+
border-bottom: 1px solid var(--border-color);
|
|
75
|
+
flex-shrink: 0;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
.app-branding {
|
|
79
|
+
display: flex;
|
|
80
|
+
align-items: center;
|
|
81
|
+
gap: 12px;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
.app-icon {
|
|
85
|
+
width: 32px;
|
|
86
|
+
height: 32px;
|
|
87
|
+
border-radius: 4px;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
.app-name {
|
|
91
|
+
font-size: 18px;
|
|
92
|
+
font-weight: 600;
|
|
93
|
+
color: var(--text-primary);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
.main-container {
|
|
97
|
+
display: flex;
|
|
98
|
+
flex: 1;
|
|
99
|
+
overflow: hidden;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
.sidebar {
|
|
103
|
+
width: 300px;
|
|
104
|
+
background-color: var(--bg-secondary);
|
|
105
|
+
border-right: 1px solid var(--border-color);
|
|
106
|
+
overflow-y: auto;
|
|
107
|
+
flex-shrink: 0;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
.sidebar-header {
|
|
111
|
+
padding: 16px;
|
|
112
|
+
border-bottom: 1px solid var(--border-color);
|
|
113
|
+
background-color: var(--bg-primary);
|
|
114
|
+
font-weight: 600;
|
|
115
|
+
font-size: 14px;
|
|
116
|
+
color: var(--text-primary);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
.theme-toggle {
|
|
120
|
+
background: none;
|
|
121
|
+
border: none;
|
|
122
|
+
cursor: pointer;
|
|
123
|
+
font-size: 20px;
|
|
124
|
+
padding: 4px 8px;
|
|
125
|
+
border-radius: 6px;
|
|
126
|
+
transition: background-color 0.2s;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
.theme-toggle:hover {
|
|
130
|
+
background-color: var(--bg-hover);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
.root-path {
|
|
134
|
+
padding: 12px 16px;
|
|
135
|
+
background-color: var(--bg-secondary);
|
|
136
|
+
border-bottom: 1px solid var(--border-color);
|
|
137
|
+
font-size: 12px;
|
|
138
|
+
color: var(--text-secondary);
|
|
139
|
+
font-family: ui-monospace, SFMono-Regular, 'SF Mono', Menlo, Consolas, monospace;
|
|
140
|
+
overflow-x: auto;
|
|
141
|
+
white-space: nowrap;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
.root-path strong {
|
|
145
|
+
color: var(--text-primary);
|
|
146
|
+
font-weight: 600;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
.change-dir {
|
|
150
|
+
padding: 12px 16px;
|
|
151
|
+
background-color: var(--bg-primary);
|
|
152
|
+
border-bottom: 1px solid var(--border-color);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
.change-dir form {
|
|
156
|
+
display: flex;
|
|
157
|
+
flex-direction: column;
|
|
158
|
+
gap: 8px;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
.change-dir input[type="text"] {
|
|
162
|
+
width: 100%;
|
|
163
|
+
padding: 6px 8px;
|
|
164
|
+
border: 1px solid var(--border-color);
|
|
165
|
+
border-radius: 6px;
|
|
166
|
+
font-size: 12px;
|
|
167
|
+
font-family: ui-monospace, SFMono-Regular, 'SF Mono', Menlo, Consolas, monospace;
|
|
168
|
+
overflow-x: auto;
|
|
169
|
+
white-space: nowrap;
|
|
170
|
+
background-color: var(--bg-primary);
|
|
171
|
+
color: var(--text-primary);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
.change-dir-buttons {
|
|
175
|
+
display: flex;
|
|
176
|
+
gap: 8px;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
.change-dir button {
|
|
180
|
+
flex: 1;
|
|
181
|
+
padding: 6px 12px;
|
|
182
|
+
background-color: var(--text-link);
|
|
183
|
+
color: #ffffff;
|
|
184
|
+
border: none;
|
|
185
|
+
border-radius: 6px;
|
|
186
|
+
font-size: 12px;
|
|
187
|
+
font-weight: 600;
|
|
188
|
+
cursor: pointer;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
.change-dir button:hover {
|
|
192
|
+
background-color: var(--text-link-hover);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
.change-dir .browse-btn {
|
|
196
|
+
background-color: var(--text-secondary);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
.change-dir .browse-btn:hover {
|
|
200
|
+
background-color: var(--text-secondary);
|
|
201
|
+
opacity: 0.8;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
.file-list {
|
|
205
|
+
list-style: none;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
.file-list li {
|
|
209
|
+
border-bottom: 1px solid var(--border-color);
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
.file-list a {
|
|
213
|
+
display: block;
|
|
214
|
+
padding: 12px 16px;
|
|
215
|
+
text-decoration: none;
|
|
216
|
+
color: var(--text-link);
|
|
217
|
+
font-size: 14px;
|
|
218
|
+
transition: background-color 0.2s;
|
|
219
|
+
word-break: break-word;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
.file-list a:hover {
|
|
223
|
+
background-color: var(--bg-hover);
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
.file-list a.active {
|
|
227
|
+
background-color: var(--bg-active);
|
|
228
|
+
font-weight: 600;
|
|
229
|
+
border-left: 3px solid var(--border-highlight);
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
/* Accordion controls */
|
|
233
|
+
.accordion-controls {
|
|
234
|
+
display: flex;
|
|
235
|
+
gap: 8px;
|
|
236
|
+
margin-top: 8px;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
.accordion-control-btn {
|
|
240
|
+
padding: 4px 8px;
|
|
241
|
+
font-size: 11px;
|
|
242
|
+
background-color: var(--bg-secondary);
|
|
243
|
+
border: 1px solid var(--border-color);
|
|
244
|
+
border-radius: 4px;
|
|
245
|
+
cursor: pointer;
|
|
246
|
+
color: var(--text-primary);
|
|
247
|
+
transition: background-color 0.2s;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
.accordion-control-btn:hover {
|
|
251
|
+
background-color: var(--bg-hover);
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
/* Directory accordion */
|
|
255
|
+
.file-tree {
|
|
256
|
+
margin-top: 12px;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
.directory-section {
|
|
260
|
+
margin-bottom: 4px;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
.directory-header {
|
|
264
|
+
display: flex;
|
|
265
|
+
align-items: center;
|
|
266
|
+
padding: 8px 12px;
|
|
267
|
+
cursor: pointer;
|
|
268
|
+
background-color: var(--bg-secondary);
|
|
269
|
+
border: 1px solid var(--border-color);
|
|
270
|
+
border-radius: 4px;
|
|
271
|
+
transition: background-color 0.2s;
|
|
272
|
+
user-select: none;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
.directory-header:hover {
|
|
276
|
+
background-color: var(--bg-hover);
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
.directory-arrow {
|
|
280
|
+
display: inline-block;
|
|
281
|
+
width: 16px;
|
|
282
|
+
font-size: 12px;
|
|
283
|
+
transition: transform 0.2s;
|
|
284
|
+
color: var(--text-secondary);
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
.directory-name {
|
|
288
|
+
flex: 1;
|
|
289
|
+
font-size: 13px;
|
|
290
|
+
font-weight: 600;
|
|
291
|
+
color: var(--text-primary);
|
|
292
|
+
margin-left: 4px;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
.file-count {
|
|
296
|
+
font-size: 11px;
|
|
297
|
+
color: var(--text-secondary);
|
|
298
|
+
margin-left: 8px;
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
.directory-files {
|
|
302
|
+
list-style: none;
|
|
303
|
+
margin: 0;
|
|
304
|
+
padding: 0;
|
|
305
|
+
border-left: 2px solid var(--border-color);
|
|
306
|
+
margin-left: 12px;
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
.directory-files li {
|
|
310
|
+
border-bottom: 1px solid var(--border-color);
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
.directory-files li:last-child {
|
|
314
|
+
border-bottom: none;
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
.directory-files a {
|
|
318
|
+
display: block;
|
|
319
|
+
padding: 8px 12px;
|
|
320
|
+
text-decoration: none;
|
|
321
|
+
color: var(--text-link);
|
|
322
|
+
font-size: 13px;
|
|
323
|
+
transition: background-color 0.2s;
|
|
324
|
+
word-break: break-word;
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
.directory-files a:hover {
|
|
328
|
+
background-color: var(--bg-hover);
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
.directory-files a.active {
|
|
332
|
+
background-color: var(--bg-active);
|
|
333
|
+
font-weight: 600;
|
|
334
|
+
border-left: 3px solid var(--border-highlight);
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
.content {
|
|
338
|
+
flex: 1;
|
|
339
|
+
overflow-y: auto;
|
|
340
|
+
padding: 32px;
|
|
341
|
+
background-color: var(--bg-primary);
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
.markdown-body {
|
|
345
|
+
max-width: 980px;
|
|
346
|
+
margin: 0 auto;
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
/* Markdown styling */
|
|
350
|
+
.markdown-body h1, .markdown-body h2, .markdown-body h3,
|
|
351
|
+
.markdown-body h4, .markdown-body h5, .markdown-body h6 {
|
|
352
|
+
margin-top: 24px;
|
|
353
|
+
margin-bottom: 16px;
|
|
354
|
+
font-weight: 600;
|
|
355
|
+
line-height: 1.25;
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
.markdown-body h1 {
|
|
359
|
+
font-size: 2em;
|
|
360
|
+
border-bottom: 1px solid var(--border-color);
|
|
361
|
+
padding-bottom: 0.3em;
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
.markdown-body h2 {
|
|
365
|
+
font-size: 1.5em;
|
|
366
|
+
border-bottom: 1px solid var(--border-color);
|
|
367
|
+
padding-bottom: 0.3em;
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
.markdown-body h3 {
|
|
371
|
+
font-size: 1.25em;
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
.markdown-body p {
|
|
375
|
+
margin-bottom: 16px;
|
|
376
|
+
line-height: 1.6;
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
.markdown-body ul, .markdown-body ol {
|
|
380
|
+
margin-bottom: 16px;
|
|
381
|
+
padding-left: 2em;
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
.markdown-body li {
|
|
385
|
+
margin-top: 0.25em;
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
.markdown-body code {
|
|
389
|
+
background-color: var(--bg-code);
|
|
390
|
+
padding: 0.2em 0.4em;
|
|
391
|
+
border-radius: 6px;
|
|
392
|
+
font-family: ui-monospace, SFMono-Regular, 'SF Mono', Menlo, Consolas, 'Liberation Mono', monospace;
|
|
393
|
+
font-size: 85%;
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
.markdown-body pre {
|
|
397
|
+
background-color: var(--bg-code);
|
|
398
|
+
padding: 16px;
|
|
399
|
+
border-radius: 6px;
|
|
400
|
+
overflow: auto;
|
|
401
|
+
margin-bottom: 16px;
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
.markdown-body pre code {
|
|
405
|
+
background-color: transparent;
|
|
406
|
+
padding: 0;
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
.markdown-body blockquote {
|
|
410
|
+
padding: 0 1em;
|
|
411
|
+
color: var(--text-secondary);
|
|
412
|
+
border-left: 0.25em solid var(--border-color);
|
|
413
|
+
margin-bottom: 16px;
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
.markdown-body table {
|
|
417
|
+
border-collapse: collapse;
|
|
418
|
+
margin-bottom: 16px;
|
|
419
|
+
width: 100%;
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
.markdown-body table th,
|
|
423
|
+
.markdown-body table td {
|
|
424
|
+
padding: 6px 13px;
|
|
425
|
+
border: 1px solid var(--border-color);
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
.markdown-body table th {
|
|
429
|
+
font-weight: 600;
|
|
430
|
+
background-color: var(--bg-code);
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
.markdown-body table tr:nth-child(2n) {
|
|
434
|
+
background-color: var(--bg-code);
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
.markdown-body a {
|
|
438
|
+
color: var(--text-link);
|
|
439
|
+
text-decoration: none;
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
.markdown-body a:hover {
|
|
443
|
+
text-decoration: underline;
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
.markdown-body img {
|
|
447
|
+
max-width: 100%;
|
|
448
|
+
height: auto;
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
/* Mermaid diagram styling */
|
|
452
|
+
.markdown-body .mermaid {
|
|
453
|
+
background-color: var(--bg-primary);
|
|
454
|
+
border: 1px solid var(--border-color);
|
|
455
|
+
border-radius: 6px;
|
|
456
|
+
padding: 16px;
|
|
457
|
+
margin-bottom: 16px;
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
.empty-state {
|
|
461
|
+
text-align: center;
|
|
462
|
+
padding: 64px 32px;
|
|
463
|
+
color: var(--text-secondary);
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
.empty-state h2 {
|
|
467
|
+
margin-bottom: 16px;
|
|
468
|
+
color: var(--text-primary);
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
/* Bookmarks styling */
|
|
472
|
+
.bookmarks-section {
|
|
473
|
+
border-bottom: 1px solid var(--border-color);
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
.bookmarks-list {
|
|
477
|
+
list-style: none;
|
|
478
|
+
margin: 0;
|
|
479
|
+
padding: 0;
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
.bookmarks-list li {
|
|
483
|
+
border-bottom: 1px solid var(--border-color);
|
|
484
|
+
display: flex;
|
|
485
|
+
align-items: center;
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
.bookmarks-list li:last-child {
|
|
489
|
+
border-bottom: none;
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
.bookmark-btn {
|
|
493
|
+
width: 100%;
|
|
494
|
+
padding: 12px 16px;
|
|
495
|
+
background: none;
|
|
496
|
+
border: none;
|
|
497
|
+
text-align: left;
|
|
498
|
+
color: var(--text-link);
|
|
499
|
+
font-size: 14px;
|
|
500
|
+
cursor: pointer;
|
|
501
|
+
transition: background-color 0.2s;
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
.shortcut-btn:hover, .bookmark-btn:hover {
|
|
505
|
+
background-color: var(--bg-hover);
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
.bookmark-btn {
|
|
509
|
+
text-overflow: ellipsis;
|
|
510
|
+
overflow: hidden;
|
|
511
|
+
white-space: nowrap;
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
.add-bookmark-btn {
|
|
515
|
+
background: var(--text-link);
|
|
516
|
+
color: #ffffff;
|
|
517
|
+
border: none;
|
|
518
|
+
border-radius: 4px;
|
|
519
|
+
width: 24px;
|
|
520
|
+
height: 24px;
|
|
521
|
+
font-size: 18px;
|
|
522
|
+
line-height: 1;
|
|
523
|
+
cursor: pointer;
|
|
524
|
+
padding: 0;
|
|
525
|
+
display: flex;
|
|
526
|
+
align-items: center;
|
|
527
|
+
justify-content: center;
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
.add-bookmark-btn:hover {
|
|
531
|
+
background-color: var(--text-link-hover);
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
.remove-bookmark-btn {
|
|
535
|
+
background: var(--text-secondary);
|
|
536
|
+
color: #ffffff;
|
|
537
|
+
border: none;
|
|
538
|
+
border-radius: 4px;
|
|
539
|
+
width: 24px;
|
|
540
|
+
height: 24px;
|
|
541
|
+
font-size: 18px;
|
|
542
|
+
line-height: 1;
|
|
543
|
+
cursor: pointer;
|
|
544
|
+
padding: 0;
|
|
545
|
+
margin-left: 4px;
|
|
546
|
+
margin-right: 8px;
|
|
547
|
+
display: flex;
|
|
548
|
+
align-items: center;
|
|
549
|
+
justify-content: center;
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
.remove-bookmark-btn:hover {
|
|
553
|
+
background-color: #d73a49;
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
.empty-bookmarks {
|
|
557
|
+
padding: 12px 16px;
|
|
558
|
+
color: var(--text-secondary);
|
|
559
|
+
font-size: 13px;
|
|
560
|
+
font-style: italic;
|
|
561
|
+
border-bottom: none;
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
/* Syntax highlighting - Rouge GitHub theme for light mode */
|
|
565
|
+
<%= Rouge::Themes::Github.render(scope: '.highlight') %>
|
|
566
|
+
|
|
567
|
+
/* Syntax highlighting - Rouge dark theme for dark mode */
|
|
568
|
+
<%= Rouge::Themes::Base16.mode(:dark).render(scope: '[data-theme="dark"] .highlight') %>
|
|
569
|
+
</style>
|
|
570
|
+
</head>
|
|
571
|
+
<body>
|
|
572
|
+
<div class="app-header">
|
|
573
|
+
<div class="app-branding">
|
|
574
|
+
<img src="/assets/marky-mark-icon2.png" alt="markymark" class="app-icon">
|
|
575
|
+
<span class="app-name">markymark</span>
|
|
576
|
+
</div>
|
|
577
|
+
<button class="theme-toggle" id="theme-toggle" aria-label="Toggle dark mode">
|
|
578
|
+
<span id="theme-icon">🌙</span>
|
|
579
|
+
</button>
|
|
580
|
+
</div>
|
|
581
|
+
|
|
582
|
+
<div class="main-container">
|
|
583
|
+
<div class="sidebar">
|
|
584
|
+
<div class="change-dir">
|
|
585
|
+
<form action="/change-dir" method="post">
|
|
586
|
+
<input type="text" name="path" placeholder="Enter directory path..." value="<%= self.class.root_path %>" id="dir-path">
|
|
587
|
+
<div class="change-dir-buttons">
|
|
588
|
+
<button type="button" class="browse-btn" onclick="window.location.href='/browse-dir?dir=<%= CGI.escape(@current_dir) %>'">Browse...</button>
|
|
589
|
+
<button type="submit">Go</button>
|
|
590
|
+
</div>
|
|
591
|
+
</form>
|
|
592
|
+
</div>
|
|
593
|
+
|
|
594
|
+
<div class="bookmarks-section">
|
|
595
|
+
<div class="sidebar-header" style="display: flex; justify-content: space-between; align-items: center;">
|
|
596
|
+
<span>Bookmarks</span>
|
|
597
|
+
<button type="button" class="add-bookmark-btn" onclick="addBookmark()" title="Add current directory">+</button>
|
|
598
|
+
</div>
|
|
599
|
+
<ul class="bookmarks-list">
|
|
600
|
+
<% if @bookmarks && @bookmarks.any? %>
|
|
601
|
+
<% @bookmarks.each_with_index do |bookmark, index| %>
|
|
602
|
+
<li>
|
|
603
|
+
<form action="/change-dir" method="post" style="margin: 0; flex: 1;">
|
|
604
|
+
<input type="hidden" name="path" value="<%= bookmark['path'] %>">
|
|
605
|
+
<button type="submit" class="bookmark-btn" title="<%= bookmark['path'] %>">
|
|
606
|
+
📌 <%= bookmark['name'] %>
|
|
607
|
+
</button>
|
|
608
|
+
</form>
|
|
609
|
+
<form action="/bookmark/<%= index %>" method="post" style="margin: 0;" onsubmit="return confirm('Remove this bookmark?');">
|
|
610
|
+
<input type="hidden" name="_method" value="delete">
|
|
611
|
+
<button type="submit" class="remove-bookmark-btn" title="Remove bookmark">×</button>
|
|
612
|
+
</form>
|
|
613
|
+
</li>
|
|
614
|
+
<% end %>
|
|
615
|
+
<% else %>
|
|
616
|
+
<li class="empty-bookmarks">No bookmarks yet</li>
|
|
617
|
+
<% end %>
|
|
618
|
+
</ul>
|
|
619
|
+
</div>
|
|
620
|
+
|
|
621
|
+
<div class="sidebar-header">
|
|
622
|
+
<span>Files</span>
|
|
623
|
+
<div class="accordion-controls">
|
|
624
|
+
<button type="button" class="accordion-control-btn" onclick="expandAllDirectories()">Expand All</button>
|
|
625
|
+
<button type="button" class="accordion-control-btn" onclick="collapseAllDirectories()">Collapse All</button>
|
|
626
|
+
</div>
|
|
627
|
+
</div>
|
|
628
|
+
<div class="file-tree">
|
|
629
|
+
<% @files_grouped.each do |dir, files| %>
|
|
630
|
+
<% dir_id = "dir-" + dir.gsub(/[^a-zA-Z0-9]/, '-') %>
|
|
631
|
+
<% is_expanded = dir == "." %>
|
|
632
|
+
<div class="directory-section">
|
|
633
|
+
<div class="directory-header" data-dir="<%= dir %>" onclick="toggleDirectory(this)">
|
|
634
|
+
<span class="directory-arrow"><%= is_expanded ? '▼' : '►' %></span>
|
|
635
|
+
<span class="directory-name"><%= dir == "." ? "📄 Root" : "📁 #{dir}" %></span>
|
|
636
|
+
<span class="file-count">(<%= files.length %>)</span>
|
|
637
|
+
</div>
|
|
638
|
+
<ul class="directory-files" style="<%= is_expanded ? '' : 'display: none;' %>">
|
|
639
|
+
<% files.each do |file| %>
|
|
640
|
+
<% full_path = dir == "." ? file : File.join(dir, file) %>
|
|
641
|
+
<li>
|
|
642
|
+
<a href="/?file=<%= CGI.escape(full_path) %>&dir=<%= CGI.escape(@current_dir) %>" class="<%= full_path == @current_file ? 'active' : '' %>">
|
|
643
|
+
<%= file %>
|
|
644
|
+
</a>
|
|
645
|
+
</li>
|
|
646
|
+
<% end %>
|
|
647
|
+
</ul>
|
|
648
|
+
</div>
|
|
649
|
+
<% end %>
|
|
650
|
+
</div>
|
|
651
|
+
</div>
|
|
652
|
+
|
|
653
|
+
<div class="content">
|
|
654
|
+
<% if @html_content %>
|
|
655
|
+
<div class="markdown-body">
|
|
656
|
+
<%= @html_content %>
|
|
657
|
+
</div>
|
|
658
|
+
<% else %>
|
|
659
|
+
<div class="empty-state">
|
|
660
|
+
<h2>No Markdown Files Found</h2>
|
|
661
|
+
<p>Add .md or .markdown files to the directory to get started.</p>
|
|
662
|
+
</div>
|
|
663
|
+
<% end %>
|
|
664
|
+
</div>
|
|
665
|
+
</div>
|
|
666
|
+
|
|
667
|
+
<script>
|
|
668
|
+
// Theme toggle functionality
|
|
669
|
+
(function() {
|
|
670
|
+
const html = document.documentElement;
|
|
671
|
+
const themeToggle = document.getElementById('theme-toggle');
|
|
672
|
+
const themeIcon = document.getElementById('theme-icon');
|
|
673
|
+
|
|
674
|
+
// Get initial theme from localStorage or system preference
|
|
675
|
+
function getInitialTheme() {
|
|
676
|
+
const stored = localStorage.getItem('theme');
|
|
677
|
+
if (stored) return stored;
|
|
678
|
+
return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
|
|
679
|
+
}
|
|
680
|
+
|
|
681
|
+
// Apply theme
|
|
682
|
+
function applyTheme(theme) {
|
|
683
|
+
if (theme === 'dark') {
|
|
684
|
+
html.setAttribute('data-theme', 'dark');
|
|
685
|
+
themeIcon.textContent = '☀️';
|
|
686
|
+
} else {
|
|
687
|
+
html.removeAttribute('data-theme');
|
|
688
|
+
themeIcon.textContent = '🌙';
|
|
689
|
+
}
|
|
690
|
+
}
|
|
691
|
+
|
|
692
|
+
// Initialize theme
|
|
693
|
+
const currentTheme = getInitialTheme();
|
|
694
|
+
applyTheme(currentTheme);
|
|
695
|
+
|
|
696
|
+
// Toggle theme on button click
|
|
697
|
+
themeToggle.addEventListener('click', function() {
|
|
698
|
+
const newTheme = html.hasAttribute('data-theme') ? 'light' : 'dark';
|
|
699
|
+
localStorage.setItem('theme', newTheme);
|
|
700
|
+
|
|
701
|
+
// Reload page to re-render mermaid diagrams with new theme
|
|
702
|
+
window.location.reload();
|
|
703
|
+
});
|
|
704
|
+
})();
|
|
705
|
+
|
|
706
|
+
// Bookmark management
|
|
707
|
+
function addBookmark() {
|
|
708
|
+
const currentPath = document.getElementById('dir-path').value;
|
|
709
|
+
const name = prompt('Enter a name for this bookmark:', currentPath.split('/').pop() || 'Home');
|
|
710
|
+
|
|
711
|
+
if (name && name.trim()) {
|
|
712
|
+
const form = document.createElement('form');
|
|
713
|
+
form.method = 'POST';
|
|
714
|
+
form.action = '/bookmark';
|
|
715
|
+
|
|
716
|
+
const nameInput = document.createElement('input');
|
|
717
|
+
nameInput.type = 'hidden';
|
|
718
|
+
nameInput.name = 'name';
|
|
719
|
+
nameInput.value = name.trim();
|
|
720
|
+
|
|
721
|
+
const pathInput = document.createElement('input');
|
|
722
|
+
pathInput.type = 'hidden';
|
|
723
|
+
pathInput.name = 'path';
|
|
724
|
+
pathInput.value = currentPath;
|
|
725
|
+
|
|
726
|
+
form.appendChild(nameInput);
|
|
727
|
+
form.appendChild(pathInput);
|
|
728
|
+
document.body.appendChild(form);
|
|
729
|
+
form.submit();
|
|
730
|
+
}
|
|
731
|
+
}
|
|
732
|
+
|
|
733
|
+
// Directory accordion functions
|
|
734
|
+
function toggleDirectory(header) {
|
|
735
|
+
const section = header.parentElement;
|
|
736
|
+
const filesList = section.querySelector('.directory-files');
|
|
737
|
+
const arrow = header.querySelector('.directory-arrow');
|
|
738
|
+
const dir = header.getAttribute('data-dir');
|
|
739
|
+
|
|
740
|
+
const isExpanded = filesList.style.display !== 'none';
|
|
741
|
+
|
|
742
|
+
// Toggle visibility
|
|
743
|
+
filesList.style.display = isExpanded ? 'none' : '';
|
|
744
|
+
arrow.textContent = isExpanded ? '►' : '▼';
|
|
745
|
+
|
|
746
|
+
// Save state to localStorage
|
|
747
|
+
const key = 'markymark_dir_' + dir.replace(/[^a-zA-Z0-9]/g, '_');
|
|
748
|
+
localStorage.setItem(key, isExpanded ? 'collapsed' : 'expanded');
|
|
749
|
+
}
|
|
750
|
+
|
|
751
|
+
function expandAllDirectories() {
|
|
752
|
+
document.querySelectorAll('.directory-section').forEach(function(section) {
|
|
753
|
+
const filesList = section.querySelector('.directory-files');
|
|
754
|
+
const arrow = section.querySelector('.directory-arrow');
|
|
755
|
+
const header = section.querySelector('.directory-header');
|
|
756
|
+
const dir = header.getAttribute('data-dir');
|
|
757
|
+
|
|
758
|
+
filesList.style.display = '';
|
|
759
|
+
arrow.textContent = '▼';
|
|
760
|
+
|
|
761
|
+
// Save state
|
|
762
|
+
const key = 'markymark_dir_' + dir.replace(/[^a-zA-Z0-9]/g, '_');
|
|
763
|
+
localStorage.setItem(key, 'expanded');
|
|
764
|
+
});
|
|
765
|
+
}
|
|
766
|
+
|
|
767
|
+
function collapseAllDirectories() {
|
|
768
|
+
document.querySelectorAll('.directory-section').forEach(function(section) {
|
|
769
|
+
const filesList = section.querySelector('.directory-files');
|
|
770
|
+
const arrow = section.querySelector('.directory-arrow');
|
|
771
|
+
const header = section.querySelector('.directory-header');
|
|
772
|
+
const dir = header.getAttribute('data-dir');
|
|
773
|
+
|
|
774
|
+
filesList.style.display = 'none';
|
|
775
|
+
arrow.textContent = '►';
|
|
776
|
+
|
|
777
|
+
// Save state
|
|
778
|
+
const key = 'markymark_dir_' + dir.replace(/[^a-zA-Z0-9]/g, '_');
|
|
779
|
+
localStorage.setItem(key, 'collapsed');
|
|
780
|
+
});
|
|
781
|
+
}
|
|
782
|
+
|
|
783
|
+
// Restore directory states from localStorage on page load
|
|
784
|
+
document.addEventListener('DOMContentLoaded', function() {
|
|
785
|
+
document.querySelectorAll('.directory-section').forEach(function(section) {
|
|
786
|
+
const header = section.querySelector('.directory-header');
|
|
787
|
+
const filesList = section.querySelector('.directory-files');
|
|
788
|
+
const arrow = section.querySelector('.directory-arrow');
|
|
789
|
+
const dir = header.getAttribute('data-dir');
|
|
790
|
+
|
|
791
|
+
const key = 'markymark_dir_' + dir.replace(/[^a-zA-Z0-9]/g, '_');
|
|
792
|
+
const state = localStorage.getItem(key);
|
|
793
|
+
|
|
794
|
+
if (state === 'collapsed') {
|
|
795
|
+
filesList.style.display = 'none';
|
|
796
|
+
arrow.textContent = '►';
|
|
797
|
+
} else if (state === 'expanded') {
|
|
798
|
+
filesList.style.display = '';
|
|
799
|
+
arrow.textContent = '▼';
|
|
800
|
+
}
|
|
801
|
+
// If no state in localStorage, use default from template (root expanded, others collapsed)
|
|
802
|
+
});
|
|
803
|
+
});
|
|
804
|
+
</script>
|
|
805
|
+
</body>
|
|
806
|
+
</html>
|