@tileflow/dev 0.1.0-alpha.31 → 0.1.0-alpha.33

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.
@@ -0,0 +1,145 @@
1
+ # Beacon map audits (legacy)
2
+
3
+ The Beacon scoring and tour interface has been replaced by **Diagnostics**. See
4
+ [Map design diagnostics](./map-design-diagnostics.md) for the current workflow. This document
5
+ describes historical audit reports; its scores are not comparable with Diagnostics findings.
6
+
7
+ Open `tileflow preview`, select **Beacon**, choose **Desktop** or **Mobile** and
8
+ **Active tour** (default), **Quick check** or **All zooms**, then **Run audit**. The report includes 48 checks grouped into Rendering (9), Loading
9
+ (8), Legibility (8), Accessibility (8), Cartography (7) and Integrity (8). Expand a result for its
10
+ measured value, target, evidence and suggested fix. Findings open Layers, Tiles or Debug. **Copy
11
+ context for agent** copies the bounded report; it does not upload it.
12
+
13
+ ## Scope and restoration
14
+
15
+ Active tour follows a deterministic route through the current view, London, New York, Tokyo,
16
+ Nairobi and São Paulo. Each location has an overview and a detailed view, with zoom, bearing and
17
+ pitch changes within the map's camera limits. Destinations outside explicit map bounds are skipped
18
+ and listed in coverage. The current theme stays active; this route does not cover every theme.
19
+
20
+ The tour has a 60-second budget: up to 55 seconds for navigation and sampling, with the remainder
21
+ reserved for diagnostics. Each stop measures a 1.2-second flight, waits up to two seconds for tiles,
22
+ then samples up to 2.4 seconds of continuous settled repaints. On a responsive map the run usually
23
+ takes 45–60 seconds. Slow loading can exhaust the budget: remaining views are explicitly unmeasured,
24
+ not passed. Synchronous analysis and restoration may add a small overhead. Reduced-motion settings
25
+ replace flights with jumps; the report does not claim an animated navigation measurement then.
26
+
27
+ **Tour measurements** lists the actual camera, moving/settled p95 frame intervals, sample counts,
28
+ remaining tile wait after navigation, and total readiness including navigation for each view.
29
+ Loading scores use the remaining wait, excluding scripted animation; any timed-out view fails
30
+ readiness. A p95 requires at least 30 intervals; coverage flags fewer than 60 settled
31
+ intervals. A view that did not finish loading before sampling receives no settled performance score.
32
+ The rendering check uses the worst qualifying per-view p95, so a slow location is not hidden by an
33
+ average across fast locations. Long stalls are retained. These measurements include browser
34
+ scheduling and other work on the machine, rather than isolated GPU execution time.
35
+
36
+ Quick check samples the current camera and concrete theme for about three seconds. It is useful
37
+ for static style checks and a short settled rendering sample. All zooms visits each integer zoom
38
+ from 0 through 22 within map limits at the same geographic centre, for every registered theme and
39
+ time-of-day variant. It waits up to five seconds for readiness and samples 150 ms per view. This
40
+ exhaustive scope can take several minutes; it is not the one-minute navigation benchmark. Use
41
+ **New audit** from a result to change scope or viewport.
42
+
43
+ Desktop uses an actual 1280×800 CSS iframe viewport; Mobile uses 390×844. The iframe is scaled to
44
+ fit the workspace. CPU, GPU and network are those of the current computer: this is not phone
45
+ hardware emulation. No cache clearing, network throttling or cold navigation occurs.
46
+
47
+ Camera, viewport, pixel ratio, overlays and scene are restored after completion or cancellation.
48
+ Moving the map interrupts the audit and keeps the user's camera. Leaving Beacon cancels the run.
49
+ A map replacement invalidates the run. Reports and preview changes stay local and in memory.
50
+
51
+ ## Measurements and limits
52
+
53
+ - **Rendering:** inter-render p95 during continuous repaints (moving and settled windows for Active tour), native overdraw samples (saturating at
54
+ 8 passes), eligible layer count, intercepted WebGL draw calls, CPU placement time, data-driven
55
+ paint-rule count, custom-renderer CPU submission time, and an observable GPU allocation estimate.
56
+ CPU submission is not GPU execution time. Readbacks and feature inspection are outside the frame
57
+ sample. GPU memory excludes inaccessible allocations; a low estimate is informational.
58
+ - **Loading:** unused source-layer MVT message bytes, original map-load event time, individual tile
59
+ sizes when exposed, view readiness, per-view transfer and request counts, compression and cache
60
+ observations. PMTiles archive ranges are not individual tile sizes. Resource Timing cannot prove
61
+ Cache-Control policy. Cached audit views are not a fresh visitor estimate.
62
+ - **Legibility:** observed text-placement rejections, line-label candidates, nearby repeated label
63
+ anchors, a scored text-size budget, approximate density, halos, configured line curvature and
64
+ requested language-field coverage. Rejections can result from collisions, offscreen placement or
65
+ line fit; the report does not pretend to know the renderer's exact rejection cause.
66
+ - **Accessibility:** sampled text contrast in current/night/other themes, color-vision simulation
67
+ heuristics, static non-text contrast, categorical color-only rules, the system motion preference,
68
+ and sampled label retention after temporarily setting the real canvas ratio to 3×. Contrast uses
69
+ evaluated colors and supported flat fill/halo backgrounds. Raster, 3D and unresolved backgrounds
70
+ are excluded. This is diagnostic evidence, not accessibility certification.
71
+ - **Cartography:** enabled zoom-range gaps, potential abrupt layer entry, road and place hierarchy
72
+ heuristics, scored size-rule continuity, unchanged eligible-layer counts and sampled fill coverage.
73
+ Naming-based hierarchy checks explicitly identify their inferred roles and exclude unmatched IDs.
74
+ - **Integrity:** missing icon references, actual theme coverage, hidden-layer source residency,
75
+ cached missing glyphs, unresolved expressions, locally unmatched filters, style-spec validation
76
+ and retained runtime/resource errors. Resident data is bounded; zero local matches does not mean
77
+ a filter never matches globally.
78
+
79
+ Samples are bounded to 4,000 rendered query results, 100 labels and 64 resident vector tiles per
80
+ view; feature expression checks use up to 24 candidates spread across the bounded source sample per
81
+ layer. Filter evidence counts only the candidates actually evaluated. Geometry is discarded after
82
+ observation. All-zoom style snapshots are shared per theme. Custom renderer and private MapLibre
83
+ observations are version-isolated and can be unavailable on unsupported renderers.
84
+
85
+ ## Scores and useful comparisons
86
+
87
+ Category scores average checks with a measured score. Overall score averages measured categories.
88
+ Informational and unavailable checks are excluded. Legibility and Cartography show a numeric score
89
+ when eligible samples exist, together with the specific score basis and sample count. Each currently
90
+ has one scored benchmark; its other checks remain diagnostic. Their scores do not assess the entire
91
+ visual quality of a map.
92
+ A category with no measurable scores shows **—**. For numeric
93
+ budgets, meeting the displayed target scores 100; exceeding it by up to 1.5× scores 70, by up to 2×
94
+ scores 45, and beyond that scores 0. Minimum targets invert the ratio. Structural counts and cartographic heuristics are explicitly marked **Diagnostic · not scored**.
95
+ These include expression counts, rejected candidates, localized name fields, halos, fill coverage
96
+ and locally unmatched filters. They remain visible as evidence without asserting a defect.
97
+ Error scoring includes only resources and runtime errors observed during the audit, not prior session failures. A high score does not establish global map quality.
98
+
99
+ The two quality benchmarks use an explicit percentage, rather than the generic budget tiers:
100
+
101
+ - **Legibility / Text-size budget:** percentage of sampled labels whose evaluated `text-size` is
102
+ at least 10 CSS px. The threshold is a design budget, not an accessibility standard.
103
+ The bounded sample rotates between rendered symbol layers so dense POI layers do not monopolize it.
104
+ Font metrics, formatted section scaling, script, perspective and final glyph rasterization are not measured.
105
+ Unresolved expressions are excluded; their renderer fallback does not count as a passing size.
106
+ - **Cartography / Size-rule continuity:** percentage of sampled layer/view size rules without a
107
+ jump exceeding both 1 px and 25% of the smaller size (with a 1 px denominator floor). For line
108
+ width, text size and circle radius, evaluate immediately around the actual zoom stops at
109
+ `stop ± 0.0001`, within the layer and camera zoom bounds. Continuous interpolation and physical
110
+ width scaling pass, even when sizes double between integer zooms. Changes at layer entry/exit,
111
+ filtered-out features and stops with opacity at or below 1% on either side are excluded.
112
+ Each ready view checks up to 128 visible layers, four feature contexts per layer and 128 stops
113
+ per rule. Missing or oversized evaluations are excluded. This measures authored rule continuity,
114
+ not final GPU pixels, label placement or transition animations. Intentional steps can exceed
115
+ the budget and should be reviewed before changing them.
116
+
117
+ Only views that finished loading contribute to these scores. Each check exports `sample` with
118
+ `passed`, `total`, `skipped` and `unit`; category `scoreBasis` identifies what its number represents.
119
+ An observed outlier remains a warning even when rounding produces a high percentage. Findings
120
+ include the layer and camera, with the exact zoom stop for a size jump. No eligible samples means
121
+ unavailable, never an automatic 100. Repeated features in different views are separate observations.
122
+
123
+ Repeat with the same view, scope, device, cache conditions and browser load when comparing changes.
124
+ Review the evidence before acting on a warning. Copying findings supplies an agent with concrete
125
+ layer IDs, camera context, measured values, coverage limits and suggested investigation steps.
126
+
127
+ ## Reproducible reports and future automation
128
+
129
+ Reports use `version: 4`, `scoring: "measured-evidence-v3"` and identify the deterministic tour as `protocol: "active-tour-v1"`.
130
+ Scenarios carry stable IDs, location, actual camera, theme, frame sample counts, p95, long-frame
131
+ fraction, maximum frame interval, `tileWaitMs` after navigation, end-to-end `readyMs` and observed
132
+ network transfer. Policy v2 excludes scripted camera time from loading scores; v1 included it.
133
+ Policy v3 adds the sampled text-size and rule-continuity benchmarks described above; v2 left both
134
+ categories entirely diagnostic. Old reports retain their original scores and must be rerun to use v3.
135
+ `plannedViews` and
136
+ `budgetExhausted` distinguish completed coverage from a partial run. Camera, viewport, DPR, duration
137
+ and warnings accompany the scores. Change the protocol identifier when its itinerary or sampling
138
+ rules change. The scoring policy is versioned separately: do not compare aggregate scores across
139
+ policies. The original Active tour itinerary, timing windows and thresholds for measured frame
140
+ performance are unchanged. There is no CI runner or GitHub integration in this feature.
141
+
142
+ For future comparisons, keep protocol, map revision, initial view, browser/device, viewport, theme,
143
+ DPR and cache conditions fixed. A single one-minute run is a diagnostic sample; repeated runs on a
144
+ controlled worker are needed before setting regression gates. The tour does not clear caches,
145
+ emulate mobile hardware, or prove worldwide coverage from six locations.
@@ -40,13 +40,12 @@ const result = await compileTileflowIconPackages(internalCatalog, {
40
40
  ```
41
41
 
42
42
  Streets declares `[streetsIcons]` and keeps light/dark image-token targets in that one closure.
43
- Baedeker, Cívica, Ferraris, Härad, Siegfried, Soundings, and Blueprint declare only
44
- `[baedekerIcons]`, `[civicaIcons]`, `[ferrarisIcons]`, `[haradIcons]`, `[siegfriedIcons]`,
43
+ Baedeker, Cívica, 1777, Siegfried, Soundings, and Blueprint declare only
44
+ `[baedekerIcons]`, `[civicaIcons]`, `[map1777Icons]`, `[siegfriedIcons]`,
45
45
  `[soundingsIcons]`, and `[blueprintIcons]`, respectively. No official root composes another map's assets even though all
46
46
  use the semantic compiler ABI. Baedeker's directory contains eight original travel-atlas patterns;
47
47
  its browser-derived Mapterhorn contours use runtime terrain tiles and are not part of the icon
48
- package. Härad's directory contains nine original Tileflow patterns inspired by Lantmäteriet's CC0
49
- Häradsekonomiska kartan series from 1859–1934. Soundings owns ten original nautical symbols and
48
+ package. Soundings owns ten original nautical symbols and
50
49
  patterns. Blueprint owns four technical blueprint patterns and one schematic POI node; its
51
50
  unbundled Mapterhorn contours remain runtime terrain, and its text uses the canonical Noto Sans
52
51
  glyph provider. An application map may inherit a root's exact array, replace it, clear it with `[]`,
@@ -0,0 +1,92 @@
1
+ # Layers inspection
2
+
3
+ Open the local preview and select **Layers**. The panel inspects the current map without changing its
4
+ authored style. **By module** groups physical layers using compiler contributions; shared layers retain
5
+ every owner. **Draw order** lists the renderer's public layer sequence, latest draw calls first.
6
+ Custom runtime layers are included even though they are absent from Style JSON. DevTools overlays
7
+ are excluded. Unknown ownership is labeled **Unattributed**, rather than guessed from an ID.
8
+
9
+ Press **I** or the element-picker icon and click an element on the map. Its layer becomes selected in
10
+ Layers and **Rules**, the first detail tab, opens with values evaluated for the picked feature. The
11
+ picked feature is a snapshot from that click; changing the layer or artifact/scene revision clears it.
12
+ Inspect no longer has a separate top-level tab. Legacy `tab=inspect` links resolve to Layers.
13
+
14
+ Enable **Visible only** beside the organization buttons to hide hidden/out-of-zoom layers and vector
15
+ layers with no rendered features in the current viewport. The filter combines with search and both
16
+ organization modes, and refreshes after camera movement, style changes and tile loading. One viewport
17
+ query serves the whole list after a 250 ms pause. During movement the last result remains stable;
18
+ failed queries do not classify layers as empty. Custom, raster and background layers remain listed
19
+ when their visibility cannot be measured; explicit hidden runtime state is excluded. This is a
20
+ rendered-feature filter, not a measurement of final pixels, transparency or occlusion.
21
+
22
+ Search by ID, semantic target, module, type, source or source-layer. Click anywhere on a row, or use
23
+ Up/Down and Home/End from a focused row. Selection stays in Layers and is retained in the `layer` URL
24
+ parameter. If a selected ID disappears, its detail shows that absence and keeps the ID for when it
25
+ returns. The Layers tab remains in the existing `tab=layers` parameter.
26
+
27
+ ## Read the evidence
28
+
29
+ Open **Diagnosis** in the selected layer's detail tabs. With a picked feature or a
30
+ Data sample, it explains the filter result and any evaluated zero-size or zero-opacity components.
31
+ A zero component is not proof that the whole layer is invisible. Diagnostics findings can open a
32
+ specific paint, layout or filter declaration directly in Rules, highlighting its row.
33
+
34
+ - **Diagnosis** reports presence, declared visibility, the inclusive minimum/exclusive maximum zoom
35
+ range, source loading, shared source tiles and viewport-query observations. Pixel visibility,
36
+ occlusion, transparency and depth are explicitly unmeasured. Later draw calls do not necessarily
37
+ cover every earlier layer. Runtime 3D controllers report only the state and statistics they expose.
38
+ - **Rules** shows paint and layout values inline, without expandable rows, alongside their zoom,
39
+ feature, feature-state, global-state and image dependencies. Values use the current zoom and the
40
+ picked feature or selected data sample. Edit a value and press Enter or leave the field to preview
41
+ it; color pickers update live while open. Enumerations and booleans use selectors. Invalid values
42
+ show an error and leave the map unchanged. Edits override the entire physical layer across zooms,
43
+ including when a picked feature supplied the evaluated value. Each modified rule has a reset
44
+ button that restores its original expression or renderer default. Paint checkboxes temporarily
45
+ remove a declaration from the renderer so its default applies. The disabled row stays visible and
46
+ crossed out, showing its retained value at the current view; re-enabling restores the exact saved
47
+ expression or override. Toggles share edit history and appear in copied context. Implicit defaults
48
+ cannot be disabled; edit one to create an explicit rule first. **Undo** and **Redo** beside the
49
+ three-dot menu share the temporary style history. **Reset all changes** in that menu restores
50
+ temporary style and geometry edits and clears their history. Reload clears temporary edits;
51
+ config, source data and build artifacts stay unchanged. Filters remain read-only. Copy context includes the raw rules and temporary edits.
52
+ Evaluation does not reproduce placement, transitions or final pixel colors.
53
+ - **Data** queries only the selected layer after the viewport settles. At most 80 returned fragments
54
+ are retained; the renderer's query itself may return more. Fragments can duplicate logical features
55
+ across tile boundaries or world copies. Zero results are not proof that a filter excluded all data.
56
+ Unsupported custom/raster/background queries are unavailable, not zero. Choose an actual sample
57
+ to inspect its properties, or use **Use for rules** to evaluate that fragment in Rules. Inline
58
+ source data is excluded from source details.
59
+ - **Origin** preserves sidecar owners, targets, slots and operations. Semantic module paths do not
60
+ establish exact source-file or line provenance; token paths appear only if explicitly supplied.
61
+
62
+ ## Follow a layer through the map
63
+
64
+ Hover or keyboard-focus a layer row to highlight its sampled geometry live in the current map view.
65
+ Click a row to keep that layer highlighted. Hovering another row temporarily replaces the highlight;
66
+ leaving restores the clicked layer, or clears the preview when no row was clicked. Closing the panel
67
+ clears both previews. Hover and click work with the element-picker mode on or off. The camera stays unchanged. The preview refreshes when map movement settles or source tiles
68
+ change, even when the hovered layer differs from the selected layer. Hover is coalesced over 80 ms;
69
+ queries are cached for the current view. There is no separate **Highlight on map** button.
70
+
71
+ Overlays retain at most 30 fragments and 25,000 vertices. They use separate scratch sources and never
72
+ change original paint, layout, source data or feature state. A highlight outlines queried geometry;
73
+ it is not a reconstruction of glyphs, symbols or 3D meshes. Hidden, out-of-zoom, custom and raster
74
+ layers without queryable rendered geometry report that limitation instead of moving the camera.
75
+
76
+ **Tiles** inspects tiles grouped by source. Tile costs belong to the shared source, not to a
77
+ single style layer. Sprite and font links in Rules open Assets. Other inspection tabs remain usable
78
+ while compilation or tile loading continues; obsolete scheduled measurements are discarded.
79
+
80
+ ## Give an agent context
81
+
82
+ **Copy context** opens a frozen preview in Markdown or JSON. Add an optional investigation question,
83
+ then copy it. The packet includes the selected ID, map/theme, generation, scene revision, camera,
84
+ viewport, rules, provenance, diagnostic evidence, source, runtime state, recent changes and temporary
85
+ edits. It retains at most eight feature property samples, excludes their geometries and inline source
86
+ data, removes credentials and query strings from source URLs, and reports projection omissions.
87
+ Nothing is sent to an agent automatically.
88
+
89
+ While Layers is active, the same observation replaces stale feature selection in the local
90
+ `tileflow inspect session --url http://127.0.0.1:3333 --json` context. The CLI applies an additional
91
+ bounded projection and reports omitted paths. Temporary edits are observations; capture commands
92
+ still use the compiled map rather than reproducing scratch edits.
@@ -1,10 +1,17 @@
1
- # Local comparison workbench and compiler inspection
1
+ # Map DevTools and compiler inspection
2
2
 
3
3
  Start with the [@tileflow/dev guide](https://github.com/tileflow/tileflow-sdk/blob/main/packages/dev/README.md) for installation and a complete first example.
4
4
 
5
- The CLI exposes the complete authoring surface through `tileflow preview --against-map ...` or
6
- `--against-config ...`. Custom local servers can compose the same shell around two existing dev
7
- handlers:
5
+ The CLI opens Map DevTools through `tileflow preview`. It uses one map and one watched artifact
6
+ session by default. A developer-tools panel groups Map, Layers, Tiles, Assets, Debug and Diagnostics. Camera controls
7
+ occupy a separate row below the navigation. Use the three-dot menu to dock left, right or below
8
+ the map and choose System, Light or Dark appearance. Drag the divider to resize it; focused dividers
9
+ also support arrow keys. Dock position and dimensions persist locally. Layer observations and highlights stay local to the preview. The map fills the remaining
10
+ workspace without brand headers or map-status badges. Closing the panel leaves a small **DevTools**
11
+ button to reopen it; capture and comparison controls are absent from the interface.
12
+
13
+ Add `--against-map ...` or `--against-config ...` for synchronized comparison. Custom local servers
14
+ can compose the same shell around existing dev handlers; omit `right` for a single-map session:
8
15
 
9
16
  ```ts
10
17
  import {createTileflowComparisonRequestHandler} from '@tileflow/dev/server';
@@ -27,11 +34,22 @@ const fetch = createTileflowComparisonRequestHandler({
27
34
  });
28
35
  ```
29
36
 
37
+ Set `fitViewport: true` on each dev handler embedded in a resizable Map DevTools pane. Scene previews
38
+ then fit their pane while captures retain the scene's declared frame. Standalone dev handlers keep
39
+ the fixed scene viewport by default.
40
+
30
41
  Each side handler retains ownership of its styles, fonts, sprites, events, diagnostics, and
31
- last-known-good artifact generation. The comparison router only owns the root HTML and strict
32
- dispatch to two disjoint same-origin route prefixes. Its synchronized camera, URL state,
33
- side-by-side/split/overlay/blink modes, rendered-feature inspector, zoom-curve sampling, sprite
34
- atlas, and scene/capture-command copying require no hosted service.
42
+ last-known-good artifact generation. The comparison router owns the shell, its bundled UI fonts and
43
+ strict dispatch to disjoint same-origin route prefixes. An optional `workbench: {url, handle}`
44
+ adapter enables theme selection and browser context. Existing CLI capture-record and comparison
45
+ APIs remain separate from this interface. Custom servers that omit the adapter retain inspection,
46
+ zoom-curve sampling and the sprite atlas. Explicit two-map server configurations retain synchronized
47
+ cameras and URL-driven side-by-side/split/overlay/blink layouts without comparison controls.
48
+
49
+ See the [human/agent workflow](https://github.com/tileflow/tileflow-sdk/blob/main/packages/cli/docs/map-authoring.md#work-with-a-person-and-an-agent)
50
+ for retained evidence, context bounds and `inspect session`. Browser context reports the observed
51
+ camera and viewport; capture commands render compiled source without temporary preview edits. The rendered feature summary shows available semantic paths and
52
+ keeps raw feature data in a disclosure. It does not infer exact source lines or token ownership.
35
53
 
36
54
  Set `inspection: true` on `createTileflowBuildArtifacts` or
37
55
  `createTileflowArtifactSession` to compute `artifacts.styleInspections[map][theme]`. A dev handler
@@ -62,166 +80,51 @@ prepared local-font bundle before authentication until managed font storage is a
62
80
  map that declares `glyphs` replaces the inherited local provider atomically; the URL-backed map is
63
81
  complete as declared and independent of its World selection.
64
82
 
65
- With both controls off, the built-in preview draws the style as compiled, as Capture and the
66
- published framework adapters draw it: flat trees, building layers marked for the 3D control left
67
- hidden, and no street lights. This keeps an ordinary `tileflow preview` session WYSIWYG with
68
- production and visual baselines.
69
-
70
- The `3D ON/OFF` and `TREES ON/OFF` controls mount Core's 3D layers as an application does, from
71
- the description the compiled style publishes. When the style describes 3D buildings
72
- (`tileflow:buildings`), `3D ON` raises them with `createTileflowBuildings3D` in place of the
73
- compiled extrusion and lights the streets with `createTileflowStreetLights` where the style
74
- describes them. When it describes 3D trees (`tileflow:vegetation`), `TREES ON` stands them with
75
- `createTileflowTrees3D`, which hides the style's flat trees while they stand. Every look the
76
- preview shows comes from the map's modules; the preview holds no appearance of its own, and the
77
- theme on screen supplies the sun. Neither mode is capture evidence. Both choices survive reloads
78
- and shared links through the `buildings3d=on|off` and `trees3d=on|off` URL parameters.
79
-
80
- The same control toggles any compiled layer whose metadata contains
81
- `tileflow:3d-toggle = building`. It switches the building volumes independently from the camera,
82
- so enabling 3D never changes the pitch and users remain free to tilt the map with pointer controls.
83
-
84
- ### Detailed landmark models
85
-
86
- A hidden style layer can opt the Streets preview into detailed GLB landmarks by setting
87
- `tileflow:landmark-manifest-url` in its metadata. The bounded version-2 manifest indexes GLB entries
88
- inside one or more PMTiles archives and is fetched only when the 3D building control is enabled near
89
- the landmark runtime zoom. Archive URLs may be absolute or relative to the manifest URL.
90
-
91
- ```json
92
- {
93
- "schemaVersion": 2,
94
- "id": "madrid-landmarks-2026-08-19",
95
- "minzoom": 16,
96
- "maximumVisibleModels": 8,
97
- "maximumCachedModels": 16,
98
- "archives": [
99
- {
100
- "id": "madrid-v1",
101
- "url": "./madrid-landmarks-v1.pmtiles",
102
- "bytes": 14832912,
103
- "sha256": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
104
- }
105
- ],
106
- "landmarks": [
107
- {
108
- "id": "palacio-cibeles",
109
- "center": [-3.6921, 40.4193],
110
- "bounds": [-3.693, 40.4186, -3.6912, 40.42],
111
- "priority": 100,
112
- "models": [
113
- {
114
- "minzoom": 16,
115
- "archiveId": "madrid-v1",
116
- "z": 0,
117
- "x": 0,
118
- "y": 0,
119
- "bytes": 481024,
120
- "sha256": "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
121
- "axisConvention": "EUN_Y_UP"
122
- },
123
- {
124
- "minzoom": 18,
125
- "archiveId": "madrid-v1",
126
- "z": 1,
127
- "x": 0,
128
- "y": 0,
129
- "bytes": 1276416,
130
- "sha256": "cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc",
131
- "axisConvention": "EUN_Y_UP"
132
- }
133
- ]
134
- }
135
- ]
136
- }
137
- ```
138
-
139
- `maximumVisibleModels` is bounded to 1–64. `maximumCachedModels` defaults to twice that value,
140
- must be at least the visible limit, and is bounded to 128. Higher `priority` values win when more
141
- landmarks intersect the viewport than can be shown. `models[0]` is the base LOD and must start at the
142
- manifest `minzoom`; later entries replace it at their distinct `minzoom`. The parser bounds manifest,
143
- archive, landmark, model and GLB-entry sizes, rejects credential-bearing or non-HTTP archive URLs,
144
- and verifies each extracted entry's declared byte length and SHA-256 before parsing it.
145
-
146
- The preview starts the Three.js runtime and manifest request together one zoom before the landmark
147
- layer becomes visible. A landmark entering directly at a close zoom always displays its base LOD
148
- first and upgrades in place. Nearby base LODs are warmed in the parsed-model cache. Style layers
149
- marked with `tileflow:landmark-fallback = true` remain visible until the corresponding active GLB is
150
- ready, so a cold request never leaves an empty building volume.
151
-
152
- Embedded Streets previews also accept a same-origin parent command without reloading the document:
153
-
154
- ```js
155
- iframe.contentWindow.postMessage(
156
- {
157
- type: 'tileflow:set-map-state',
158
- schemaVersion: 1,
159
- state: {
160
- center: [-3.688344, 40.453053],
161
- zoom: 17.75,
162
- bearing: -24,
163
- pitch: 58,
164
- buildings3d: true,
165
- trees3d: false,
166
- visibleLayerGroups: ['labels', 'roads', 'buildings', 'landuse', 'water'],
167
- },
168
- },
169
- location.origin,
170
- );
171
- ```
172
-
173
- The command is applied atomically only for the embedding parent, with exact same-origin and numeric
174
- range checks. `visibleLayerGroups` is optional and accepts only `labels`, `pois`, `roads`, `transit`,
175
- `buildings`, `landuse`, and `water`; omitted groups are hidden while base background and attribution
176
- remain. Camera and toggle state continue to be written to the preview URL after application.
177
-
178
- GLB base colors, textures, emissive values, metalness, and roughness are authoritative and are not
179
- rewritten by the preview. Models outside the active selection remain hidden in a least-recently-used
180
- cache; leaving the cache disposes their geometry, materials, and textures.
181
-
182
- The vector building represented by a detailed model must carry `hide_3d = true`. The ordinary
183
- building extrusion and both screen-space shadow passes filter on that field, preventing the coarse
184
- volume from intersecting the GLB while retaining the flat footprint below it. This flag belongs in
185
- the vector-tile dataset; the landmark endpoint cannot substitute for it. Version model and GLB URLs
186
- so they can be served with immutable caching. Cross-origin endpoints must allow credentialed GETs
187
- because the development renderer requests the manifest and models with credentials.
188
-
189
- If a map uses `icons: ['./icons/brand']`, build artifacts include the generated MapLibre
190
- `sprite.json`, `sprite.png`, `sprite@2x.json`, and `sprite@2x.png` beside its style. Ordinary sources
191
- are normalized into 24 px icon cells. A source named `<id>.pattern.svg` (or another supported raster
192
- format with the same marker) instead keeps its intrinsic dimensions and is published as `<id>`.
193
- This provides calibrated, non-square textures for line and fill patterns while the runtime still
194
- consumes only the generated PNG sprite. Pattern widths must be a power of two from 2 through 512
195
- pixels, matching MapLibre's seamless line-pattern contract.
196
-
197
- `icons` is one ordered array. Omitting it inherits the parent's exact array, declaring it replaces
198
- the array atomically, and `[]` means no icons. Use a spread to preserve imported directories while
199
- adding or replacing files:
200
-
201
- ```ts
202
- import {defineMap} from '@tileflow/core';
203
- import {streets} from '@tileflow/maps';
204
-
205
- export default defineMap({
206
- id: 'brand-map',
207
- version: 1,
208
- extends: streets,
209
- icons: [...streets.icons, './icons/brand'],
210
- });
211
- ```
212
-
213
- Directories apply left to right. `<id>.<ext>` publishes an icon as `<id>`;
214
- `<id>.pattern.<ext>` publishes the intrinsic-size pattern as `<id>`. The published ID must already
215
- be canonical lower-kebab-case. A later file replaces an earlier file only for the same exact ID;
216
- case-only collisions fail. Package maps export `streetsIcons`, `baedekerIcons`, `civicaIcons`,
217
- `ferrarisIcons`, `haradIcons`, `siegfriedIcons`, `soundingsIcons`, `blueprintIcons` directory descriptors from
218
- `@tileflow/maps`. Baedeker, Cívica, and Siegfried also export
219
- `baedekerFonts`, `civicaFonts`, and `siegfriedFonts`. Preparation
220
- resolves package descriptors inside their owning package, checks real-path containment, and
221
- compiles them through the same pipeline as a config-relative directory. There are no
222
- built-in/source/sprite selectors, mappings, icon-specific inheritance, or compatibility aliases.
223
-
224
- Local directories resolve from the directory containing the selected `tileflow.config.ts`, while
225
- the workspace `cwd` remains their containment boundary. Canonical local syntax starts with `./` or
226
- one or more leading `../` segments and then uses only non-empty segments other than `.` or `..`;
227
- trailing or repeated slashes, backslashes, controls, and paths longer than 512 characters fail.
83
+ ### Pick an element in Layers
84
+
85
+ Press **I** or the element-picker icon, hover for a layer/type/ID hint, then click the map.
86
+ The clicked element selects its physical layer in **Layers** and opens **Rules**, the first detail tab.
87
+ Expression values use that picked feature, rather than an arbitrary viewport sample. **Data** shows
88
+ its properties and additional viewport samples; **Use for rules** chooses a different sample.
89
+ There is no separate Inspect tab. Legacy `tab=inspect` links open Layers.
90
+
91
+ Selecting another layer clears the picked-feature context. Compiler and scene revisions invalidate
92
+ old feature snapshots. The renderer's layer ID remains selected across reloads through the `layer`
93
+ URL parameter; missing IDs are reported explicitly. Rules expose inline temporary paint/layout
94
+ editors for the entire physical layer, with per-rule reset and shared undo history. Preview edits do
95
+ not write config, source data or build artifacts. See [Layers inspection](./layers-inspection.md) for diagnostic limits,
96
+ map highlighting, provenance and agent context.
97
+
98
+ ### Tiles
99
+
100
+ **Tiles** shows in-view resident tiles in a spatial grid and source-grouped list, with canonical
101
+ `z/x/y`, display zoom, state and raw tile bytes where available. Filter by source or coordinates.
102
+ Select a tile to outline its bounds and inspect **Source layers**, **Features** and **Info**.
103
+ Source-layer rows link to consuming style layers. Features are paginated in groups of 50 and can
104
+ be copied as real GeoJSON. Counts describe tile fragments, including buffer geometry, rather than
105
+ unique objects across tiles. **Show on map** fits the selected bounds.
106
+
107
+ Raster DEM tiles show decoded grid dimensions and elevation statistics. Raster imagery has no
108
+ vector features. Eviction, unavailable internals and unsupported MLT decoding are reported explicitly.
109
+ Inspection decodes resident MVT buffers on demand without requesting tiles.
110
+
111
+ The view reads the pinned MapLibre renderer's tile managers through an isolated adapter. A resident
112
+ tile can come from cache and need not have a new HTTP request. Unsupported renderer internals
113
+ produce an unavailable state. Network does not list application scripts, development-server
114
+ traffic or other HTTP resources. Sprite and font sources remain in **Assets**.
115
+
116
+ ### Assets and debug overlays
117
+
118
+ **Assets** separates Icons, 3D models and Fonts. Icons lists registered images, including runtime
119
+ additions, with lazy thumbnails and searchable IDs. Theme and time-of-day filters load the actual
120
+ registered theme atlases; the selected icon's sidebar compares its day/night variants and links to
121
+ consuming layers. Missing variants are marked, never synthesized from a naming convention.
122
+
123
+ 3D models previews bounded samples of the actual loaded building and vegetation meshes. Counts,
124
+ bytes, LODs and available instance counts describe controller data. The wire geometry preview is
125
+ not the map's lighting renderer. Fonts renders editable specimens from the actual font or glyph
126
+ provider and shows observed glyph ranges and consuming layers. Pending requests are cancelled
127
+ when leaving the panel; unavailable sources do not silently use a fallback font.
128
+
129
+ **Debug** offers collision boxes, overdraw with a color legend, tile boundaries and real pixel-ratio
130
+ controls. **Diagnostics** lists design findings and optional drawing-cost observations; see [Map design diagnostics](./map-design-diagnostics.md).
@@ -0,0 +1,76 @@
1
+ # Map design diagnostics
2
+
3
+ Open **Diagnostics** in Map DevTools to investigate a map's rules, visual resources and current-view
4
+ composition. The list groups findings under **Readability**, **Style**, **Assets** and **Performance**.
5
+ **Problem** means a confirmed configuration or resource failure. **Review** means an observed
6
+ situation may be intentional. Counts refer to grouped findings, not quality scores.
7
+
8
+ ## Check the map
9
+
10
+ Style observations refresh after changes settle. **Check view** explicitly collects samples from
11
+ the current map and briefly profiles drawing work. It does not navigate to other cities, change the
12
+ camera, compile themes or save edits. Changing the map or leaving the panel cancels pending work.
13
+ Expensive renderer observations are not repeated continuously while editing colors or dragging.
14
+
15
+ The initial detectors cover invalid style rules, broken references and zoom ranges, unresolved
16
+ icons, verifiable text contrast, large layer stacks, potentially repeated rules and drawing work.
17
+ Findings group affected layers and keep bounded examples. A missing image must remain unresolved
18
+ after loading; images with a working fallback are not missing resources. An empty viewport or a
19
+ layer outside its zoom range does not by itself create a global problem.
20
+
21
+ Text contrast is checked only where the evaluated text and opaque halo make the color pair
22
+ verifiable. This is not a measurement of all pixels behind a label or certification that a map is
23
+ readable. Unavailable evaluation context does not become a passing observation. Size thresholds,
24
+ absence of halos and ordinary collision rejection do not establish design defects.
25
+
26
+ Performance findings are review indications. A style layer, a queried feature, a draw call and an
27
+ overdraw pass measure different things. Layer stacks are grouped by compiler provenance when it is
28
+ available; unknown ownership is not inferred from names. Equivalent drawing rules may be deliberate
29
+ casings, shadows or blending, so they are never automatically removed. Counts and short CPU samples
30
+ do not attribute GPU cost to an individual layer or establish a universal performance grade.
31
+
32
+ Native overdraw is observed only when the renderer and scene permit a meaningful measurement.
33
+ Custom rendering, terrain, sky, globe projection or active debug modes can make it unavailable. The native sample saturates
34
+ at eight passes, so an eight-pass observation is a lower bound. Sampling temporarily enables the
35
+ native debug rendering and restores it; open **Debug** explicitly to keep an overlay visible.
36
+
37
+ Drawing-work sampling requests repaints for approximately 650 ms and requires at least ten complete
38
+ frames. Compatible native overdraw sampling has a separate two-second limit. A median of at least
39
+ 1,000 draw submissions, or at least 25% of sampled pixels reaching eight passes, creates a review
40
+ finding. These are disclosed review triggers, not universal limits. Label evaluation and retained
41
+ examples are capped; reading the style and querying rendered features still depend on map size.
42
+ Detailed rule checks inspect at most 2,000 non-internal layers, while stack counts include the
43
+ whole style. Contrast sampling retains up to 5,000 symbol candidates, evaluates at most 200 of
44
+ them and keeps up to 40 eligible examples. Coverage limits remain available in the copied report.
45
+
46
+ ## Investigate a finding
47
+
48
+ Expand a row to read its evidence and limitations. **Open rule** selects the layer and focuses the
49
+ related paint, layout or filter declaration. **Show on map** uses the finding's observed context;
50
+ asset and overdraw actions open the corresponding inspection tools. Available actions depend on
51
+ the evidence. Preview changes remain temporary and share Undo/Redo and Reset all changes.
52
+
53
+ Findings retain the map revision and camera in which they were observed. Moving away is not proof
54
+ that a problem was fixed. Old view evidence is marked stale until checked again. Local dismissals
55
+ apply to a case fingerprint; changing the relevant evidence makes it reviewable again. Incomplete
56
+ checks and missing renderer capabilities are included in the copied report.
57
+
58
+ **Copy findings** and each finding's copy action include structured evidence, conditions and
59
+ temporary edits. Context is sanitized, bounded and reports omissions. Nothing is sent to an agent
60
+ automatically. The output is diagnostic evidence, not a benchmark or release certification.
61
+
62
+ ## Explain a layer's visibility
63
+
64
+ Select a layer in **Layers**, then open **Diagnosis**. It shows declared visibility,
65
+ zoom eligibility, source state and viewport-query observations. With an actual selected feature it
66
+ also evaluates its filter and relevant zero-size or zero-opacity rules. A zero component does not
67
+ prove the whole layer is invisible: a circle stroke or another symbol component may still draw.
68
+ Unknown expression context and geometry-dependent filters remain unavailable. Query matches do
69
+ not establish final pixel visibility, depth or occlusion.
70
+
71
+ ## Compatibility
72
+
73
+ The tab persists as `tab=diagnostics`. Previous `tab=beacon` links open the new panel. The former
74
+ Beacon scoring and tour interface is retired; its historical report scores are not comparable to
75
+ Diagnostics findings. Network infrastructure, platform regressions and broad map quality remain
76
+ separate concerns from these author-facing checks.
@@ -202,7 +202,7 @@ pnpm exec tsx scripts/native-lowering-audit.ts > native-lowering-audit.json
202
202
  ```
203
203
 
204
204
  This diagnostic report compiles the unchanged full Streets family through the web preparation
205
- path, then measures the native representation for **both** dark and light before applying output
205
+ path, then measures the native representation for **all twelve** themes before applying output
206
206
  budget assertions. It writes no native artifact generation and is not a compatibility verdict.
207
207
  Each theme includes layer counts, visited JSON nodes, UTF-8 bytes and maximum depth before/after;
208
208
  every transformed layer is listed with its ID, physical span, branch count and zoom intervals,
@@ -214,7 +214,7 @@ cap/dash decisions. They identify whether predicate expansion or repetition of o
214
214
  dominates; they are not an additive byte decomposition. The counters include array keys, matching
215
215
  the profile's existing node convention. Retain the report with the checkout SHA. An over-budget
216
216
  result must not be treated as a successful native build or fixed by dropping layers or weakening
217
- validation. The test suite prints the two-theme size summary even when a budget assertion fails.
217
+ validation. The test suite prints the full theme size summary even when a budget assertion fails.
218
218
 
219
219
  ## Prepare Streets for a directed native check
220
220
 
@@ -251,7 +251,7 @@ actually contains features for the transformed layers listed in
251
251
  `/tmp/native-lowering-audit.json`. Use the audit's exact physical IDs, source layers, branch counts
252
252
  and zoom intervals rather than assuming that a map location exercises every branch.
253
253
 
254
- Check both themes at the same center, viewport, pitch 0 and bearing 0. Exercise at least one cap
254
+ Check all themes at the same center, viewport, pitch 0 and bearing 0. Exercise at least one cap
255
255
  partition, one dashed/solid partition and an overlap between simultaneously active branches. Pan
256
256
  slightly and inspect just below/at/above the relevant audit zoom cuts (for example 15.99/16/16.01
257
257
  when a selected interval changes at 16). Check that caps and dash patterns are visible, layers do