ark-meta-tags 0.0.1

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/README.md ADDED
@@ -0,0 +1,579 @@
1
+ # 🏷 ArkMetaTags
2
+
3
+ > A self-contained, zero-dependency multi-instance metadata tag-capture widget.
4
+ > Built for **News Trace** β€” ARK: Immanuel project.
5
+
6
+ [![Version](https://img.shields.io/badge/version-1.0.0-1a1a1a.svg)](https://github.com/your-org/ark-meta-tags)
7
+ [![License](https://img.shields.io/badge/license-MIT-green.svg)](LICENSE)
8
+ [![Zero Dependencies](https://img.shields.io/badge/dependencies-none-brightgreen.svg)](#)
9
+ [![UMD](https://img.shields.io/badge/build-UMD%20%7C%20CJS%20%7C%20ESM-blue.svg)](#installation)
10
+
11
+ ---
12
+
13
+ ## What it does
14
+
15
+ ArkMetaTags renders a tag-chip strip. Tapping any tag adds an entry below; tapping again adds another instance of the **same tag**. This makes it ideal for recording sequential events against a single record β€” multiple court hearings, multiple follow-up dates, multiple witnesses β€” each with its own note.
16
+
17
+ ## Demo Link
18
+
19
+ You may be using [Demo Link](https://ark-meta-tags.immanuel.co/demo.html).
20
+
21
+ **Key features**
22
+
23
+ | Feature | Detail |
24
+ |---|---|
25
+ | 16 built-in tags | Contact, Phone, FIR, IPC Section, Officer, Victim, Witness, Accused, Case Status, Court/Hearing, Hospital, Source, Reference, Follow-up Date, Note, Custom Tag |
26
+ | 6 field types | `text`, `textarea`, `select`, `chiplist`, `keyvalue`, `dateNote` |
27
+ | `hasNote` flag | Adds an *action / note* sub-row to any simple type |
28
+ | `hasDate` flag | Adds a date picker sub-row to any simple type |
29
+ | `fields[]` array | Full multi-field control per instance |
30
+ | Custom `tagDefs` | Extend or fully replace the 16 built-in tags |
31
+ | `collectData()` | Returns structured JSON β€” one array per tag key |
32
+ | `loadData(meta)` | Restores widget from a previous `collectData()` output |
33
+ | DOM events | `ark-meta:add`, `ark-meta:remove`, `ark-meta:change` |
34
+ | Self-contained CSS | Injects `<style>` into `<head>` once, no external sheet needed |
35
+ | UMD build | Works as `<script>`, `require()`, or `import` |
36
+
37
+ ---
38
+
39
+ ## File structure
40
+
41
+ ```
42
+ ark-meta-tags.js ← Main library
43
+ ark-test-data.js ← Companion test-data generator (optional)
44
+ ark-meta-tags-docs.html← Interactive documentation page
45
+ cases-list.html ← Case review list (approve / reject / invalid)
46
+ news-trace-demo.html ← News Trace entry form using the library
47
+ ```
48
+
49
+ ---
50
+
51
+ ## Installation
52
+
53
+ ### Script tag
54
+ ```html
55
+ <script src="ark-meta-tags.js"></script>
56
+ ```
57
+
58
+ ### ES Module
59
+ ```js
60
+ import ArkMetaTags from './ark-meta-tags.js';
61
+ ```
62
+
63
+ ### CommonJS
64
+ ```js
65
+ const ArkMetaTags = require('./ark-meta-tags');
66
+ ```
67
+
68
+ No npm, no bundler required.
69
+
70
+ ---
71
+
72
+ ## Quick Start
73
+
74
+ ```html
75
+ <!-- Host element -->
76
+ <div id="meta-host"></div>
77
+
78
+ <script src="ark-meta-tags.js"></script>
79
+ <script>
80
+ const widget = new ArkMetaTags('#meta-host', {
81
+ onChange(data) {
82
+ console.log(data); // fires on every add / remove
83
+ },
84
+ });
85
+
86
+ // Collect structured JSON
87
+ const meta = widget.collectData();
88
+
89
+ // Restore from saved data
90
+ widget.loadData(savedMeta);
91
+ </script>
92
+ ```
93
+
94
+ > **No CSS needed on the host element.** The library writes its own `<style>` once. Override with `--amt-font` CSS variable.
95
+
96
+ ---
97
+
98
+ ## Tag Types
99
+
100
+ Every tag definition requires a `type` that controls the rendered input and the shape of each `collectData()` entry.
101
+
102
+ ### `text`
103
+ Single-line text input.
104
+ ```js
105
+ { key: 'witness', label: 'Witness', icon: `…`,
106
+ type: 'text', ph: 'Witness name and account…' }
107
+ // collectData entry: { value: "…" }
108
+ ```
109
+
110
+ ### `textarea`
111
+ Resizable multiline text area.
112
+ ```js
113
+ { key: 'note', label: 'Internal Note', icon: `…`,
114
+ type: 'textarea', ph: 'Off-record notes…' }
115
+ // collectData entry: { value: "…" }
116
+ ```
117
+
118
+ ### `select`
119
+ Dropdown from a fixed options list. The first option is treated as a blank placeholder.
120
+ ```js
121
+ { key: 'status', label: 'Case Status', icon: `…`,
122
+ type: 'select',
123
+ opts: ['Select status…', 'Under investigation', 'FIR registered', 'In court'] }
124
+ // collectData entry: { value: "Under investigation" }
125
+ ```
126
+
127
+ ### `chiplist`
128
+ Multiple values entered as chips. Press **Enter** or **,** to commit each chip. Backspace removes the last chip.
129
+ ```js
130
+ { key: 'phone', label: 'Phone', icon: `…`,
131
+ type: 'chiplist', ph: 'Type number, press Enter…' }
132
+ // collectData entry: { values: ["+91 9876543210", "+91 9898989898"] }
133
+ ```
134
+
135
+ ### `keyvalue`
136
+ Side-by-side label : value pair. Left field is the key/label, right field is the value.
137
+ ```js
138
+ { key: 'custom', label: 'Custom Tag', icon: `…`,
139
+ type: 'keyvalue', phKey: 'Field name…', phVal: 'Value…' }
140
+ // collectData entry: { key: "RTI Ref", value: "RTI/221/2025" }
141
+ ```
142
+
143
+ ### `dateNote`
144
+ Date picker as the primary input with a mandatory note textarea always rendered below it.
145
+ ```js
146
+ { key: 'followup', label: 'Follow-up Date', icon: `…`,
147
+ type: 'dateNote', ph: 'What to follow up on…' }
148
+ // collectData entry: { date: "2025-06-01", note: "Check FIR status" }
149
+ ```
150
+
151
+ ---
152
+
153
+ ## `hasNote` and `hasDate` Flags
154
+
155
+ Add optional sub-rows to any simple `type` without switching to multi-field mode.
156
+
157
+ ```js
158
+ // hasNote β€” appends "Action / note" textarea below the main input
159
+ { key: 'fir', label: 'FIR No.', icon: `…`,
160
+ type: 'text', ph: 'FIR No. 000/2025…',
161
+ hasNote: true }
162
+ // β†’ { value: "FIR No. 047/2025", note: "IPC 302, 307" }
163
+
164
+ // hasDate β€” appends a date picker below the main input
165
+ { key: 'officer', label: 'Officer / Official', icon: `…`,
166
+ type: 'text', ph: 'Name, rank, department…',
167
+ hasDate: true }
168
+ // β†’ { value: "SP Rajesh Kumar", date: "2025-05-13" }
169
+
170
+ // Both together
171
+ { key: 'accused', label: 'Accused', icon: `…`,
172
+ type: 'text', ph: 'Name(s) of accused…',
173
+ hasDate: true, hasNote: true }
174
+ // β†’ { value: "…", date: "2025-03-10", note: "Arrested; currently in judicial custody" }
175
+ ```
176
+
177
+ ---
178
+
179
+ ## Multi-field (`fields[]`)
180
+
181
+ For tags that need several named sub-fields per instance, replace `type` with a `fields` array.
182
+
183
+ | Field type | Renders as |
184
+ |---|---|
185
+ | `text` | Single-line input (primary row or labelled secondary row) |
186
+ | `date` | Date picker in a date-row style |
187
+ | `note` | Textarea in a note-row style (italic, subdued background) |
188
+ | `chiplist` | Chip input |
189
+ | `select` | Dropdown |
190
+ | `textarea` | Multiline input |
191
+
192
+ ```js
193
+ {
194
+ key: 'court', label: 'Court / Hearing', icon: `…`,
195
+ fields: [
196
+ { name: 'court', type: 'text', ph: 'Court name…' },
197
+ { name: 'date', type: 'date', label: 'Hearing date' },
198
+ { name: 'judge', type: 'text', ph: 'Presiding judge…',
199
+ label: 'Judge' },
200
+ { name: 'outcome', type: 'note', ph: 'What happened…' },
201
+ { name: 'links', type: 'chiplist', ph: 'Paste URL, Enter…' },
202
+ { name: 'verdict', type: 'select',
203
+ opts: ['Select…', 'Pending', 'Convicted', 'Acquitted'] },
204
+ ],
205
+ }
206
+
207
+ // collectData() entry per instance:
208
+ // {
209
+ // court: "Madras High Court",
210
+ // date: "2025-05-13",
211
+ // judge: "Hon. Justice S. Ramakrishnan",
212
+ // outcome: "Witness did not appear; hearing adjourned",
213
+ // links: ["https://ecourts.gov.in/…"],
214
+ // verdict: "Pending"
215
+ // }
216
+ ```
217
+
218
+ **Multiple instances example** β€” three separate court hearings on one case:
219
+ ```js
220
+ widget.collectData();
221
+ // {
222
+ // court: [
223
+ // { court: "Chennai HC", date: "2025-03-10", outcome: "Bail rejected" },
224
+ // { court: "Chennai HC", date: "2025-05-13", outcome: "Witness absent" },
225
+ // { court: "Chennai HC", date: "2025-07-22", outcome: "Judge reserved" },
226
+ // ]
227
+ // }
228
+ ```
229
+
230
+ ---
231
+
232
+ ## Custom `tagDefs`
233
+
234
+ ### Merge mode (default β€” `replaceTagDefs: false`)
235
+
236
+ User-supplied entries override built-ins by matching `key`. Unknown keys are appended to the end of the strip.
237
+
238
+ ```js
239
+ const widget = new ArkMetaTags('#host', {
240
+ tagDefs: [
241
+ // New tag β€” appended to the strip
242
+ {
243
+ key: 'journalist', label: 'Journalist',
244
+ icon: `<path d="M12 20h9"/><path d="M16.5 3.5a2.12 2.12 0 013 3L7 19l-4 1 1-4z"/>`,
245
+ type: 'text', ph: 'Reporter name & outlet…',
246
+ },
247
+ // Override built-in 'court' with multi-field version
248
+ {
249
+ key: 'court',
250
+ fields: [
251
+ { name: 'court', type: 'text', ph: 'Court name…' },
252
+ { name: 'date', type: 'date', label: 'Hearing date' },
253
+ { name: 'note', type: 'note', ph: 'What happened…' },
254
+ ],
255
+ },
256
+ ],
257
+ });
258
+ ```
259
+
260
+ ### Replace mode (`replaceTagDefs: true`)
261
+
262
+ Skips all 16 built-ins entirely β€” use only your definitions.
263
+
264
+ ```js
265
+ const widget = new ArkMetaTags('#host', {
266
+ replaceTagDefs: true,
267
+ tagDefs: [
268
+ { key: 'sku', label: 'SKU', icon: `…`, type: 'text' },
269
+ { key: 'colour', label: 'Colour', icon: `…`, type: 'chiplist' },
270
+ { key: 'expiry', label: 'Expiry', icon: `…`, type: 'dateNote' },
271
+ ],
272
+ });
273
+ ```
274
+
275
+ ---
276
+
277
+ ## Options Reference
278
+
279
+ ```js
280
+ new ArkMetaTags(container, {
281
+ tagDefs : [], // custom tag definitions (see above)
282
+ replaceTagDefs: false, // true = use tagDefs only, ignore built-ins
283
+ onChange : meta => {}, // called on add/remove with full collectData() object
284
+ sectionTitle : 'Details β€” tap to add', // text in the ornamental divider
285
+ hint : 'Tap a tag to add…', // italic hint below divider
286
+ showRule : true, // show/hide the ornamental divider
287
+ injectStyles : true, // auto-inject library CSS into <head>
288
+ });
289
+ ```
290
+
291
+ | Option | Type | Default | Description |
292
+ |---|---|---|---|
293
+ | `tagDefs` | `Array` | `null` | Custom tag definitions |
294
+ | `replaceTagDefs` | `boolean` | `false` | Skip built-in tags entirely |
295
+ | `onChange` | `function` | `null` | Structural-change callback |
296
+ | `sectionTitle` | `string` | `'Details β€” tap to add'` | Section-rule label |
297
+ | `hint` | `string` | `'Tap a tag to add…'` | Italic hint text |
298
+ | `showRule` | `boolean` | `true` | Show ornamental divider |
299
+ | `injectStyles` | `boolean` | `true` | Auto-inject CSS |
300
+
301
+ ---
302
+
303
+ ## Methods
304
+
305
+ ```js
306
+ const w = new ArkMetaTags('#host', { /* options */ });
307
+
308
+ // Read all entries as structured JSON
309
+ const data = w.collectData();
310
+ // β†’ { court: [{value,note}], phone: [{values:[…]}], followup: [{date,note}], … }
311
+
312
+ // Restore from a previous collectData() result (clears first)
313
+ w.loadData(data);
314
+
315
+ // Programmatically add one instance of a tag
316
+ w.addTag('court'); // same as clicking the "Court / Hearing" chip
317
+
318
+ // Clear all entries and groups
319
+ w.reset();
320
+
321
+ // Unmount β€” empties the container element
322
+ w.destroy();
323
+
324
+ // Static properties
325
+ ArkMetaTags.version; // '1.0.0'
326
+ ArkMetaTags.defaultTagDefs; // array of 16 built-in definitions
327
+ ```
328
+
329
+ ---
330
+
331
+ ## Events
332
+
333
+ All events are dispatched on the **host container element** with `bubbles: true`.
334
+
335
+ | Event | `e.detail` | Fired when |
336
+ |---|---|---|
337
+ | `ark-meta:add` | `{ key, iid, instance }` | An instance is added |
338
+ | `ark-meta:remove` | `{ key, iid, instance }` | An instance is removed |
339
+ | `ark-meta:change` | `{ data, instance }` | Any structural change (add/remove/reset) |
340
+
341
+ `e.detail.instance` is the live `ArkMetaTags` object β€” call any method from inside a listener.
342
+
343
+ ```js
344
+ const host = document.getElementById('meta-host');
345
+ const w = new ArkMetaTags(host, {
346
+ onChange(data) {
347
+ // Fires on every add / remove / reset
348
+ saveToServer(data);
349
+ },
350
+ });
351
+
352
+ // Or listen via DOM:
353
+ host.addEventListener('ark-meta:add', e => {
354
+ console.log('Added tag:', e.detail.key, 'instance id:', e.detail.iid);
355
+ });
356
+
357
+ host.addEventListener('ark-meta:remove', e => {
358
+ console.log('Removed:', e.detail.key);
359
+ });
360
+
361
+ host.addEventListener('ark-meta:change', e => {
362
+ // e.detail.data = full collectData() object
363
+ console.log(e.detail.data);
364
+ });
365
+ ```
366
+
367
+ ---
368
+
369
+ ## `collectData()` Format Reference
370
+
371
+ ```js
372
+ {
373
+ // text / select / textarea β†’ { value, [note], [date] }
374
+ witness : [{ value: "Arjun Kumar β€” Eyewitness" }],
375
+ fir : [{ value: "FIR No. 047/2025", note: "IPC 302, 307" }],
376
+ officer : [{ value: "SP Rajesh Kumar", date: "2025-05-13", note: "Present at scene" }],
377
+ status : [{ value: "In court / Trial", note: "Fast-track court hearing scheduled" }],
378
+
379
+ // chiplist β†’ { values: string[] }
380
+ phone : [{ values: ["+91 9876543210", "+91 9898989898"] }],
381
+ ref : [{ values: ["https://thehindu.com/…", "https://ecourts.gov.in/…"] }],
382
+
383
+ // keyvalue β†’ { key, value }
384
+ custom : [{ key: "RTI Ref No.", value: "RTI/221/2025" },
385
+ { key: "Editor Note", value: "Hold for legal clearance" }],
386
+
387
+ // dateNote β†’ { date, note }
388
+ followup : [{ date: "2025-06-01", note: "Chase FIR status from PS" },
389
+ { date: "2025-07-22", note: "Attend court hearing" }],
390
+
391
+ // fields[] β†’ flat object keyed by field name
392
+ court : [
393
+ { court: "Madras HC", date: "2025-03-10", judge: "Hon. Justice S.R.",
394
+ outcome: "Bail rejected", links: ["https://…"], verdict: "Pending" },
395
+ { court: "Madras HC", date: "2025-05-13", judge: "Hon. Justice S.R.",
396
+ outcome: "Case reserved for judgment", links: [], verdict: "Reserved" },
397
+ ],
398
+ }
399
+ ```
400
+
401
+ ---
402
+
403
+ ## Styling & CSS Variables
404
+
405
+ ```css
406
+ /* Override font */
407
+ .amt-widget { --amt-font: 'Your Font', sans-serif; }
408
+
409
+ /* Key classes for targeted overrides */
410
+ .amt-strip { /* tag chip row */ }
411
+ .amt-tag { /* individual chip */ }
412
+ .amt-tag-count { /* instance count badge */ }
413
+ .amt-group { /* all instances of one tag */ }
414
+ .amt-group-head { /* group header (label + "Add another" button) */ }
415
+ .amt-inst { /* one instance row */ }
416
+ .amt-inst-num { /* instance number badge */ }
417
+ .amt-note-row { /* note sub-row */ }
418
+ .amt-date-row { /* date sub-row */ }
419
+ .amt-chip { /* chip in a chiplist */ }
420
+ .amt-kv-key { /* key field of keyvalue */ }
421
+ .amt-kv-val { /* value field of keyvalue */ }
422
+ ```
423
+
424
+ All library classes are prefixed `amt-` to avoid collision with host page styles.
425
+
426
+ ---
427
+
428
+ ## ArkTestData β€” Companion Test Library
429
+
430
+ `ark-test-data.js` generates realistic Indian news-reporter data to seed and test the widget.
431
+
432
+ ### Quick usage
433
+
434
+ ```js
435
+ // Generate and load into widget
436
+ widget.loadData(ArkTestData.generateMeta());
437
+
438
+ // Generate a full case object
439
+ const c = ArkTestData.generateCase();
440
+ // β†’ { id, title, priority, category, location, meta, body, reporter, … }
441
+
442
+ // Generate N cases with unique IDs
443
+ const cases = ArkTestData.generateCases(10);
444
+
445
+ // Seed localStorage (used by cases-list.html)
446
+ ArkTestData.seedLocalStorage(20); // append 20 cases
447
+ ArkTestData.seedLocalStorage(5, true); // replace with 5
448
+ ArkTestData.clearLocalStorage();
449
+
450
+ // Summary table in console
451
+ ArkTestData.consoleReport(cases);
452
+
453
+ // Use raw corpus for custom generators
454
+ const city = ArkTestData.rng.pick(ArkTestData.corpus.CITIES);
455
+ const phone = ArkTestData.rng.phone();
456
+ const name = ArkTestData.rng.name();
457
+ const date = ArkTestData.rng.isoDate(365); // random past date ≀ 1 year ago
458
+ ```
459
+
460
+ ### Data generated per tag key
461
+
462
+ | Tag key | Generated shape |
463
+ |---|---|
464
+ | `contact` | `{ value: "Priya Krishnan" }` |
465
+ | `phone` | `{ values: ["+91 9876543210"] }` |
466
+ | `fir` | `{ value: "FIR No. 047/2025 β€” Central PS", note: "IPC 302, 307" }` |
467
+ | `section` | `{ value: "IPC 302, BNS 101" }` |
468
+ | `officer` | 1–3 instances β€” `{ value: "SP Rajesh Kumar β€” Tamil Nadu Police", note: "…" }` |
469
+ | `victim` | 1–2 instances β€” `{ value: "Arjun Kumar, 34 yrs, Male", note: "Admitted to Govt. General Hospital" }` |
470
+ | `witness` | `{ value: "Sunita Rao β€” Eyewitness", note: "Identity withheld" }` |
471
+ | `accused` | 1–3 instances β€” `{ value: "Mohan Raj", note: "Arrested; in judicial custody" }` |
472
+ | `status` | `{ value: "In court / Trial", note: "Last updated: 2025-03-10" }` |
473
+ | `court` | 1–4 instances, each one hearing β€” `{ value: "Chennai HC Β· 13 May", note: "Witness absent" }` |
474
+ | `hospital` | `{ value: "Govt. General Hospital", note: "Condition: Stable" }` |
475
+ | `source` | `{ value: "Eyewitness", note: "Contact: +91 …" }` |
476
+ | `ref` | `{ values: ["https://thehindu.com/…", "https://scroll.in/…"] }` |
477
+ | `followup` | 1–3 instances β€” `{ date: "2025-07-01", note: "Chase FIR status from PS" }` |
478
+ | `note` | `{ value: "Off-record: source requested anonymity" }` |
479
+ | `custom` | 1–2 instances β€” `{ key: "RTI Ref No.", value: "RTI/221/2025" }` |
480
+
481
+ ### Smoke test
482
+
483
+ ```js
484
+ const { pass, fail } = ArkTestData.smokeTest();
485
+ // Logs ~20 βœ“/βœ— assertions covering:
486
+ // - case object shape (id, title, priority, reviewStatus, location, meta, body, created)
487
+ // - meta key shapes (phone.values, court.note, custom.key, followup.date)
488
+ // - generateCases(5) unique IDs
489
+ // - localStorage seed, read, clear cycle
490
+ ```
491
+
492
+ ---
493
+
494
+ ## Integration Test Pattern
495
+
496
+ ```js
497
+ function integrationTest(widget) {
498
+ // 1. Generate random meta
499
+ const meta = ArkTestData.generateMeta();
500
+
501
+ // 2. Load into widget
502
+ widget.loadData(meta);
503
+
504
+ // 3. Collect back
505
+ const out = widget.collectData();
506
+
507
+ // 4. Assert shapes match what was loaded
508
+ if (meta.court && out.court) {
509
+ console.assert(out.court.length === meta.court.length, 'court entry count matches');
510
+ console.assert('value' in out.court[0], 'court[0] has value');
511
+ console.assert('note' in out.court[0], 'court[0] has note');
512
+ }
513
+ if (meta.phone) {
514
+ console.assert(Array.isArray(out.phone[0].values), 'phone values is array');
515
+ }
516
+ if (meta.custom) {
517
+ console.assert(out.custom[0].key === meta.custom[0].key, 'custom key round-trips');
518
+ console.assert(out.custom[0].value === meta.custom[0].value, 'custom value round-trips');
519
+ }
520
+ if (meta.followup) {
521
+ console.assert(out.followup[0].date === meta.followup[0].date, 'followup date round-trips');
522
+ }
523
+
524
+ // 5. Reset and verify empty
525
+ widget.reset();
526
+ const empty = widget.collectData();
527
+ console.assert(Object.keys(empty).length === 0, 'reset gives empty collectData()');
528
+
529
+ console.log('Integration test: PASS');
530
+ }
531
+
532
+ integrationTest(new ArkMetaTags('#test-host'));
533
+ ```
534
+
535
+ ---
536
+
537
+ ## Browser Support
538
+
539
+ | Chrome | Firefox | Safari | Edge |
540
+ |:---:|:---:|:---:|:---:|
541
+ | βœ… 80+ | βœ… 75+ | βœ… 13.1+ | βœ… 80+ |
542
+
543
+ Requires: `CustomEvent`, `IntersectionObserver` (docs page only), `navigator.clipboard` (copy button only).
544
+
545
+ ---
546
+
547
+ ## File List
548
+
549
+ | File | Purpose |
550
+ |---|---|
551
+ | `ark-meta-tags.js` | Library β€” mount with `new ArkMetaTags(el, options)` |
552
+ | `ark-test-data.js` | Test helper β€” `ArkTestData.generateMeta()`, `seedLocalStorage()`, `smokeTest()` |
553
+ | `ark-meta-tags-docs.html` | Interactive documentation with all features and live demos |
554
+ | `news-trace-demo.html` | News Trace entry form β€” real-world usage of the library |
555
+ | `cases-list.html` | Responsive case list with approve / reject / invalid review actions |
556
+
557
+ ---
558
+
559
+ ## Changelog
560
+
561
+ ### v1.0.0
562
+ - Initial release extracted from News Trace project
563
+ - 16 built-in news-reporter tag definitions
564
+ - 6 field types: `text`, `textarea`, `select`, `chiplist`, `keyvalue`, `dateNote`
565
+ - `hasNote` and `hasDate` flags for simple types
566
+ - `fields[]` multi-field mode per tag
567
+ - `collectData()` / `loadData()` round-trip
568
+ - DOM events: `ark-meta:add`, `ark-meta:remove`, `ark-meta:change`
569
+ - `onChange` callback option
570
+ - `addTag()`, `reset()`, `destroy()` public methods
571
+ - `ArkMetaTags.defaultTagDefs` static accessor
572
+ - UMD wrapper (script tag / require / import)
573
+ - Self-injecting CSS with `amt-` namespace prefix
574
+
575
+ ---
576
+
577
+ ## License
578
+
579
+ MIT Β© ARK: Immanuel β€” https://immanuel.co