@jjlmoya/utils-tabletop 1.20.0 → 1.22.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 (80) hide show
  1. package/package.json +1 -1
  2. package/src/category/index.ts +2 -0
  3. package/src/entries.ts +4 -0
  4. package/src/index.ts +1 -0
  5. package/src/tests/locale_completeness.test.ts +1 -1
  6. package/src/tests/seo_translation_completeness.test.ts +69 -0
  7. package/src/tests/spanish_leakage.test.ts +25 -3
  8. package/src/tests/tool_validation.test.ts +1 -1
  9. package/src/tool/decision-wheel/i18n/de.ts +10 -10
  10. package/src/tool/decision-wheel/i18n/es.ts +10 -10
  11. package/src/tool/decision-wheel/i18n/fr.ts +10 -10
  12. package/src/tool/decision-wheel/i18n/id.ts +10 -10
  13. package/src/tool/decision-wheel/i18n/it.ts +10 -10
  14. package/src/tool/decision-wheel/i18n/ja.ts +10 -10
  15. package/src/tool/decision-wheel/i18n/ko.ts +10 -10
  16. package/src/tool/decision-wheel/i18n/nl.ts +10 -10
  17. package/src/tool/decision-wheel/i18n/pl.ts +10 -10
  18. package/src/tool/decision-wheel/i18n/pt.ts +10 -10
  19. package/src/tool/decision-wheel/i18n/sv.ts +10 -10
  20. package/src/tool/decision-wheel/i18n/tr.ts +10 -10
  21. package/src/tool/decision-wheel/i18n/zh.ts +10 -10
  22. package/src/tool/dungeon-map-generator/bibliography.astro +16 -0
  23. package/src/tool/dungeon-map-generator/bibliography.ts +12 -0
  24. package/src/tool/dungeon-map-generator/component.astro +134 -0
  25. package/src/tool/dungeon-map-generator/controller.ts +242 -0
  26. package/src/tool/dungeon-map-generator/document.ts +72 -0
  27. package/src/tool/dungeon-map-generator/dom-views.ts +139 -0
  28. package/src/tool/dungeon-map-generator/editor.ts +57 -0
  29. package/src/tool/dungeon-map-generator/entry.ts +28 -0
  30. package/src/tool/dungeon-map-generator/evaluator.ts +40 -0
  31. package/src/tool/dungeon-map-generator/i18n/de.ts +172 -0
  32. package/src/tool/dungeon-map-generator/i18n/en.ts +172 -0
  33. package/src/tool/dungeon-map-generator/i18n/es.ts +172 -0
  34. package/src/tool/dungeon-map-generator/i18n/fr.ts +172 -0
  35. package/src/tool/dungeon-map-generator/i18n/id.ts +172 -0
  36. package/src/tool/dungeon-map-generator/i18n/it.ts +172 -0
  37. package/src/tool/dungeon-map-generator/i18n/ja.ts +172 -0
  38. package/src/tool/dungeon-map-generator/i18n/ko.ts +172 -0
  39. package/src/tool/dungeon-map-generator/i18n/nl.ts +172 -0
  40. package/src/tool/dungeon-map-generator/i18n/pl.ts +172 -0
  41. package/src/tool/dungeon-map-generator/i18n/pt.ts +172 -0
  42. package/src/tool/dungeon-map-generator/i18n/ru.ts +172 -0
  43. package/src/tool/dungeon-map-generator/i18n/sv.ts +172 -0
  44. package/src/tool/dungeon-map-generator/i18n/tr.ts +172 -0
  45. package/src/tool/dungeon-map-generator/i18n/zh.ts +172 -0
  46. package/src/tool/dungeon-map-generator/index.ts +11 -0
  47. package/src/tool/dungeon-map-generator/logic.test.ts +75 -0
  48. package/src/tool/dungeon-map-generator/logic.ts +255 -0
  49. package/src/tool/dungeon-map-generator/random-dungeon-map-generator.css +678 -0
  50. package/src/tool/dungeon-map-generator/seo.astro +16 -0
  51. package/src/tool/dungeon-map-generator/storage.ts +18 -0
  52. package/src/tool/dungeon-map-generator/ui.ts +53 -0
  53. package/src/tool/dungeon-map-generator/validation.ts +40 -0
  54. package/src/tool/hidden-role-dealer/i18n/de.ts +6 -6
  55. package/src/tool/hidden-role-dealer/i18n/id.ts +6 -6
  56. package/src/tool/hidden-role-dealer/i18n/it.ts +6 -6
  57. package/src/tool/hidden-role-dealer/i18n/ja.ts +6 -6
  58. package/src/tool/hidden-role-dealer/i18n/ko.ts +6 -6
  59. package/src/tool/hidden-role-dealer/i18n/nl.ts +6 -6
  60. package/src/tool/hidden-role-dealer/i18n/pl.ts +7 -7
  61. package/src/tool/hidden-role-dealer/i18n/pt.ts +7 -7
  62. package/src/tool/hidden-role-dealer/i18n/ru.ts +7 -7
  63. package/src/tool/hidden-role-dealer/i18n/sv.ts +7 -7
  64. package/src/tool/hidden-role-dealer/i18n/tr.ts +7 -7
  65. package/src/tool/hidden-role-dealer/i18n/zh.ts +9 -9
  66. package/src/tool/scatter-direction-selector/i18n/de.ts +9 -9
  67. package/src/tool/scatter-direction-selector/i18n/es.ts +9 -9
  68. package/src/tool/scatter-direction-selector/i18n/fr.ts +12 -12
  69. package/src/tool/scatter-direction-selector/i18n/id.ts +9 -9
  70. package/src/tool/scatter-direction-selector/i18n/it.ts +11 -11
  71. package/src/tool/scatter-direction-selector/i18n/ja.ts +8 -8
  72. package/src/tool/scatter-direction-selector/i18n/ko.ts +8 -8
  73. package/src/tool/scatter-direction-selector/i18n/nl.ts +9 -10
  74. package/src/tool/scatter-direction-selector/i18n/pl.ts +9 -9
  75. package/src/tool/scatter-direction-selector/i18n/pt.ts +12 -12
  76. package/src/tool/scatter-direction-selector/i18n/ru.ts +7 -7
  77. package/src/tool/scatter-direction-selector/i18n/sv.ts +9 -9
  78. package/src/tool/scatter-direction-selector/i18n/tr.ts +8 -8
  79. package/src/tool/scatter-direction-selector/i18n/zh.ts +11 -11
  80. package/src/tools.ts +2 -0
