@florasync/leaflet-geokit 0.2.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/CHANGELOG.md +5 -0
- package/LICENSE +21 -0
- package/README.md +400 -0
- package/dist/leaflet-geokit.es.js +9233 -0
- package/dist/leaflet-geokit.es.js.map +1 -0
- package/dist/leaflet-geokit.umd.js +202 -0
- package/dist/leaflet-geokit.umd.js.map +1 -0
- package/package.json +106 -0
package/CHANGELOG.md
ADDED
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025-2026 FloraSync
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,400 @@
|
|
|
1
|
+
# @florasync/leaflet-geokit
|
|
2
|
+
|
|
3
|
+
A framework-agnostic, TypeScript-first Web Component that wraps Leaflet and Leaflet.draw to provide a beautiful, stateful, embeddable GeoJSON editor. Ships as a Vite library (ESM + UMD), bundles Leaflet/Draw assets, injects CSS into Shadow DOM, exposes a clean attributes/events/methods API, and includes verbose, structured logging for first-class diagnostics.
|
|
4
|
+
|
|
5
|
+
Primary sources
|
|
6
|
+
|
|
7
|
+
- Element host (custom element): [src/components/LeafletDrawMapElement.ts](src/components/LeafletDrawMapElement.ts)
|
|
8
|
+
- Controller (Leaflet + Draw lifecycle): [src/lib/MapController.ts](src/lib/MapController.ts)
|
|
9
|
+
- Feature store (ID-centric CRUD): [src/lib/FeatureStore.ts](src/lib/FeatureStore.ts)
|
|
10
|
+
- Assets and CSS bridge: [src/lib/leaflet-assets.ts](src/lib/leaflet-assets.ts)
|
|
11
|
+
- Public API and event types: [src/types/public.ts](src/types/public.ts), [src/types/events.ts](src/types/events.ts)
|
|
12
|
+
- Logger utility: [src/utils/logger.ts](src/utils/logger.ts)
|
|
13
|
+
- GeoJSON helpers: [src/utils/geojson.ts](src/utils/geojson.ts)
|
|
14
|
+
|
|
15
|
+
Documentation quick-links
|
|
16
|
+
|
|
17
|
+
- Architecture overview: [ARCHITECTURE.md](ARCHITECTURE.md)
|
|
18
|
+
- Dev harness (example): [index.html](index.html)
|
|
19
|
+
- Unit tests: [tests/element.spec.ts](tests/element.spec.ts)
|
|
20
|
+
- Vitest config: [vitest.config.ts](vitest.config.ts)
|
|
21
|
+
- Vite build config: [vite.config.ts](vite.config.ts)
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
## Contents
|
|
26
|
+
|
|
27
|
+
- What you get
|
|
28
|
+
- Install
|
|
29
|
+
- Build and compilation
|
|
30
|
+
- Runtime and architecture overview
|
|
31
|
+
- Public API (attributes, properties, methods, events)
|
|
32
|
+
- Usage examples (HTML, ESM, framework integration, recipes)
|
|
33
|
+
- Logging, diagnostics, and troubleshooting
|
|
34
|
+
- Performance, accessibility, and SSR notes
|
|
35
|
+
- Roadmap and versioning
|
|
36
|
+
|
|
37
|
+
---
|
|
38
|
+
|
|
39
|
+
## What you get
|
|
40
|
+
|
|
41
|
+
- A self-contained custom element, <leaflet-geokit>, that:
|
|
42
|
+
- Renders a Leaflet map inside Shadow DOM
|
|
43
|
+
- Configures Leaflet.draw tools via boolean attributes
|
|
44
|
+
- Injects Leaflet and Leaflet.draw CSS automatically
|
|
45
|
+
- Emits typed CustomEvents with id-aware payloads
|
|
46
|
+
- Offers an imperative API to import/export GeoJSON and control the view
|
|
47
|
+
- Exposes structured verbose logging with adjustable levels
|
|
48
|
+
|
|
49
|
+
- A design that separates:
|
|
50
|
+
- Web component host logic from Leaflet orchestration (controller)
|
|
51
|
+
- Controller logic from id-centric data storage (feature store)
|
|
52
|
+
|
|
53
|
+
- Strong TypeScript types for the public API (for consumers using TS)
|
|
54
|
+
|
|
55
|
+
---
|
|
56
|
+
|
|
57
|
+
## Install
|
|
58
|
+
|
|
59
|
+
Inside this package directory:
|
|
60
|
+
|
|
61
|
+
- npm install
|
|
62
|
+
- npm run dev — starts Vite dev server
|
|
63
|
+
- Open http://localhost:5173 in the browser (or whatever port VITE is using
|
|
64
|
+
|
|
65
|
+
For consumption from another app:
|
|
66
|
+
|
|
67
|
+
- Add this package as a dependency
|
|
68
|
+
- `npm install @florasync/leaflet-geokit`
|
|
69
|
+
- `import "@florasync/leaflet-geokit";` and ensure the element is sized via CSS (it does not self-size)
|
|
70
|
+
|
|
71
|
+
---
|
|
72
|
+
|
|
73
|
+
## Build and compilation
|
|
74
|
+
|
|
75
|
+
Tooling
|
|
76
|
+
|
|
77
|
+
- Build: Vite (library mode), target: ES2019
|
|
78
|
+
- Outputs:
|
|
79
|
+
- dist/leaflet-geokit.es.js (ESM)
|
|
80
|
+
- dist/leaflet-geokit.umd.js (UMD)
|
|
81
|
+
- dist/types/\*\* (TypeScript declaration files)
|
|
82
|
+
- Bundles Leaflet and Leaflet.draw by default
|
|
83
|
+
- CSS/Assets handled by Vite and injected into Shadow DOM at runtime
|
|
84
|
+
|
|
85
|
+
Scripts (see [package.json](package.json))
|
|
86
|
+
|
|
87
|
+
- npm run dev — Vite dev server
|
|
88
|
+
- npm run build — type declarations + Vite build
|
|
89
|
+
- npm run test:unit — Vitest (happy-dom)
|
|
90
|
+
- npm run typecheck — TypeScript noEmit
|
|
91
|
+
- npm run lint — ESLint (strict TS rules)
|
|
92
|
+
- npm run format — Prettier write
|
|
93
|
+
- npm run test:e2e — Playwright (currently a minimal smoke test under e2e/)
|
|
94
|
+
|
|
95
|
+
Browser support
|
|
96
|
+
|
|
97
|
+
- ES2019, evergreen browsers (Chromium, Firefox, Safari modern)
|
|
98
|
+
|
|
99
|
+
---
|
|
100
|
+
|
|
101
|
+
## Runtime and architecture overview
|
|
102
|
+
|
|
103
|
+
High-level components:
|
|
104
|
+
|
|
105
|
+
- [src/components/LeafletDrawMapElement.ts](src/components/LeafletDrawMapElement.ts)
|
|
106
|
+
- Manages Shadow DOM container and attributes/properties
|
|
107
|
+
- Instantiates the controller; delegates public methods
|
|
108
|
+
- Dispatches high-level CustomEvents
|
|
109
|
+
|
|
110
|
+
- [src/lib/MapController.ts](src/lib/MapController.ts)
|
|
111
|
+
- Creates Leaflet map and tile layer
|
|
112
|
+
- Manages L.FeatureGroup for drawn items
|
|
113
|
+
- Configures Leaflet.draw tools from attributes
|
|
114
|
+
- Bridges Leaflet.draw events to component events with typed payloads
|
|
115
|
+
- Provides procedural methods: setView, fitBoundsToData, etc.
|
|
116
|
+
- Persists features in the FeatureStore
|
|
117
|
+
|
|
118
|
+
- [src/lib/FeatureStore.ts](src/lib/FeatureStore.ts)
|
|
119
|
+
- Guarantees stable feature ids (feature.id → properties.id → generated uuid)
|
|
120
|
+
- CRUD operations and conversion to FeatureCollection
|
|
121
|
+
- Computes bounds for fit-to-data
|
|
122
|
+
|
|
123
|
+
CSS and assets:
|
|
124
|
+
|
|
125
|
+
- [src/lib/leaflet-assets.ts](src/lib/leaflet-assets.ts) injects Leaflet + Leaflet.draw CSS into ShadowRoot and wires default marker icons via bundled asset URLs.
|
|
126
|
+
|
|
127
|
+
---
|
|
128
|
+
|
|
129
|
+
## Public API
|
|
130
|
+
|
|
131
|
+
Attributes (string/boolean)
|
|
132
|
+
|
|
133
|
+
- Map configuration
|
|
134
|
+
- latitude (string number): initial map latitude; default 0
|
|
135
|
+
- longitude (string number): initial map longitude; default 0
|
|
136
|
+
- zoom (string number): initial zoom; default 2
|
|
137
|
+
- min-zoom (string number, optional)
|
|
138
|
+
- max-zoom (string number, optional)
|
|
139
|
+
- tile-url (string): tile URL template (default OSM)
|
|
140
|
+
- tile-attribution (string, optional): attribution text
|
|
141
|
+
- prefer-canvas (boolean): use Canvas rendering instead of SVG for better performance with large datasets; default true
|
|
142
|
+
- Controls (boolean; presence = enabled)
|
|
143
|
+
- draw-polygon, draw-polyline, draw-rectangle, draw-circle, draw-layer-cake, draw-marker
|
|
144
|
+
- edit-features, delete-features
|
|
145
|
+
- Behavior
|
|
146
|
+
- read-only (boolean): disables all drawing/editing/removing
|
|
147
|
+
- log-level (string): trace | debug | info | warn | error | silent (default debug)
|
|
148
|
+
- dev-overlay (boolean): reserved for a runtime overlay (future)
|
|
149
|
+
|
|
150
|
+
Properties (runtime)
|
|
151
|
+
|
|
152
|
+
- latitude: number
|
|
153
|
+
- longitude: number
|
|
154
|
+
- zoom: number
|
|
155
|
+
- minZoom?: number
|
|
156
|
+
- maxZoom?: number
|
|
157
|
+
- tileUrl: string
|
|
158
|
+
- tileAttribution?: string
|
|
159
|
+
- readOnly: boolean
|
|
160
|
+
- logLevel: 'trace' | 'debug' | 'info' | 'warn' | 'error' | 'silent'
|
|
161
|
+
- devOverlay: boolean
|
|
162
|
+
- preferCanvas: boolean
|
|
163
|
+
|
|
164
|
+
Methods (Promise-based, invoked on the element instance)
|
|
165
|
+
|
|
166
|
+
- getGeoJSON(): returns FeatureCollection of current data
|
|
167
|
+
- loadGeoJSON(fc): clears + loads FeatureCollection; does not auto-fit
|
|
168
|
+
- clearLayers(): clears map layers and store
|
|
169
|
+
- addFeatures(fc): adds features; returns array of assigned ids
|
|
170
|
+
- updateFeature(id, feature): replaces a feature in the store (visual sync is progressively enhanced)
|
|
171
|
+
- removeFeature(id): removes a feature (and layers with matching id)
|
|
172
|
+
- fitBoundsToData(padding?): fits view to data bounds (default padding ~0.05)
|
|
173
|
+
- fitBounds(bounds, padding?): fits view to provided [[south, west], [north, east]] bounds with optional padding ratio
|
|
174
|
+
- setView(lat, lng, zoom?): sets map view
|
|
175
|
+
- loadGeoJSONFromUrl(url): fetches a URL (application/json) and loads; auto-fits
|
|
176
|
+
- loadGeoJSONFromText(text): parses JSON text and loads; auto-fits
|
|
177
|
+
- exportGeoJSON(): emits 'leaflet-draw:export' with current FeatureCollection and returns it
|
|
178
|
+
|
|
179
|
+
Events (CustomEvent with detail)
|
|
180
|
+
|
|
181
|
+
- leaflet-draw:ready — { bounds?: [[south, west], [north, east]] }
|
|
182
|
+
- leaflet-draw:created — { id: string, layerType: 'polygon'|'polyline'|'rectangle'|'circle'|'marker', geoJSON: Feature }
|
|
183
|
+
- leaflet-draw:edited — { ids: string[], geoJSON: FeatureCollection }
|
|
184
|
+
- leaflet-draw:deleted — { ids: string[], geoJSON: FeatureCollection }
|
|
185
|
+
- leaflet-draw:error — { message: string, cause?: unknown }
|
|
186
|
+
- leaflet-draw:export — { geoJSON: FeatureCollection, featureCount: number }
|
|
187
|
+
- leaflet-draw:ingest — { fc: FeatureCollection, mode: 'load'|'add' } (listener may mutate detail.fc to transform input)
|
|
188
|
+
|
|
189
|
+
Event payload types live in [src/types/events.ts](src/types/events.ts). Public API types live in [src/types/public.ts](src/types/public.ts).
|
|
190
|
+
|
|
191
|
+
---
|
|
192
|
+
|
|
193
|
+
## Usage examples
|
|
194
|
+
|
|
195
|
+
A. Basic HTML, served by Vite (development)
|
|
196
|
+
|
|
197
|
+
```html
|
|
198
|
+
<style>
|
|
199
|
+
leaflet-geokit {
|
|
200
|
+
display: block;
|
|
201
|
+
width: 100%;
|
|
202
|
+
height: 500px;
|
|
203
|
+
}
|
|
204
|
+
</style>
|
|
205
|
+
<script type="module" src="/src/index.ts"></script>
|
|
206
|
+
|
|
207
|
+
<leaflet-geokit
|
|
208
|
+
latitude="39.7392"
|
|
209
|
+
longitude="-104.9903"
|
|
210
|
+
zoom="11"
|
|
211
|
+
tile-url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
|
|
212
|
+
draw-polygon
|
|
213
|
+
draw-polyline
|
|
214
|
+
draw-rectangle
|
|
215
|
+
draw-circle
|
|
216
|
+
draw-layer-cake
|
|
217
|
+
draw-marker
|
|
218
|
+
edit-features
|
|
219
|
+
delete-features
|
|
220
|
+
log-level="debug"
|
|
221
|
+
></leaflet-geokit>
|
|
222
|
+
|
|
223
|
+
<script type="module">
|
|
224
|
+
const el = document.querySelector("leaflet-geokit");
|
|
225
|
+
|
|
226
|
+
el.addEventListener("leaflet-draw:ready", (e) =>
|
|
227
|
+
console.log("READY", e.detail),
|
|
228
|
+
);
|
|
229
|
+
el.addEventListener("leaflet-draw:created", (e) =>
|
|
230
|
+
console.log("CREATED", e.detail),
|
|
231
|
+
);
|
|
232
|
+
el.addEventListener("leaflet-draw:edited", (e) =>
|
|
233
|
+
console.log("EDITED", e.detail),
|
|
234
|
+
);
|
|
235
|
+
el.addEventListener("leaflet-draw:deleted", (e) =>
|
|
236
|
+
console.log("DELETED", e.detail),
|
|
237
|
+
);
|
|
238
|
+
</script>
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
B. ESM consumption from another app (node_modules)
|
|
242
|
+
|
|
243
|
+
```js
|
|
244
|
+
// main.ts
|
|
245
|
+
import "@florasync/leaflet-geokit";
|
|
246
|
+
|
|
247
|
+
// later in DOM:
|
|
248
|
+
const el = document.querySelector("leaflet-geokit");
|
|
249
|
+
await el.loadGeoJSONFromUrl("/data/feature-collection.json");
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
C. Load from text (e.g., user paste or file input)
|
|
253
|
+
|
|
254
|
+
```js
|
|
255
|
+
const text = await file.text();
|
|
256
|
+
await el.loadGeoJSONFromText(text);
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
D. Programmatic CRUD with ids
|
|
260
|
+
|
|
261
|
+
```js
|
|
262
|
+
const ids = await el.addFeatures({
|
|
263
|
+
type: "FeatureCollection",
|
|
264
|
+
features: [
|
|
265
|
+
{
|
|
266
|
+
type: "Feature",
|
|
267
|
+
properties: { name: "A" },
|
|
268
|
+
geometry: { type: "Point", coordinates: [-105, 39.73] },
|
|
269
|
+
},
|
|
270
|
+
],
|
|
271
|
+
});
|
|
272
|
+
await el.updateFeature(ids[0], {
|
|
273
|
+
type: "Feature",
|
|
274
|
+
properties: { name: "A (edited)" },
|
|
275
|
+
geometry: { type: "Point", coordinates: [-105.01, 39.735] },
|
|
276
|
+
});
|
|
277
|
+
await el.removeFeature(ids[0]);
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
E. Framework integration (React/Preact/Vue/Svelte)
|
|
281
|
+
|
|
282
|
+
- Use the custom element as a regular JSX/HTML element.
|
|
283
|
+
- Ensure typescript recognizes custom elements (tsconfig compilerOptions.lib includes DOM).
|
|
284
|
+
- Manage data in your state and pass event handlers via addEventListener in useEffect/onMount.
|
|
285
|
+
- Remember to set CSS height on the element.
|
|
286
|
+
|
|
287
|
+
---
|
|
288
|
+
|
|
289
|
+
## Recipes
|
|
290
|
+
|
|
291
|
+
1. Persist to your API
|
|
292
|
+
|
|
293
|
+
```js
|
|
294
|
+
el.addEventListener("leaflet-draw:edited", async (e) => {
|
|
295
|
+
await fetch("/api/shapes", {
|
|
296
|
+
method: "PUT",
|
|
297
|
+
headers: { "content-type": "application/json" },
|
|
298
|
+
body: JSON.stringify(e.detail.geoJSON),
|
|
299
|
+
});
|
|
300
|
+
});
|
|
301
|
+
```
|
|
302
|
+
|
|
303
|
+
2. Read-only review mode
|
|
304
|
+
|
|
305
|
+
```html
|
|
306
|
+
<leaflet-geokit read-only></leaflet-geokit>
|
|
307
|
+
```
|
|
308
|
+
|
|
309
|
+
3. Alternate tile provider with attribution
|
|
310
|
+
|
|
311
|
+
```html
|
|
312
|
+
<leaflet-geokit
|
|
313
|
+
tile-url="https://{s}.tile.openstreetmap.fr/hot/{z}/{x}/{y}.png"
|
|
314
|
+
tile-attribution="© OSM contributors, Humanitarian style"
|
|
315
|
+
/>
|
|
316
|
+
```
|
|
317
|
+
|
|
318
|
+
4. Fit view after load
|
|
319
|
+
|
|
320
|
+
- loadGeoJSONFromUrl and loadGeoJSONFromText auto-fit to the loaded data.
|
|
321
|
+
- loadGeoJSON does not auto-fit; call el.fitBoundsToData(0.1) after load if desired.
|
|
322
|
+
|
|
323
|
+
5. Custom logger injection (advanced)
|
|
324
|
+
|
|
325
|
+
- At runtime, set el.logLevel = 'info' to reduce chatter.
|
|
326
|
+
- If you need a different sink, wrap the element logic in your app and forward to your own logger (see [src/utils/logger.ts](src/utils/logger.ts)).
|
|
327
|
+
|
|
328
|
+
---
|
|
329
|
+
|
|
330
|
+
## Logging and diagnostics
|
|
331
|
+
|
|
332
|
+
Logger namespaces:
|
|
333
|
+
|
|
334
|
+
- component:leaflet-geokit — element lifecycle, attribute changes, public methods
|
|
335
|
+
- component:leaflet-geokit:controller — controller init, draw events, CRUD, timings
|
|
336
|
+
- component:leaflet-geokit:controller:store — feature add/update/remove, bounds, ids
|
|
337
|
+
|
|
338
|
+
Control verbosity:
|
|
339
|
+
|
|
340
|
+
- Attribute log-level="debug" (default 'debug')
|
|
341
|
+
- el.logLevel at runtime
|
|
342
|
+
|
|
343
|
+
Troubleshooting checklist:
|
|
344
|
+
|
|
345
|
+
- Blank/empty map
|
|
346
|
+
- Ensure the element has a fixed height (CSS); 0-height containers won’t render.
|
|
347
|
+
- For hidden tabs, call setView or fitBoundsToData once the tab becomes visible.
|
|
348
|
+
- Draw tools missing
|
|
349
|
+
- Confirm boolean attributes are present (e.g., draw-polygon). read-only disables tools entirely.
|
|
350
|
+
- Events not received
|
|
351
|
+
- Add listeners directly on the element instance (e.target is the custom element). Verify event names.
|
|
352
|
+
- IDs absent or not stable
|
|
353
|
+
- Persist feature.id (or properties.id) in your backend; the store uses and preserves them.
|
|
354
|
+
|
|
355
|
+
---
|
|
356
|
+
|
|
357
|
+
## Performance, accessibility, SSR
|
|
358
|
+
|
|
359
|
+
Performance
|
|
360
|
+
|
|
361
|
+
- **Canvas Rendering (Default)**: The component uses Canvas rendering by default (prefer-canvas="true"), which provides significantly better performance than SVG when displaying large numbers of features or complex polygons. Canvas uses a single canvas element instead of individual DOM elements for each feature, reducing DOM overhead and improving rendering speed.
|
|
362
|
+
- To switch to SVG rendering (e.g., for better print quality or specific styling needs), remove the prefer-canvas attribute or set it explicitly: `<leaflet-geokit prefer-canvas="false">`.
|
|
363
|
+
- L.geoJSON is adequate for small/medium collections. For very large datasets (thousands of features), consider server-side tiling or clustering (not included).
|
|
364
|
+
- fitBoundsToData uses padding to reduce cramped framing; tune via method arg.
|
|
365
|
+
|
|
366
|
+
Accessibility
|
|
367
|
+
|
|
368
|
+
- Keyboard navigation and zoom are Leaflet-provided. Consider documenting shortcuts in your app.
|
|
369
|
+
- Host element applies modern focus/shape defaults; restyle as desired.
|
|
370
|
+
|
|
371
|
+
SSR
|
|
372
|
+
|
|
373
|
+
- This is a browser-only custom element. If importing in SSR, gate the import to client-side only.
|
|
374
|
+
|
|
375
|
+
---
|
|
376
|
+
|
|
377
|
+
## Roadmap and versioning
|
|
378
|
+
|
|
379
|
+
Planned enhancements
|
|
380
|
+
|
|
381
|
+
- Dev overlay (opt-in) to visualize state, counts, and last event payloads
|
|
382
|
+
- Geometry-level layer sync for updateFeature without re-add
|
|
383
|
+
- Playwright e2e and CI workflows
|
|
384
|
+
- Advanced import providers (files, streams) and output format adapters
|
|
385
|
+
- Theming hooks for overlay UI
|
|
386
|
+
|
|
387
|
+
Versioning and releases
|
|
388
|
+
|
|
389
|
+
- Types shipped in dist/types
|
|
390
|
+
- Keep a Changelog in CHANGELOG.md (to be populated during releases)
|
|
391
|
+
|
|
392
|
+
---
|
|
393
|
+
|
|
394
|
+
## License
|
|
395
|
+
|
|
396
|
+
## MIT
|
|
397
|
+
|
|
398
|
+
## Contributing
|
|
399
|
+
|
|
400
|
+
Please see CONTRIBUTING.md for environment setup, testing, and our Good Vibes Policy. PRs with clear rationale, small focused changes, and tests are very welcome.
|