@jjlmoya/utils-tabletop 1.28.0 → 1.30.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/package.json +1 -1
- package/src/category/index.ts +2 -0
- package/src/entries.ts +4 -0
- package/src/tests/locale_completeness.test.ts +2 -9
- package/src/tests/tool_validation.test.ts +1 -2
- package/src/tool/mus-scoreboard/component.astro +21 -11
- package/src/tool/rpg-settlement-exploration-map-generator/.keep +1 -0
- package/src/tool/rpg-settlement-exploration-map-generator/bibliography.astro +11 -0
- package/src/tool/rpg-settlement-exploration-map-generator/bibliography.ts +7 -0
- package/src/tool/rpg-settlement-exploration-map-generator/component.astro +66 -0
- package/src/tool/rpg-settlement-exploration-map-generator/controller.ts +231 -0
- package/src/tool/rpg-settlement-exploration-map-generator/dom-views.ts +174 -0
- package/src/tool/rpg-settlement-exploration-map-generator/editor.ts +85 -0
- package/src/tool/rpg-settlement-exploration-map-generator/entry.ts +27 -0
- package/src/tool/rpg-settlement-exploration-map-generator/evaluator.ts +8 -0
- package/src/tool/rpg-settlement-exploration-map-generator/i18n/de.ts +152 -0
- package/src/tool/rpg-settlement-exploration-map-generator/i18n/en.ts +63 -0
- package/src/tool/rpg-settlement-exploration-map-generator/i18n/es.ts +233 -0
- package/src/tool/rpg-settlement-exploration-map-generator/i18n/fr.ts +152 -0
- package/src/tool/rpg-settlement-exploration-map-generator/i18n/id.ts +152 -0
- package/src/tool/rpg-settlement-exploration-map-generator/i18n/it.ts +152 -0
- package/src/tool/rpg-settlement-exploration-map-generator/i18n/ja.ts +152 -0
- package/src/tool/rpg-settlement-exploration-map-generator/i18n/ko.ts +152 -0
- package/src/tool/rpg-settlement-exploration-map-generator/i18n/nl.ts +152 -0
- package/src/tool/rpg-settlement-exploration-map-generator/i18n/pl.ts +152 -0
- package/src/tool/rpg-settlement-exploration-map-generator/i18n/pt.ts +152 -0
- package/src/tool/rpg-settlement-exploration-map-generator/i18n/ru.ts +152 -0
- package/src/tool/rpg-settlement-exploration-map-generator/i18n/sv.ts +152 -0
- package/src/tool/rpg-settlement-exploration-map-generator/i18n/tr.ts +152 -0
- package/src/tool/rpg-settlement-exploration-map-generator/i18n/zh.ts +152 -0
- package/src/tool/rpg-settlement-exploration-map-generator/index.ts +4 -0
- package/src/tool/rpg-settlement-exploration-map-generator/logic.test.ts +65 -0
- package/src/tool/rpg-settlement-exploration-map-generator/logic.ts +288 -0
- package/src/tool/rpg-settlement-exploration-map-generator/rpg-settlement-exploration-map-generator.css +885 -0
- package/src/tool/rpg-settlement-exploration-map-generator/seo.astro +11 -0
- package/src/tool/rpg-settlement-exploration-map-generator/storage.test.ts +12 -0
- package/src/tool/rpg-settlement-exploration-map-generator/storage.ts +24 -0
- package/src/tool/rpg-settlement-exploration-map-generator/ui.ts +81 -0
- package/src/tools.ts +2 -0
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
---
|
|
2
|
+
import { SEORenderer } from '@jjlmoya/utils-shared';
|
|
3
|
+
import { rpgSettlementExplorationMapGenerator } from './index';
|
|
4
|
+
import type { KnownLocale } from '../../types';
|
|
5
|
+
interface Props { locale?: KnownLocale; }
|
|
6
|
+
const { locale = 'en' } = Astro.props as Props;
|
|
7
|
+
const loader = rpgSettlementExplorationMapGenerator.i18n[locale] || rpgSettlementExplorationMapGenerator.i18n.en;
|
|
8
|
+
const content = await loader?.();
|
|
9
|
+
if (!content) return null;
|
|
10
|
+
---
|
|
11
|
+
{content.seo.length > 0 && <SEORenderer content={{ locale, sections: content.seo }} />}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import { DEFAULT_CONFIG, generateSettlement } from './logic';
|
|
3
|
+
import { decodeShareState, encodeShareState } from './storage';
|
|
4
|
+
|
|
5
|
+
describe('settlement map storage', () => {
|
|
6
|
+
it('shares the edited map and custom service catalog together', () => {
|
|
7
|
+
const map = generateSettlement({ ...DEFAULT_CONFIG, services: ['tavern', 'Shrine'] });
|
|
8
|
+
const decoded = decodeShareState(encodeShareState(map, ['Shrine', 'Waystone']));
|
|
9
|
+
expect(decoded?.map).toEqual(map);
|
|
10
|
+
expect(decoded?.customServices).toEqual(['Shrine', 'Waystone']);
|
|
11
|
+
});
|
|
12
|
+
});
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { normalizeConfig, type SettlementConfig, type SettlementMap } from './logic';
|
|
2
|
+
|
|
3
|
+
const STORAGE_KEY = 'jjlmoya:rpg-settlement-map-generator';
|
|
4
|
+
export interface SettlementState {
|
|
5
|
+
map: SettlementMap;
|
|
6
|
+
customServices: string[];
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
function cleanCustomServices(values: unknown): string[] { return Array.isArray(values) ? [...new Set(values.filter((value): value is string => typeof value === 'string' && value.trim().length > 0).map((value) => value.trim().slice(0, 28)))] : []; }
|
|
10
|
+
|
|
11
|
+
function stateFromUnknown(value: unknown): SettlementState | null {
|
|
12
|
+
if (!value || typeof value !== 'object') return null;
|
|
13
|
+
const candidate = value as Partial<SettlementState> & Partial<SettlementMap>;
|
|
14
|
+
if (isSettlementMap(candidate.map)) return { map: candidate.map, customServices: cleanCustomServices(candidate.customServices) };
|
|
15
|
+
if (isSettlementMap(value)) return { map: value, customServices: [] };
|
|
16
|
+
return null;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function saveSettlement(map: SettlementMap, customServices: string[] = []): void { try { window.localStorage.setItem(STORAGE_KEY, JSON.stringify({ map, customServices: cleanCustomServices(customServices) })); } catch {} }
|
|
20
|
+
export function loadSettlement(): SettlementState | null { try { const raw = window.localStorage.getItem(STORAGE_KEY); return raw ? stateFromUnknown(JSON.parse(raw)) : null; } catch { return null; } }
|
|
21
|
+
export function encodeShareState(map: SettlementMap, customServices: string[] = []): string { const state = { map, customServices: cleanCustomServices(customServices) }; return btoa(unescape(encodeURIComponent(JSON.stringify(state)))).replaceAll('+', '-').replaceAll('/', '_').replaceAll('=', ''); }
|
|
22
|
+
export function decodeShareState(value: string): SettlementState | null { try { const padded = value.replaceAll('-', '+').replaceAll('_', '/') + '='.repeat((4 - value.length % 4) % 4); return stateFromUnknown(JSON.parse(decodeURIComponent(escape(atob(padded))))); } catch { return null; } }
|
|
23
|
+
export function configFromMap(map: SettlementMap): SettlementConfig { return normalizeConfig({ ...map.config, services: [...map.config.services] }); }
|
|
24
|
+
export function isSettlementMap(value: unknown): value is SettlementMap { if (!value || typeof value !== 'object') return false; const candidate = value as Partial<SettlementMap>; return typeof candidate.width === 'number' && typeof candidate.height === 'number' && !!candidate.config && Array.isArray(candidate.buildings) && Array.isArray(candidate.paths) && Array.isArray(candidate.water); }
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
export interface SettlementMapUI extends Record<string, string> {
|
|
2
|
+
intro: string;
|
|
3
|
+
seedLabel: string;
|
|
4
|
+
seedHint: string;
|
|
5
|
+
randomSeed: string;
|
|
6
|
+
environmentLabel: string;
|
|
7
|
+
forest: string;
|
|
8
|
+
plains: string;
|
|
9
|
+
coast: string;
|
|
10
|
+
river: string;
|
|
11
|
+
mountain: string;
|
|
12
|
+
styleLabel: string;
|
|
13
|
+
styleHint: string;
|
|
14
|
+
styleTimber: string;
|
|
15
|
+
styleStone: string;
|
|
16
|
+
styleCoastal: string;
|
|
17
|
+
styleHighland: string;
|
|
18
|
+
styleMedieval: string;
|
|
19
|
+
styleEdo: string;
|
|
20
|
+
styleSahelian: string;
|
|
21
|
+
sizeLabel: string;
|
|
22
|
+
hamlet: string;
|
|
23
|
+
hamletHint: string;
|
|
24
|
+
village: string;
|
|
25
|
+
villageHint: string;
|
|
26
|
+
town: string;
|
|
27
|
+
townHint: string;
|
|
28
|
+
homesLabel: string;
|
|
29
|
+
homesHint: string;
|
|
30
|
+
servicesLabel: string;
|
|
31
|
+
serviceListHint: string;
|
|
32
|
+
newServicePlaceholder: string;
|
|
33
|
+
addService: string;
|
|
34
|
+
removeService: string;
|
|
35
|
+
serviceTavern: string;
|
|
36
|
+
serviceSmithy: string;
|
|
37
|
+
serviceTemple: string;
|
|
38
|
+
serviceMarket: string;
|
|
39
|
+
serviceStable: string;
|
|
40
|
+
serviceHall: string;
|
|
41
|
+
generate: string;
|
|
42
|
+
mapRegionLabel: string;
|
|
43
|
+
mapSummary: string;
|
|
44
|
+
buildings: string;
|
|
45
|
+
paths: string;
|
|
46
|
+
services: string;
|
|
47
|
+
terrain: string;
|
|
48
|
+
legendLabel: string;
|
|
49
|
+
legendHome: string;
|
|
50
|
+
legendService: string;
|
|
51
|
+
legendPath: string;
|
|
52
|
+
legendWater: string;
|
|
53
|
+
legendWild: string;
|
|
54
|
+
editLabel: string;
|
|
55
|
+
toolSelect: string;
|
|
56
|
+
toolBuilding: string;
|
|
57
|
+
toolPath: string;
|
|
58
|
+
toolWater: string;
|
|
59
|
+
toolTree: string;
|
|
60
|
+
toolErase: string;
|
|
61
|
+
serviceSelectLabel: string;
|
|
62
|
+
clickHint: string;
|
|
63
|
+
selectedHint: string;
|
|
64
|
+
shareLink: string;
|
|
65
|
+
linkCopied: string;
|
|
66
|
+
exportPng: string;
|
|
67
|
+
exportSvg: string;
|
|
68
|
+
exportJson: string;
|
|
69
|
+
importJson: string;
|
|
70
|
+
importError: string;
|
|
71
|
+
mapData: string;
|
|
72
|
+
readyBadge: string;
|
|
73
|
+
serviceNone: string;
|
|
74
|
+
contextMenuLabel: string;
|
|
75
|
+
contextInspect: string;
|
|
76
|
+
contextBuilding: string;
|
|
77
|
+
contextPath: string;
|
|
78
|
+
contextWater: string;
|
|
79
|
+
contextTree: string;
|
|
80
|
+
contextErase: string;
|
|
81
|
+
}
|
package/src/tools.ts
CHANGED
|
@@ -15,6 +15,7 @@ import { DUNGEON_MAP_GENERATOR_TOOL } from './tool/dungeon-map-generator';
|
|
|
15
15
|
import { ENCOUNTER_DIFFICULTY_CALCULATOR_TOOL } from './tool/encounter-difficulty-calculator';
|
|
16
16
|
import { TOKEN_STAMP_STUDIO_TOOL } from './tool/token-stamp-studio';
|
|
17
17
|
import { MUS_SCOREBOARD_TOOL } from './tool/mus-scoreboard';
|
|
18
|
+
import { RPG_SETTLEMENT_EXPLORATION_MAP_GENERATOR_TOOL } from './tool/rpg-settlement-exploration-map-generator';
|
|
18
19
|
|
|
19
20
|
export const ALL_TOOLS: ToolDefinition[] = [
|
|
20
21
|
DICE_ROLLER_SIMULATOR_TOOL,
|
|
@@ -32,5 +33,6 @@ export const ALL_TOOLS: ToolDefinition[] = [
|
|
|
32
33
|
ENCOUNTER_DIFFICULTY_CALCULATOR_TOOL,
|
|
33
34
|
TOKEN_STAMP_STUDIO_TOOL,
|
|
34
35
|
MUS_SCOREBOARD_TOOL,
|
|
36
|
+
RPG_SETTLEMENT_EXPLORATION_MAP_GENERATOR_TOOL,
|
|
35
37
|
];
|
|
36
38
|
|