@@ -0,0 +1,678 @@
1
+ :root {
2
+ --n-dmg-canvas: #eee8dc;
3
+ --n-dmg-panel: #fffaf0;
4
+ --n-dmg-workbench: #f4eddf;
5
+ --n-dmg-ink: #242a2f;
6
+ --n-dmg-muted: #626a6f;
7
+ --n-dmg-line: #c9bca5;
8
+ --n-dmg-accent: #963d29;
9
+ --n-dmg-accent-hover: #78301f;
10
+ --n-dmg-accent-soft: #f1d4c7;
11
+ --n-dmg-gold: #b08434;
12
+ --n-dmg-wall: #202831;
13
+ --n-dmg-floor: #ead9b7;
14
+ --n-dmg-door: #b45532;
15
+ --n-dmg-grid: #8b7e6b;
16
+ --n-dmg-white: #fff;
17
+ --n-dmg-shadow: rgba(40, 34, 26, 0.18);
18
+ --n-dmg-focus: #176da8;
19
+ --n-dmg-cavern-wall: #18251e;
20
+ --n-dmg-cavern-floor: #cbd5b2;
21
+ --n-dmg-cavern-grid: #768b70;
22
+ --n-dmg-scifi-wall: #111c29;
23
+ --n-dmg-scifi-floor: #c9eee8;
24
+ --n-dmg-scifi-grid: #448f95;
25
+ --n-dmg-hit: rgba(255, 255, 255, 0.24);
26
+ }
27
+
28
+ .theme-dark {
29
+ --n-dmg-canvas: #161c21;
30
+ --n-dmg-panel: #222a30;
31
+ --n-dmg-workbench: #1b2228;
32
+ --n-dmg-ink: #f3ede3;
33
+ --n-dmg-muted: #b9c0c4;
34
+ --n-dmg-line: #4d575d;
35
+ --n-dmg-accent: #d9795d;
36
+ --n-dmg-accent-hover: #ed9277;
37
+ --n-dmg-accent-soft: #4d342d;
38
+ --n-dmg-gold: #d4ac5a;
39
+ --n-dmg-wall: #0d1216;
40
+ --n-dmg-floor: #d8c7a7;
41
+ --n-dmg-door: #e17e58;
42
+ --n-dmg-grid: #756b5e;
43
+ --n-dmg-white: #fff;
44
+ --n-dmg-shadow: rgba(0, 0, 0, 0.38);
45
+ --n-dmg-focus: #75c8ff;
46
+ --n-dmg-cavern-wall: #0d1711;
47
+ --n-dmg-cavern-floor: #b9c8a3;
48
+ --n-dmg-cavern-grid: #617a62;
49
+ --n-dmg-scifi-wall: #07111d;
50
+ --n-dmg-scifi-floor: #b8e4df;
51
+ --n-dmg-scifi-grid: #3f878e;
52
+ --n-dmg-hit: rgba(255, 255, 255, 0.2);
53
+ }
54
+
55
+ .dmg {
56
+ display: grid;
57
+ grid-template-columns: minmax(17rem, 0.68fr) minmax(0, 1.65fr);
58
+ min-height: 42rem;
59
+ overflow: hidden;
60
+ color: var(--n-dmg-ink);
61
+ background: var(--n-dmg-canvas);
62
+ border: 1px solid var(--n-dmg-line);
63
+ border-radius: 1.25rem;
64
+ box-shadow: 0 1.35rem 3.4rem var(--n-dmg-shadow);
65
+ }
66
+
67
+ .dmg button,
68
+ .dmg input,
69
+ .dmg summary {
70
+ font: inherit;
71
+ }
72
+
73
+ .dmg button,
74
+ .dmg summary,
75
+ .dmg-import {
76
+ cursor: pointer;
77
+ }
78
+
79
+ .dmg button:focus-visible,
80
+ .dmg input:focus-visible,
81
+ .dmg summary:focus-visible,
82
+ .dmg-import:focus-within {
83
+ outline: 3px solid var(--n-dmg-focus);
84
+ outline-offset: 2px;
85
+ }
86
+
87
+ .dmg-controls {
88
+ display: flex;
89
+ flex-direction: column;
90
+ padding: clamp(1rem, 2.5vw, 1.6rem);
91
+ background: var(--n-dmg-panel);
92
+ border-right: 1px solid var(--n-dmg-line);
93
+ }
94
+
95
+ .dmg-seed-row {
96
+ display: grid;
97
+ grid-template-columns: minmax(0, 1fr) auto;
98
+ gap: 0.55rem;
99
+ align-items: end;
100
+ }
101
+
102
+ .dmg-field {
103
+ display: grid;
104
+ gap: 0.4rem;
105
+ }
106
+
107
+ .dmg-field > span,
108
+ .dmg-styles legend,
109
+ .dmg-section-label,
110
+ .dmg-range > span {
111
+ color: var(--n-dmg-ink);
112
+ font-size: 0.71rem;
113
+ font-weight: 850;
114
+ letter-spacing: 0.08em;
115
+ text-transform: uppercase;
116
+ }
117
+
118
+ .dmg-field input {
119
+ width: 100%;
120
+ min-height: 2.65rem;
121
+ padding: 0.55rem 0.7rem;
122
+ color: var(--n-dmg-ink);
123
+ background: var(--n-dmg-workbench);
124
+ border: 1px solid var(--n-dmg-line);
125
+ border-radius: 0.65rem;
126
+ }
127
+
128
+ .dmg-shuffle {
129
+ min-height: 2.65rem;
130
+ padding: 0.55rem 0.72rem;
131
+ color: var(--n-dmg-accent);
132
+ background: var(--n-dmg-accent-soft);
133
+ border: 1px solid var(--n-dmg-accent);
134
+ border-radius: 0.65rem;
135
+ font-size: 0.78rem;
136
+ font-weight: 800;
137
+ }
138
+
139
+ .dmg-styles {
140
+ display: grid;
141
+ gap: 0.5rem;
142
+ margin: 1.25rem 0 0;
143
+ padding: 0;
144
+ border: 0;
145
+ }
146
+
147
+ .dmg-styles legend {
148
+ margin-bottom: 0.15rem;
149
+ }
150
+
151
+ .dmg-style-card {
152
+ display: grid;
153
+ grid-template-columns: 1.9rem 1fr;
154
+ gap: 0.05rem 0.65rem;
155
+ align-items: center;
156
+ min-height: 3.5rem;
157
+ padding: 0.55rem 0.65rem;
158
+ background: var(--n-dmg-workbench);
159
+ border: 1px solid var(--n-dmg-line);
160
+ border-radius: 0.7rem;
161
+ cursor: pointer;
162
+ }
163
+
164
+ .dmg-style-card:has(input:checked) {
165
+ background: var(--n-dmg-accent-soft);
166
+ border-color: var(--n-dmg-accent);
167
+ box-shadow: inset 3px 0 0 var(--n-dmg-accent);
168
+ }
169
+
170
+ .dmg-style-card input {
171
+ position: absolute;
172
+ opacity: 0;
173
+ }
174
+
175
+ .dmg-style-card strong,
176
+ .dmg-style-card small {
177
+ grid-column: 2;
178
+ }
179
+
180
+ .dmg-style-card strong {
181
+ font-size: 0.9rem;
182
+ }
183
+
184
+ .dmg-style-card small {
185
+ color: var(--n-dmg-muted);
186
+ font-size: 0.67rem;
187
+ line-height: 1.3;
188
+ }
189
+
190
+ .dmg-style-mark {
191
+ grid-row: 1 / 3;
192
+ width: 1.75rem;
193
+ height: 1.75rem;
194
+ background: var(--n-dmg-accent);
195
+ border: 0.3rem solid var(--n-dmg-wall);
196
+ border-radius: 0.2rem;
197
+ box-shadow: inset 0 0 0 2px var(--n-dmg-floor);
198
+ }
199
+
200
+ .dmg-style-mark-cavern {
201
+ background: var(--n-dmg-cavern-grid);
202
+ border-radius: 48% 52% 38% 62%;
203
+ transform: rotate(12deg);
204
+ }
205
+
206
+ .dmg-style-mark-scifi {
207
+ background: var(--n-dmg-scifi-grid);
208
+ border-radius: 50% 12%;
209
+ }
210
+
211
+ .dmg-size-section {
212
+ display: grid;
213
+ gap: 0.5rem;
214
+ margin-top: 1.25rem;
215
+ }
216
+
217
+ .dmg-size-presets {
218
+ display: grid;
219
+ grid-template-columns: repeat(3, minmax(0, 1fr));
220
+ gap: 0.35rem;
221
+ }
222
+
223
+ .dmg-size-presets button {
224
+ display: grid;
225
+ min-width: 0;
226
+ min-height: 3.1rem;
227
+ padding: 0.45rem 0.25rem;
228
+ color: var(--n-dmg-ink);
229
+ background: var(--n-dmg-workbench);
230
+ border: 1px solid var(--n-dmg-line);
231
+ border-radius: 0.6rem;
232
+ text-align: center;
233
+ }
234
+
235
+ .dmg-size-presets button[aria-pressed='true'] {
236
+ color: var(--n-dmg-white);
237
+ background: var(--n-dmg-accent);
238
+ border-color: var(--n-dmg-accent);
239
+ }
240
+
241
+ .dmg-size-presets strong {
242
+ overflow: hidden;
243
+ font-size: 0.7rem;
244
+ text-overflow: ellipsis;
245
+ }
246
+
247
+ .dmg-size-presets span {
248
+ font-size: 0.65rem;
249
+ font-variant-numeric: tabular-nums;
250
+ }
251
+
252
+ .dmg-grid-tuning {
253
+ margin-top: 0.8rem;
254
+ border-block: 1px solid var(--n-dmg-line);
255
+ }
256
+
257
+ .dmg-grid-tuning summary {
258
+ padding: 0.65rem 0;
259
+ color: var(--n-dmg-muted);
260
+ font-size: 0.75rem;
261
+ font-weight: 750;
262
+ }
263
+
264
+ .dmg-measurements {
265
+ display: grid;
266
+ gap: 0.8rem;
267
+ padding: 0.25rem 0 0.85rem;
268
+ }
269
+
270
+ .dmg-range {
271
+ display: grid;
272
+ grid-template-columns: 1fr auto;
273
+ gap: 0.35rem;
274
+ align-items: center;
275
+ }
276
+
277
+ .dmg-range output {
278
+ min-width: 3.2ch;
279
+ padding: 0.12rem 0.35rem;
280
+ background: var(--n-dmg-workbench);
281
+ border-radius: 0.3rem;
282
+ font-size: 0.76rem;
283
+ font-variant-numeric: tabular-nums;
284
+ font-weight: 850;
285
+ text-align: center;
286
+ }
287
+
288
+ .dmg-range input {
289
+ grid-column: 1 / -1;
290
+ width: 100%;
291
+ accent-color: var(--n-dmg-accent);
292
+ }
293
+
294
+ .dmg-range small {
295
+ grid-column: 1 / -1;
296
+ color: var(--n-dmg-muted);
297
+ font-size: 0.68rem;
298
+ }
299
+
300
+ .dmg-generate {
301
+ width: 100%;
302
+ min-height: 2.85rem;
303
+ margin-top: auto;
304
+ padding: 0.65rem 1rem;
305
+ color: var(--n-dmg-white);
306
+ background: var(--n-dmg-accent);
307
+ border: 1px solid var(--n-dmg-accent);
308
+ border-radius: 0.68rem;
309
+ font-weight: 850;
310
+ }
311
+
312
+ .dmg-generate:hover,
313
+ .dmg-action-primary:hover {
314
+ background: var(--n-dmg-accent-hover);
315
+ }
316
+
317
+ .dmg-chart {
318
+ display: flex;
319
+ min-width: 0;
320
+ flex-direction: column;
321
+ padding: clamp(0.85rem, 2.5vw, 1.5rem);
322
+ }
323
+
324
+ .dmg-map-frame {
325
+ position: relative;
326
+ display: grid;
327
+ min-height: 29rem;
328
+ margin: 0.65rem 0 0.9rem;
329
+ overflow: auto;
330
+ flex: 1;
331
+ place-items: center;
332
+ background: var(--n-dmg-wall);
333
+ border: 0.55rem solid var(--n-dmg-panel);
334
+ border-radius: 0.85rem;
335
+ box-shadow: 0 0 0 1px var(--n-dmg-line), inset 0 0 2rem var(--n-dmg-shadow);
336
+ }
337
+
338
+ .dmg-edit-hint {
339
+ position: absolute;
340
+ top: 0.55rem;
341
+ right: 0.55rem;
342
+ z-index: 2;
343
+ padding: 0.28rem 0.48rem;
344
+ color: var(--n-dmg-muted);
345
+ background: var(--n-dmg-panel);
346
+ border: 1px solid var(--n-dmg-line);
347
+ border-radius: 0.4rem;
348
+ font-size: 0.66rem;
349
+ pointer-events: none;
350
+ }
351
+
352
+ .dmg-context-menu {
353
+ position: absolute;
354
+ z-index: 5;
355
+ display: grid;
356
+ min-width: 7rem;
357
+ gap: 0.25rem;
358
+ padding: 0.35rem;
359
+ background: var(--n-dmg-panel);
360
+ border: 1px solid var(--n-dmg-line);
361
+ border-radius: 0.6rem;
362
+ box-shadow: 0 0.7rem 1.6rem var(--n-dmg-shadow);
363
+ transform: translate(-0.35rem, -0.35rem);
364
+ }
365
+
366
+ .dmg-context-menu[hidden] {
367
+ display: none;
368
+ }
369
+
370
+ .dmg-context-menu button {
371
+ min-height: 2.2rem;
372
+ padding: 0.35rem 0.55rem;
373
+ color: var(--n-dmg-ink);
374
+ background: var(--n-dmg-workbench);
375
+ border: 1px solid transparent;
376
+ border-radius: 0.4rem;
377
+ font-size: 0.73rem;
378
+ font-weight: 800;
379
+ text-align: left;
380
+ }
381
+
382
+ .dmg-context-menu button:hover {
383
+ border-color: var(--n-dmg-accent);
384
+ }
385
+
386
+ .dmg-map-host {
387
+ display: grid;
388
+ width: 100%;
389
+ place-items: center;
390
+ }
391
+
392
+ .dmg-map {
393
+ display: block;
394
+ width: 100%;
395
+ min-width: 31rem;
396
+ max-height: 35rem;
397
+ animation: dmg-map-ink 220ms ease-out;
398
+ }
399
+
400
+ .dmg-map-void {
401
+ fill: var(--n-dmg-wall);
402
+ }
403
+
404
+ .dmg-map-floor {
405
+ fill: var(--n-dmg-floor);
406
+ stroke: var(--n-dmg-grid);
407
+ stroke-width: 0.7px;
408
+ }
409
+
410
+ .dmg[data-style='cavern'] .dmg-map-void {
411
+ fill: var(--n-dmg-cavern-wall);
412
+ }
413
+
414
+ .dmg[data-style='cavern'] .dmg-map-floor {
415
+ fill: var(--n-dmg-cavern-floor);
416
+ stroke: var(--n-dmg-cavern-grid);
417
+ stroke-width: 0.4px;
418
+ }
419
+
420
+ .dmg[data-style='scifi'] .dmg-map-void {
421
+ fill: var(--n-dmg-scifi-wall);
422
+ }
423
+
424
+ .dmg[data-style='scifi'] .dmg-map-floor {
425
+ fill: var(--n-dmg-scifi-floor);
426
+ stroke: var(--n-dmg-scifi-grid);
427
+ }
428
+
429
+ .dmg-map-door line {
430
+ stroke: var(--n-dmg-door);
431
+ stroke-linecap: square;
432
+ stroke-width: 4px;
433
+ }
434
+
435
+ .dmg-map-door circle {
436
+ fill: var(--n-dmg-white);
437
+ }
438
+
439
+ .dmg-map-compass circle {
440
+ fill: var(--n-dmg-wall);
441
+ stroke: var(--n-dmg-gold);
442
+ stroke-width: 1px;
443
+ }
444
+
445
+ .dmg-map-compass path {
446
+ fill: var(--n-dmg-gold);
447
+ }
448
+
449
+ .dmg-map-hit {
450
+ fill: transparent;
451
+ cursor: context-menu;
452
+ pointer-events: all;
453
+ }
454
+
455
+ .dmg-map-hit:hover {
456
+ fill: var(--n-dmg-hit);
457
+ stroke: var(--n-dmg-white);
458
+ stroke-width: 1px;
459
+ }
460
+
461
+ .dmg-map-footer {
462
+ display: grid;
463
+ grid-template-columns: auto minmax(0, 1fr);
464
+ gap: 1rem;
465
+ align-items: center;
466
+ }
467
+
468
+ .dmg-stats {
469
+ display: flex;
470
+ gap: clamp(1rem, 3vw, 2.2rem);
471
+ margin: 0;
472
+ }
473
+
474
+ .dmg-stats div {
475
+ display: grid;
476
+ gap: 0.1rem;
477
+ }
478
+
479
+ .dmg-stats dt {
480
+ color: var(--n-dmg-muted);
481
+ font-size: 0.65rem;
482
+ font-weight: 800;
483
+ letter-spacing: 0.05em;
484
+ text-transform: uppercase;
485
+ }
486
+
487
+ .dmg-stats dd {
488
+ margin: 0;
489
+ font-size: 1.25rem;
490
+ font-variant-numeric: tabular-nums;
491
+ font-weight: 900;
492
+ }
493
+
494
+ .dmg-legend {
495
+ display: flex;
496
+ gap: 0.75rem;
497
+ align-items: center;
498
+ flex-wrap: wrap;
499
+ justify-content: flex-end;
500
+ }
501
+
502
+ .dmg-legend span {
503
+ display: inline-flex;
504
+ gap: 0.35rem;
505
+ align-items: center;
506
+ color: var(--n-dmg-muted);
507
+ font-size: 0.68rem;
508
+ }
509
+
510
+ .dmg-legend i {
511
+ position: relative;
512
+ width: 0.75rem;
513
+ height: 0.75rem;
514
+ background: var(--n-dmg-floor);
515
+ border: 1px solid var(--n-dmg-grid);
516
+ }
517
+
518
+ .dmg-legend i[data-tile='wall'] {
519
+ background: var(--n-dmg-wall);
520
+ }
521
+
522
+ .dmg-legend i[data-tile='door'] {
523
+ background: transparent;
524
+ border-color: transparent;
525
+ }
526
+
527
+ .dmg-legend i[data-tile='door']::after {
528
+ position: absolute;
529
+ top: 0.29rem;
530
+ left: 0;
531
+ width: 0.75rem;
532
+ height: 0.18rem;
533
+ background: var(--n-dmg-door);
534
+ content: '';
535
+ }
536
+
537
+ .dmg-actions {
538
+ display: flex;
539
+ gap: 0.45rem;
540
+ margin-top: 1rem;
541
+ align-items: center;
542
+ flex-wrap: wrap;
543
+ }
544
+
545
+ .dmg-actions > button,
546
+ .dmg-map-data > summary,
547
+ .dmg-map-data button,
548
+ .dmg-import {
549
+ display: inline-flex;
550
+ min-height: 2.55rem;
551
+ align-items: center;
552
+ justify-content: center;
553
+ padding: 0.48rem 0.72rem;
554
+ color: var(--n-dmg-ink);
555
+ background: var(--n-dmg-panel);
556
+ border: 1px solid var(--n-dmg-line);
557
+ border-radius: 0.62rem;
558
+ font-size: 0.73rem;
559
+ font-weight: 800;
560
+ list-style: none;
561
+ }
562
+
563
+ .dmg-actions > button:hover,
564
+ .dmg-map-data > summary:hover,
565
+ .dmg-map-data button:hover,
566
+ .dmg-import:hover {
567
+ border-color: var(--n-dmg-accent);
568
+ }
569
+
570
+ .dmg-actions > .dmg-action-primary {
571
+ color: var(--n-dmg-white);
572
+ background: var(--n-dmg-accent);
573
+ border-color: var(--n-dmg-accent);
574
+ }
575
+
576
+ .dmg-map-data {
577
+ position: relative;
578
+ }
579
+
580
+ .dmg-map-data > summary::-webkit-details-marker {
581
+ display: none;
582
+ }
583
+
584
+ .dmg-map-data > div {
585
+ position: absolute;
586
+ right: 0;
587
+ bottom: calc(100% + 0.45rem);
588
+ z-index: 4;
589
+ display: grid;
590
+ min-width: 10rem;
591
+ gap: 0.35rem;
592
+ padding: 0.45rem;
593
+ background: var(--n-dmg-panel);
594
+ border: 1px solid var(--n-dmg-line);
595
+ border-radius: 0.7rem;
596
+ box-shadow: 0 0.8rem 1.8rem var(--n-dmg-shadow);
597
+ }
598
+
599
+ .dmg-import input {
600
+ position: absolute;
601
+ width: 1px;
602
+ height: 1px;
603
+ overflow: hidden;
604
+ opacity: 0;
605
+ }
606
+
607
+ @keyframes dmg-map-ink {
608
+ from {
609
+ opacity: 0.45;
610
+ transform: scale(0.995);
611
+ }
612
+
613
+ to {
614
+ opacity: 1;
615
+ transform: scale(1);
616
+ }
617
+ }
618
+
619
+ @media (max-width: 900px) {
620
+ .dmg {
621
+ grid-template-columns: 1fr;
622
+ }
623
+
624
+ .dmg-controls {
625
+ border-right: 0;
626
+ border-bottom: 1px solid var(--n-dmg-line);
627
+ }
628
+
629
+ .dmg-styles {
630
+ grid-template-columns: repeat(3, minmax(0, 1fr));
631
+ }
632
+
633
+ .dmg-styles legend {
634
+ grid-column: 1 / -1;
635
+ }
636
+
637
+ .dmg-generate {
638
+ margin-top: 1rem;
639
+ }
640
+ }
641
+
642
+ @media (max-width: 580px) {
643
+ .dmg-styles,
644
+ .dmg-size-presets {
645
+ grid-template-columns: 1fr;
646
+ }
647
+
648
+ .dmg-styles legend {
649
+ grid-column: auto;
650
+ }
651
+
652
+ .dmg-map-frame {
653
+ min-height: 20rem;
654
+ }
655
+
656
+ .dmg-map-footer {
657
+ grid-template-columns: 1fr;
658
+ }
659
+
660
+ .dmg-legend {
661
+ justify-content: flex-start;
662
+ }
663
+
664
+ .dmg-actions > button,
665
+ .dmg-map-data {
666
+ flex: 1 1 auto;
667
+ }
668
+
669
+ .dmg-map-data > summary {
670
+ width: 100%;
671
+ }
672
+ }
673
+
674
+ @media (prefers-reduced-motion: reduce) {
675
+ .dmg-map {
676
+ animation: none;
677
+ }
678
+ }
@@ -0,0 +1,16 @@
1
+ ---
2
+ import { SEORenderer } from '@jjlmoya/utils-shared';
3
+ import { dungeonMapGenerator } from './index';
4
+ import type { KnownLocale } from '../../types';
5
+
6
+ interface Props {
7
+ locale?: KnownLocale;
8
+ }
9
+
10
+ const { locale = 'en' } = Astro.props as Props;
11
+ const loader = dungeonMapGenerator.i18n[locale] || dungeonMapGenerator.i18n.en;
12
+ const content = await loader?.();
13
+ if (!content) return null;
14
+ ---
15
+
16
+ {content.seo?.length > 0 && <SEORenderer content={{ locale, sections: content.seo }} />}
@@ -0,0 +1,18 @@
1
+ import { decodeDocument, encodeDocument, type DungeonDocument } from './document';
2
+
3
+ const STORAGE_KEY = 'jjlmoya:dungeon-map-generator:v1';
4
+
5
+ export function loadDocument(): DungeonDocument | undefined {
6
+ try {
7
+ const value = localStorage.getItem(STORAGE_KEY);
8
+ return value ? decodeDocument(value) : undefined;
9
+ } catch {
10
+ return undefined;
11
+ }
12
+ }
13
+
14
+ export function saveDocument(document: DungeonDocument): void {
15
+ try {
16
+ localStorage.setItem(STORAGE_KEY, encodeDocument(document));
17
+ } catch {}
18
+ }