From 2ba6107826d1b5835981dd13f23d6d0703d31393 Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 3 Oct 2026 15:58:20 +0000 Subject: [PATCH] Remove the import wizard's scene-to-map link step Chronicle maps open in Foundry as journal pages that appear on connect, so pairing scenes with maps only set an unused scene flag. The wizard drops the Maps step (7 steps now), its plan items and strings, and the import runner drops the link-map case and the createMapping wrapper it alone used. Fixes #129 Co-Authored-By: Claude Opus 5.5 (1M context) Claude-Session: https://claude.ai/code/session_01WURTwLzEdwXRLKXnnQtFCS --- API-CONTRACT.md | 2 +- lang/en.json | 6 --- scripts/import-wizard.mjs | 67 ++---------------------------- scripts/sync-manager.mjs | 25 ----------- styles/import-wizard.css | 6 +-- templates/import-wizard.hbs | 38 +---------------- tools/test-import-wizard-steps.mjs | 31 ++++++++++++++ 7 files changed, 38 insertions(+), 137 deletions(-) create mode 100644 tools/test-import-wizard-steps.mjs diff --git a/API-CONTRACT.md b/API-CONTRACT.md index 17736ad..1e554ca 100644 --- a/API-CONTRACT.md +++ b/API-CONTRACT.md @@ -393,7 +393,7 @@ Lists all tags in the campaign. #### POST /tags Create a new tag. -**Used by:** `import-wizard.mjs` → Step 8 tag creation during import +**Used by:** `import-wizard.mjs` → Step 7 (Review) tag creation during import **Request:** ```json diff --git a/lang/en.json b/lang/en.json index f7b2964..e39c42a 100644 --- a/lang/en.json +++ b/lang/en.json @@ -328,7 +328,6 @@ "Tags": "Tags", "Characters": "Characters", "Calendar": "Calendar", - "Maps": "Maps", "Review": "Review" }, "ConnectDesc": "Verifying connection to Chronicle and detecting your campaign configuration.", @@ -372,11 +371,6 @@ "CalendarPull": "Pull Chronicle calendar into Foundry", "CalendarPush": "Push Foundry calendar to Chronicle", "CalendarSkip": "Skip calendar sync for now", - "MapsDesc": "Link Foundry scenes to Chronicle maps. Scenes with matching names are auto-suggested.", - "FoundryScene": "Foundry Scene", - "ChronicleMap": "Chronicle Map", - "NoLink": "No link", - "NoScenes": "No unlinked scenes found.", "ReviewSummary": "The following operations will be performed:", "Errors": "errors", "NoPlan": "No import plan generated — go back and configure at least one step.", diff --git a/scripts/import-wizard.mjs b/scripts/import-wizard.mjs index 5bf4732..9a53bc7 100644 --- a/scripts/import-wizard.mjs +++ b/scripts/import-wizard.mjs @@ -25,7 +25,6 @@ const STEPS = Object.freeze([ { key: 'tags', labelKey: 'CHRONICLE.Wizard.Steps.Tags', icon: 'fa-solid fa-tags' }, { key: 'characters', labelKey: 'CHRONICLE.Wizard.Steps.Characters', icon: 'fa-solid fa-users' }, { key: 'calendar', labelKey: 'CHRONICLE.Wizard.Steps.Calendar', icon: 'fa-solid fa-calendar' }, - { key: 'maps', labelKey: 'CHRONICLE.Wizard.Steps.Maps', icon: 'fa-solid fa-map' }, { key: 'review', labelKey: 'CHRONICLE.Wizard.Steps.Review', icon: 'fa-solid fa-clipboard-check' }, ]); @@ -153,13 +152,11 @@ export class ImportWizard extends HandlebarsApplicationMixin(ApplicationV2) { /** Step 6: calendar sync direction choice. */ this._calendarDirection = 'skip'; - /** Step 7: scene ↔ Chronicle map link plan. */ - this._mapLinkPlan = []; - /** Step 8: aggregated import plan built from all previous steps. */ + /** Step 7: aggregated import plan built from all previous steps. */ this._importPlan = null; - /** Step 8: live import progress tracking. */ + /** Step 7: live import progress tracking. */ this._importProgress = { total: 0, done: 0, errors: 0, log: [] }; /** Whether an import is currently running. */ @@ -216,11 +213,6 @@ export class ImportWizard extends HandlebarsApplicationMixin(ApplicationV2) { const calendarAddon = addonSlugs.has('calendar') || addonSlugs.has('calendars'); enabled = calendarAddon && hasCalendarModule; } - // Maps step: only if maps addon is enabled. - if (step.key === 'maps') { - const mapsAddon = addonSlugs.has('maps') || addonSlugs.has('map'); - enabled = mapsAddon; - } return { ...step, index, enabled }; }); } @@ -465,7 +457,7 @@ export class ImportWizard extends HandlebarsApplicationMixin(ApplicationV2) { } // --------------------------------------------------------------------------- - // Step 8: Review & Import + // Step 7: Review & Import // --------------------------------------------------------------------------- /** @@ -508,16 +500,6 @@ export class ImportWizard extends HandlebarsApplicationMixin(ApplicationV2) { }); } - // Map links (Step 7). - for (const link of this._mapLinkPlan.filter((l) => l.chronicleMapId)) { - plan.push({ - type: 'link-map', - label: `Link scene "${link.sceneName}" to Chronicle map`, - data: { sceneId: link.sceneId, mapId: link.chronicleMapId }, - status: 'pending', - }); - } - this._importPlan = plan; this._importProgress = { total: plan.length, done: 0, errors: 0, log: [] }; return plan; @@ -605,7 +587,6 @@ export class ImportWizard extends HandlebarsApplicationMixin(ApplicationV2) { tagPlan: this._tagPlan, characterPlan: this._characterPlan, calendarDirection: this._calendarDirection, - mapLinkPlan: this._mapLinkPlan, importPlan: this._importPlan, importProgress: this._importProgress, importRunning: this._importRunning, @@ -682,16 +663,6 @@ export class ImportWizard extends HandlebarsApplicationMixin(ApplicationV2) { this._calendarDirection = e.target.value; }); } - - // Step 7: map link selects. - for (const select of el.querySelectorAll('.wizard-map-select')) { - select.addEventListener('change', (e) => { - const idx = Number(e.target.dataset.mapIndex); - if (this._mapLinkPlan[idx]) { - this._mapLinkPlan[idx].chronicleMapId = e.target.value || null; - } - }); - } } // --------------------------------------------------------------------------- @@ -759,9 +730,6 @@ export class ImportWizard extends HandlebarsApplicationMixin(ApplicationV2) { case 'characters': if (this._characterPlan.length === 0) this._buildCharacterPlan(); break; - case 'maps': - if (this._mapLinkPlan.length === 0) await this._buildMapLinkPlan(); - break; case 'review': this._buildImportPlan(); break; @@ -783,35 +751,6 @@ export class ImportWizard extends HandlebarsApplicationMixin(ApplicationV2) { })); } - /** - * Build the map linking plan by matching Foundry scenes with Chronicle maps. - */ - async _buildMapLinkPlan() { - if (!this._worldScan || !this.api) return; - - let chronicleMaps = []; - try { - const result = await this.api.get('/maps'); - chronicleMaps = Array.isArray(result) ? result : (result?.data ?? result?.maps ?? []); - } catch { - // Maps endpoint not available. - } - - this._mapLinkPlan = this._worldScan.scenes.map((scene) => { - // Auto-match by name. - const nameLower = scene.name.toLowerCase(); - const match = chronicleMaps.find( - (m) => (m.name || '').toLowerCase() === nameLower - ); - return { - sceneId: scene.id, - sceneName: scene.name, - chronicleMapId: match?.id ?? null, - chronicleMaps, - }; - }); - } - // --------------------------------------------------------------------------- // Action handlers // --------------------------------------------------------------------------- diff --git a/scripts/sync-manager.mjs b/scripts/sync-manager.mjs index a7759ff..3e7aeb3 100644 --- a/scripts/sync-manager.mjs +++ b/scripts/sync-manager.mjs @@ -703,17 +703,6 @@ export class SyncManager { this._activityLog = []; } - /** - * Create or update a sync mapping on the server. Delegates to - * `ensureMapping` so legacy callers (wizard `link-map`, dashboard - * manual-sync) inherit conflict-tolerant idempotency. - * @param {object} mapping - * @returns {Promise} - */ - async createMapping(mapping) { - return this.ensureMapping(mapping); - } - /** * Idempotent sync-mapping POST. * @@ -865,20 +854,6 @@ export class SyncManager { await actorSync._handleCreateActor(actor, {}, game.user.id); break; } - case 'link-map': { - const scene = game.scenes.get(item.data.sceneId); - if (!scene) throw new Error(`Scene ${item.data.sceneId} not found`); - await scene.setFlag('chronicle-sync', 'mapId', item.data.mapId); - await this.createMapping({ - chronicle_type: 'map', - chronicle_id: item.data.mapId, - external_system: 'foundry', - external_id: item.data.sceneId, - sync_direction: 'both', - sync_metadata: { foundry_type: 'Scene' }, - }); - break; - } case 'assign-tags': { await this.api.bulkAssignTags(item.data); break; diff --git a/styles/import-wizard.css b/styles/import-wizard.css index b3d097f..08526d1 100644 --- a/styles/import-wizard.css +++ b/styles/import-wizard.css @@ -311,8 +311,7 @@ text-align: center; } -.wizard-type-select, -.wizard-map-select { +.wizard-type-select { width: 100%; padding: 4px 6px; font-size: 12px; @@ -322,8 +321,7 @@ color: inherit; } -.wizard-type-select:focus, -.wizard-map-select:focus { +.wizard-type-select:focus { border-color: #60a5fa; outline: none; } diff --git a/templates/import-wizard.hbs b/templates/import-wizard.hbs index 5caf0d0..1c2555b 100644 --- a/templates/import-wizard.hbs +++ b/templates/import-wizard.hbs @@ -270,45 +270,9 @@ {{!-- ================================================================= --}} - {{!-- STEP 7: Map Linking --}} + {{!-- STEP 7: Review & Import --}} {{!-- ================================================================= --}}
-

