Conversation
Adds the second converter through the registry contract introduced in PR1.
Produces byte-identical output to the reference Python script
(kml_to_mif.py) on a real 1854-feature dataset (Karaganda 108 KMZ).
What it does
- Resolves per-feature colors by following styleUrl -> StyleMap(normal)
-> Style chains. KML AABBGGRR -> MapInfo decimal RGB int.
- Preserves <name> (label), <description>, and the resolved StyleId as
MID attribute columns. Adds a Folder column with the full slash-joined
KML folder path so the hierarchy is queryable in MapInfo SQL.
- Lays files out either as nested directories matching the KML Folder
tree (default) or one flat directory with `parent__child` prefixes
(`flat: true`) so they can be merged without TAB name collisions.
- Charset option: WindowsCyrillic (cp1251, default) for Russian/Kazakh
data, or Neutral (UTF-8) for MapInfo Pro 15.2+.
- Supports KML and KMZ. KMZ is unzipped in-process via a minimal pure-JS
ZIP reader (stored + deflate) — no native deps.
- Skips placemarks without geometry; reports per-file errors in stats.
New modules
- src/core/common/color.js KML AABBGGRR <-> MapInfo int <-> #RRGGBB
- src/core/common/zip.js minimal ZIP reader (zlib only)
- src/core/converters/kml-to-mif/
parseKml.js KML parsing (fast-xml-parser) +
Style/StyleMap collection + resolution
writeMif.js MIF/MID writer with CRLF normalization
and Unicode-safe filename sanitization
index.js Converter definition + run()
- test/fixtures/sample.kml polygons + line + point + StyleMap
New dependency: fast-xml-parser (zero runtime deps, MIT, popular)
Tests: 75 passing (62 from PR1 + 13 new)
- color: AABBGGRR / BBGGRR / fallback / hex round-trip
- zip: deflate + stored extraction, rejects non-ZIP buffers
- kml-to-mif: schema, full folder dispatch, flat layout, KMZ archive,
Neutral charset, resolved Pen/Brush/Symbol values per feature
Verified end-to-end: `diff -r` of the original Python output against
the JS output on the 1854-feature Karaganda 108 KMZ shows no
differences across all 18 produced files.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
UI and worker now talk to the registry instead of the legacy convert.js
engine directly. Adding a converter is one folder under
src/core/converters/ — the desktop app auto-renders its options panel
from the option schema; no UI changes required to surface it.
Renderer
- New "Converter" card with a dropdown of registered converters and a
per-converter description line.
- "Options" card rendered dynamically from the selected converter's
options[] schema (boolean -> checkbox, enum -> select, number ->
number input, string -> text). Saved per-converter so switching
direction keeps each one's last values.
- Drop-zone and file picker now filter by the current converter's
inputs.extensions (e.g. .mif/.mid for mif-to-xlsx, .kml/.kmz for
kml-to-mif).
- Removed all converter-specific HTML/JS for the Excel exporter; the
same form generator handles both shipped converters.
Worker / IPC
- worker.js dispatches converters.get(id).run({inputs, output, options},
ctx) and runs converters.validateOptions() before invoking run.
- main.js: new IPC channel converters:list returns a serializable lean
view of the registry (id, name, description, io extensions, options).
selectFiles accepts the current converter's allowed extensions.
- preload.js exposes listConverters() and the extension-aware
selectFiles(options).
- convert:start payload moved from the legacy {inputMode, inputFolder,
selectedFiles, recursive, paintRows, ...} bag to the new
{converterId, inputs, output, options} contract.
Settings
- New format v2: shared top-level (language, inputMode, inputFolder,
outputFolder, selectedFiles), plus converterId and converterOptions
keyed by converter id.
- migrate() lifts legacy flat option keys (paintRows, skipBlack,
combineIntoOneWorkbook, etc.) into converterOptions['mif-to-xlsx']
so existing users see identical behavior on first launch.
- saveSettings stamps the version on every write so future migrations
can branch on it.
i18n
- New shared keys: section.converter, field.converter,
hint.optionsEmpty. Subtitle and drop hint reworded to be
format-agnostic. English / Russian / Kazakh updated.
- Per-option labels currently come from the schema (English). Future
PR can add optional i18nKey on options without touching the
renderer.
Tests: 81 passing (75 + 6 settings migration). Renderer is browser-only
JS so it is not unit-tested here; verified via node --check.
Manual smoke (run by maintainer): `npm run dev`, pick "MapInfo -> Excel/CSV",
confirm the same form as 1.0.4 renders and conversion behavior is
unchanged; switch to "KML/KMZ -> MapInfo MIF/MID", confirm the new
options (flat, charset, recursive) render and a real KMZ converts.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Both docs now reflect the post-rebrand reality: a registry of converters, a schema-driven UI, and kml-to-mif shipped. README - Converter table: mark mif-to-xlsx and kml-to-mif as shipped; keep the planned ones for direction. - Replace the single-feature blurb with two feature sections (one per shipped converter) covering what each actually does. - Add an "Adding a new converter" section with a runnable skeleton so contributors can land a new format without reading the whole core. - Trim and reorder project structure to match the current src/core/ layout, including src/core/common/ and src/core/converters/. PROJECT_CONTEXT - Full rewrite. Old doc only described the original MIF -> XLSX desktop app; new doc describes the toolkit, the three-layer architecture (core / shell / planned CLI), the Converter contract, and how the GUI talks to the engine. - Tables: shipped converters (with implementation notes), planned converters (with rationale). - Input format assumptions split between read (MIF, MID, KML/KMZ) and write (MIF/MID from kml-to-mif), with the byte-level details that matter for compatibility (CRLF, charset, Pen/Brush/Symbol). - Settings v2 shape documented + migration policy. - Quality bar and non-goals updated for the toolkit scope. - New "Definition of done for a new converter PR" and "Guidance for future AI assistants" sections. No code touched. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds bin/mifkit — a CLI front-end over the converter registry. The GUI
and CLI now share one engine: same options schema, same validation,
same run() function. Adding a converter still requires no CLI work;
it appears automatically in both surfaces.
Commands
mifkit top-level help
mifkit --version package version
mifkit list list registered converters
mifkit help <converter> show one converter's schema
mifkit convert <converter> <inputs...> --output=<dir> [options]
Conventions
--key=value string/enum/number options
--key boolean option set to true
--no-key boolean option set to false
--output, -o required for convert
exit 0 success
exit 1 run errors (per-file)
exit 2 usage errors (unknown flag, missing --output, etc.)
Output produced by `convert` goes to stdout (one path per line) and
log/progress messages go to stderr, so output paths can be piped to
xargs / another tool without parsing logs.
New files
bin/mifkit.js Shebang entry, delegates to runCli
src/cli/index.js runCli(argv, io) — pure dispatch
so tests can drive without spawn
src/cli/parseArgs.js argv -> { command, positional, flags }
src/cli/coerceOptions.js schema-driven flag coercion +
unknown-option detection
src/cli/format.js help / list / converter-help text
package.json
- new "bin": { "mifkit": "bin/mifkit.js" }
- electron-builder "files" now includes bin/** so the CLI lands in
the desktop installer's resources too
Tests: 102 passing (81 + 21 CLI):
- parseArgs (5): command/positional/flags, --no-*, embedded =, -h/-v
- coerceOptions (7): booleans (string aliases), numbers, strings,
enum strings, missing-value error, unknown-option error, reserved
flag pass-through
- runCli end-to-end (9): top help, --version, list, help <id>, help
of unknown id, convert without args, convert without --output,
convert with unknown option, full kml-to-mif on a fixture writes
expected output filenames
Manual smoke verified on the real Karaganda 108.kmz:
./bin/mifkit.js convert kml-to-mif "Караганда 108.kmz" \
--output=/tmp/out --flat
# processes 1854 features in 9 layers, exit 0
Docs
README gains a "CLI" section with all conventions and an updated
project structure including bin/ and src/cli/. PROJECT_CONTEXT moves
the CLI from "planned" to "shipped" and includes the new files in
the layout block.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This was referenced May 19, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Bundled merge of the kml-to-mif converter, schema-driven UI dispatch, docs refresh, and CLI on top of the registry caved-in by #1. Four logical PRs (originally #2 #3 #4 #5) collapsed into one merge after their stacked bases got removed during #1's merge.
Commits in this PR (each one was a separate PR review-wise before the stack broke):
feat: kml-to-mif converter (KML/KMZ -> MapInfo MIF/MID)— was Add kml-to-mif converter (KML/KMZ → MapInfo) #2feat: schema-driven UI and worker dispatch by converter id— was Schema-driven UI and worker dispatch by converter id #3docs: refresh README and PROJECT_CONTEXT for multi-converter shape— was docs: refresh README and PROJECT_CONTEXT for multi-converter shape #4feat: mifkit CLI built on the same registry as the GUI— was Stack: kml-to-mif + schema-driven UI + docs refresh + CLI #5What lands
kml-to-mif converter — KML/KMZ → MapInfo MIF/MID, preserving per-feature colors via
styleUrl → StyleMap → Styleresolution, withName/Description/StyleId/Folderattribute columns. Byte-identical to the reference Python script on a real 1854-feature dataset.Schema-driven UI — renderer reads the converter registry on startup and builds its dropdown + options form from each converter's
options[]schema. Adding a converter requires no UI code.Settings v2 + migration — per-converter option storage, legacy flat keys auto-migrated from 1.0.x users on first load.
mifkitCLI —mifkit list,mifkit help <id>,mifkit convert <id> <inputs...> --output=<dir> [--flag=value]. Same registry, same validation, samerun()as the GUI. stdout = output paths, stderr = logs. Bin registered inpackage.json, also bundled into the electron-builder installer.Docs — README has Converter table, CLI section, "Adding a new converter" guide. PROJECT_CONTEXT is a full rewrite for the toolkit shape (three layers, contract, settings, quality bar).
Tests
102 passing locally (
npm test):Smoke
npm testis green./bin/mifkit.js convert kml-to-mif "Караганда 108.kmz" --output=/tmp/out --flatprocessed all 1854 features into 18 files, exit 0🤖 Generated with Claude Code