{{localize "CHRONICLE.Wizard.Steps.Maps"}}

-

{{localize "CHRONICLE.Wizard.MapsDesc"}}

- - {{#if mapLinkPlan.length}} - - - - - - - - - {{#each mapLinkPlan}} - - - - - {{/each}} - -
{{localize "CHRONICLE.Wizard.FoundryScene"}}{{localize "CHRONICLE.Wizard.ChronicleMap"}}
{{sceneName}} - -
- {{else}} -
{{localize "CHRONICLE.Wizard.NoScenes"}}
- {{/if}} -
- - {{!-- ================================================================= --}} - {{!-- STEP 8: Review & Import --}} - {{!-- ================================================================= --}} -

{{localize "CHRONICLE.Wizard.Steps.Review"}}

{{#if importRunning}} diff --git a/tools/test-import-wizard-steps.mjs b/tools/test-import-wizard-steps.mjs new file mode 100644 index 0000000..52b2b5f --- /dev/null +++ b/tools/test-import-wizard-steps.mjs @@ -0,0 +1,31 @@ +#!/usr/bin/env node +/** + * The import wizard's step list and its template panels must line up: a + * panel shows when its data-step equals the current STEPS index. Also pins + * that the scene-to-map link step stays gone: Chronicle maps materialize as + * journal pages on connect, so linking scenes did nothing. + */ + +import test from 'node:test'; +import assert from 'node:assert/strict'; +import { readFile } from 'node:fs/promises'; + +const src = await readFile(new URL('../scripts/import-wizard.mjs', import.meta.url), 'utf8'); +const hbs = await readFile(new URL('../templates/import-wizard.hbs', import.meta.url), 'utf8'); + +const stepKeys = [...src.matchAll(/\{\s*key:\s*'([a-z]+)',\s*labelKey:\s*'CHRONICLE\.Wizard\.Steps\./g)].map((m) => m[1]); + +test('step list has no maps step and ends with review', () => { + assert.deepEqual(stepKeys, ['connect', 'scan', 'types', 'tags', 'characters', 'calendar', 'review']); +}); + +test('template has one panel per step, numbered 0..n-1', () => { + const panels = [...hbs.matchAll(/class="wizard-step" data-step="(\d+)"/g)].map((m) => Number(m[1])); + assert.deepEqual(panels, stepKeys.map((_, i) => i)); +}); + +test('no scene-to-map linking is left in the wizard or the import runner', async () => { + const manager = await readFile(new URL('../scripts/sync-manager.mjs', import.meta.url), 'utf8'); + assert.ok(!/link-map/.test(src + hbs + manager)); + assert.ok(!/wizard-map-select|mapLinkPlan/.test(src + hbs)); +});