astro-md-editor 0.0.3 → 0.0.5

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.
Files changed (25) hide show
  1. package/.output/nitro.json +1 -1
  2. package/.output/public/assets/index-UQt6zCrS.js +334 -0
  3. package/.output/public/assets/{main-_miJG8oz.js → main-DzXOjsqg.js} +1 -1
  4. package/.output/public/assets/styles-CezfLgHY.css +1 -0
  5. package/.output/server/{__root-C4T8UKZy.mjs → __root-CL_tq3ov.mjs} +1 -1
  6. package/.output/server/_libs/mixmark-io__domino.mjs +16632 -0
  7. package/.output/server/_libs/turndown.mjs +687 -675
  8. package/.output/server/_ssr/IconPickerPopover-fNUx2jAc.mjs +125 -0
  9. package/.output/server/_ssr/{ImageAssetBrowser-DWEdNK_g.mjs → ImageAssetBrowser-DNDdTqtf.mjs} +2 -2
  10. package/.output/server/_ssr/{collections.server-DyZEpKg6.mjs → collections.server-s12asITg.mjs} +24 -1
  11. package/.output/server/_ssr/{file-history.api-BLrUovD8.mjs → file-history.api-oydnxFVV.mjs} +8 -2
  12. package/.output/server/_ssr/{image-assets.server-Dn044pdj.mjs → image-assets.server-Ci2E1_Tm.mjs} +4 -2
  13. package/.output/server/_ssr/{image-preview.api-bqd1erIP.mjs → image-preview.api-pcN5PQhH.mjs} +2 -2
  14. package/.output/server/_ssr/{index-wJDNoCM2.mjs → index-BjQuGIib.mjs} +7 -2
  15. package/.output/server/_ssr/{index-2cOxFkCu.mjs → index-Bn1uKwTu.mjs} +527 -97
  16. package/.output/server/_ssr/index.mjs +17 -11
  17. package/.output/server/_ssr/{router-sxRLAh5a.mjs → router-dPC3iVPs.mjs} +2 -2
  18. package/.output/server/{_tanstack-start-manifest_v-CWdBk-ge.mjs → _tanstack-start-manifest_v-Da8tuNDH.mjs} +1 -1
  19. package/.output/server/index.mjs +50 -50
  20. package/LICENSE.md +21 -0
  21. package/README.md +71 -69
  22. package/package.json +3 -2
  23. package/scripts/bootstrap-collections.mjs +59 -13
  24. package/.output/public/assets/index-DVVq2QjK.js +0 -334
  25. package/.output/public/assets/styles-yeMm8TG7.css +0 -1
@@ -440,7 +440,7 @@ function getResponse() {
440
440
  return event.res;
441
441
  }
442
442
  async function getStartManifest(matchedRoutes) {
443
- const { tsrStartManifest } = await import("../_tanstack-start-manifest_v-CWdBk-ge.mjs");
443
+ const { tsrStartManifest } = await import("../_tanstack-start-manifest_v-Da8tuNDH.mjs");
444
444
  const startManifest = tsrStartManifest();
445
445
  const rootRoute = startManifest.routes[rootRouteId] = startManifest.routes[rootRouteId] || {};
446
446
  rootRoute.assets = rootRoute.assets || [];
@@ -596,31 +596,37 @@ function createMultiplexedStream(jsonStream, rawStreams) {
596
596
  }
597
597
  const manifest = { "e3ae5ae6db5120d43fecff71cd0258cb4a93807a1a382949ccd189596d190566": {
598
598
  functionName: "loadCollectionsData_createServerFn_handler",
599
- importer: () => import("../__root-C4T8UKZy.mjs")
599
+ importer: () => import("../__root-CL_tq3ov.mjs")
600
600
  }, "b1ee42a5714ab9be8f37a4c5fca02ffd6269030b4d3ef663ff002865393e2386": {
601
601
  functionName: "saveSelectionToFile_createServerFn_handler",
602
- importer: () => import("./index-wJDNoCM2.mjs")
602
+ importer: () => import("./index-BjQuGIib.mjs")
603
603
  }, "a215cd23c3ee5dbffd561330a77a2b6ed0e0b5b10aac60190e712ffa6067a121": {
604
604
  functionName: "deleteSelectionFromFile_createServerFn_handler",
605
- importer: () => import("./index-wJDNoCM2.mjs")
605
+ importer: () => import("./index-BjQuGIib.mjs")
606
606
  }, "248b908cf72b9979ba134470388db1f63e57871f390fb58a8be8d0757531e84c": {
607
607
  functionName: "createSelectionFile_createServerFn_handler",
608
- importer: () => import("./index-wJDNoCM2.mjs")
608
+ importer: () => import("./index-BjQuGIib.mjs")
609
609
  }, "28b009a83a8070215402885cb1254e3f7ab49160f54c955f55e15fd2119831e7": {
610
610
  functionName: "listImageAssets_createServerFn_handler",
611
- importer: () => import("./ImageAssetBrowser-DWEdNK_g.mjs")
611
+ importer: () => import("./ImageAssetBrowser-DNDdTqtf.mjs")
612
612
  }, "1f487dcb5d0b909c2785163b904a0ee48e748b32783309e964b4502d83a2e7a7": {
613
613
  functionName: "getImageAssetPreview_createServerFn_handler",
614
- importer: () => import("./ImageAssetBrowser-DWEdNK_g.mjs")
614
+ importer: () => import("./ImageAssetBrowser-DNDdTqtf.mjs")
615
615
  }, "dcd3839820feeaed3b1a4f86186dd05c2e7e26ffaacdeaba309f44d121c95ad1": {
616
616
  functionName: "listFileHistoryServerFn_createServerFn_handler",
617
- importer: () => import("./file-history.api-BLrUovD8.mjs")
617
+ importer: () => import("./file-history.api-oydnxFVV.mjs")
618
618
  }, "02ba027930daf730cf535415a08fab7365d58edddcc678dbcb19c72f990c25b5": {
619
619
  functionName: "readFileRevisionServerFn_createServerFn_handler",
620
- importer: () => import("./file-history.api-BLrUovD8.mjs")
620
+ importer: () => import("./file-history.api-oydnxFVV.mjs")
621
621
  }, "8346edc0b9da2addd68ca674ed9b2030f439d40deb713c45ef88393f5bdb91b9": {
622
622
  functionName: "getLocalImagePreviewServerFn_createServerFn_handler",
623
- importer: () => import("./image-preview.api-bqd1erIP.mjs")
623
+ importer: () => import("./image-preview.api-pcN5PQhH.mjs")
624
+ }, "3ba1a566c82090c8422dd6ca7258b2e1aed644206364441faf1bd80942f21745": {
625
+ functionName: "listIconifyCollectionsServerFn_createServerFn_handler",
626
+ importer: () => import("./IconPickerPopover-fNUx2jAc.mjs")
627
+ }, "c9dff6ed7373b505d6aeeb84c63f87a0d0c205051d9d817929a5b500e5e79e03": {
628
+ functionName: "listCollectionIconsServerFn_createServerFn_handler",
629
+ importer: () => import("./IconPickerPopover-fNUx2jAc.mjs")
624
630
  } };
625
631
  async function getServerFnById(id) {
626
632
  const serverFnInfo = manifest[id];
@@ -1043,7 +1049,7 @@ let entriesPromise;
1043
1049
  let baseManifestPromise;
1044
1050
  let cachedFinalManifestPromise;
1045
1051
  async function loadEntries() {
1046
- const routerEntry = await import("./router-sxRLAh5a.mjs").then((n) => n.r);
1052
+ const routerEntry = await import("./router-dPC3iVPs.mjs").then((n) => n.r);
1047
1053
  const startEntry = await import("./start-HYkvq4Ni.mjs");
1048
1054
  return { startEntry, routerEntry };
1049
1055
  }
@@ -47,7 +47,7 @@ const createSsrRpc = (functionId, importer) => {
47
47
  [TSS_SERVER_FUNCTION]: true
48
48
  });
49
49
  };
50
- const appCss = "/assets/styles-yeMm8TG7.css";
50
+ const appCss = "/assets/styles-CezfLgHY.css";
51
51
  const CollectionsDataContext = reactExports.createContext(null);
52
52
  function CollectionsDataProvider({
53
53
  value,
@@ -144,7 +144,7 @@ function RootDocument({
144
144
  ] })
145
145
  ] });
146
146
  }
147
- const $$splitComponentImporter = () => import("./index-2cOxFkCu.mjs");
147
+ const $$splitComponentImporter = () => import("./index-Bn1uKwTu.mjs");
148
148
  const Route = createFileRoute("/")({
149
149
  validateSearch: (search) => {
150
150
  const record = search;
@@ -1,4 +1,4 @@
1
- const tsrStartManifest = () => ({ "routes": { "__root__": { "filePath": "/Users/bimsina/Desktop/Development/Personal/astro-md-editor/src/routes/__root.tsx", "children": ["/"], "preloads": ["/assets/main-_miJG8oz.js"], "assets": [] }, "/": { "filePath": "/Users/bimsina/Desktop/Development/Personal/astro-md-editor/src/routes/index.tsx", "assets": [], "preloads": ["/assets/index-DVVq2QjK.js"] } }, "clientEntry": "/assets/main-_miJG8oz.js" });
1
+ const tsrStartManifest = () => ({ "routes": { "__root__": { "filePath": "/Users/bimsina/Desktop/Development/Personal/astro-md-editor/src/routes/__root.tsx", "children": ["/"], "preloads": ["/assets/main-DzXOjsqg.js"], "assets": [] }, "/": { "filePath": "/Users/bimsina/Desktop/Development/Personal/astro-md-editor/src/routes/index.tsx", "assets": [], "preloads": ["/assets/index-UQt6zCrS.js"] } }, "clientEntry": "/assets/main-DzXOjsqg.js" });
2
2
  export {
3
3
  tsrStartManifest
4
4
  };
@@ -100,107 +100,107 @@ const assets = {
100
100
  "/favicon.ico": {
101
101
  "type": "image/vnd.microsoft.icon",
102
102
  "etag": '"f1e-ESBTjHetHyiokkO0tT/irBbMO8Y"',
103
- "mtime": "2026-03-07T13:57:35.890Z",
103
+ "mtime": "2026-03-08T06:10:25.839Z",
104
104
  "size": 3870,
105
105
  "path": "../public/favicon.ico"
106
106
  },
107
+ "/manifest.json": {
108
+ "type": "application/json",
109
+ "etag": '"1f2-Oqn/x1R1hBTtEjA8nFhpBeFJJNg"',
110
+ "mtime": "2026-03-08T06:10:25.839Z",
111
+ "size": 498,
112
+ "path": "../public/manifest.json"
113
+ },
107
114
  "/logo192.png": {
108
115
  "type": "image/png",
109
116
  "etag": '"14e3-f08taHgqf6/O2oRVTsq5tImHdQA"',
110
- "mtime": "2026-03-07T13:57:35.890Z",
117
+ "mtime": "2026-03-08T06:10:25.839Z",
111
118
  "size": 5347,
112
119
  "path": "../public/logo192.png"
113
120
  },
114
- "/logo512.png": {
115
- "type": "image/png",
116
- "etag": '"25c0-RpFfnQJpTtSb/HqVNJR2hBA9w/4"',
117
- "mtime": "2026-03-07T13:57:35.890Z",
118
- "size": 9664,
119
- "path": "../public/logo512.png"
120
- },
121
121
  "/robots.txt": {
122
122
  "type": "text/plain; charset=utf-8",
123
123
  "etag": '"43-BEzmj4PuhUNHX+oW9uOnPSihxtU"',
124
- "mtime": "2026-03-07T13:57:35.890Z",
124
+ "mtime": "2026-03-08T06:10:25.839Z",
125
125
  "size": 67,
126
126
  "path": "../public/robots.txt"
127
127
  },
128
- "/manifest.json": {
129
- "type": "application/json",
130
- "etag": '"1f2-Oqn/x1R1hBTtEjA8nFhpBeFJJNg"',
131
- "mtime": "2026-03-07T13:57:35.890Z",
132
- "size": 498,
133
- "path": "../public/manifest.json"
134
- },
135
- "/assets/inter-cyrillic-ext-wght-normal-BOeWTOD4.woff2": {
136
- "type": "font/woff2",
137
- "etag": '"6568-cF1iUGbboMFZ8TfnP5HiMgl9II0"',
138
- "mtime": "2026-03-07T13:57:35.439Z",
139
- "size": 25960,
140
- "path": "../public/assets/inter-cyrillic-ext-wght-normal-BOeWTOD4.woff2"
141
- },
142
128
  "/assets/inter-cyrillic-wght-normal-DqGufNeO.woff2": {
143
129
  "type": "font/woff2",
144
130
  "etag": '"493c-n3Oy9D6jvzfMjpClqox+Zo7ERQQ"',
145
- "mtime": "2026-03-07T13:57:35.439Z",
131
+ "mtime": "2026-03-08T06:10:25.333Z",
146
132
  "size": 18748,
147
133
  "path": "../public/assets/inter-cyrillic-wght-normal-DqGufNeO.woff2"
148
134
  },
149
135
  "/assets/inter-greek-ext-wght-normal-DlzME5K_.woff2": {
150
136
  "type": "font/woff2",
151
137
  "etag": '"2be0-BP5iTzJeB8nLqYAgKpWNi5o1Zm8"',
152
- "mtime": "2026-03-07T13:57:35.439Z",
138
+ "mtime": "2026-03-08T06:10:25.332Z",
153
139
  "size": 11232,
154
140
  "path": "../public/assets/inter-greek-ext-wght-normal-DlzME5K_.woff2"
155
141
  },
142
+ "/assets/inter-cyrillic-ext-wght-normal-BOeWTOD4.woff2": {
143
+ "type": "font/woff2",
144
+ "etag": '"6568-cF1iUGbboMFZ8TfnP5HiMgl9II0"',
145
+ "mtime": "2026-03-08T06:10:25.332Z",
146
+ "size": 25960,
147
+ "path": "../public/assets/inter-cyrillic-ext-wght-normal-BOeWTOD4.woff2"
148
+ },
149
+ "/logo512.png": {
150
+ "type": "image/png",
151
+ "etag": '"25c0-RpFfnQJpTtSb/HqVNJR2hBA9w/4"',
152
+ "mtime": "2026-03-08T06:10:25.839Z",
153
+ "size": 9664,
154
+ "path": "../public/logo512.png"
155
+ },
156
156
  "/assets/inter-greek-wght-normal-CkhJZR-_.woff2": {
157
157
  "type": "font/woff2",
158
158
  "etag": '"4a34-xor/hj4YNqI52zFecXnUbzQ4Xs4"',
159
- "mtime": "2026-03-07T13:57:35.439Z",
159
+ "mtime": "2026-03-08T06:10:25.333Z",
160
160
  "size": 18996,
161
161
  "path": "../public/assets/inter-greek-wght-normal-CkhJZR-_.woff2"
162
162
  },
163
- "/assets/inter-latin-wght-normal-Dx4kXJAl.woff2": {
164
- "type": "font/woff2",
165
- "etag": '"bc80-8R1ym7Ck2DUNLqPQ/AYs9u8tUpg"',
166
- "mtime": "2026-03-07T13:57:35.439Z",
167
- "size": 48256,
168
- "path": "../public/assets/inter-latin-wght-normal-Dx4kXJAl.woff2"
169
- },
170
163
  "/assets/inter-latin-ext-wght-normal-DO1Apj_S.woff2": {
171
164
  "type": "font/woff2",
172
165
  "etag": '"14c4c-zz61D7IQFMB9QxHvTAOk/Vh4ibQ"',
173
- "mtime": "2026-03-07T13:57:35.439Z",
166
+ "mtime": "2026-03-08T06:10:25.333Z",
174
167
  "size": 85068,
175
168
  "path": "../public/assets/inter-latin-ext-wght-normal-DO1Apj_S.woff2"
176
169
  },
170
+ "/assets/inter-latin-wght-normal-Dx4kXJAl.woff2": {
171
+ "type": "font/woff2",
172
+ "etag": '"bc80-8R1ym7Ck2DUNLqPQ/AYs9u8tUpg"',
173
+ "mtime": "2026-03-08T06:10:25.333Z",
174
+ "size": 48256,
175
+ "path": "../public/assets/inter-latin-wght-normal-Dx4kXJAl.woff2"
176
+ },
177
177
  "/assets/inter-vietnamese-wght-normal-CBcvBZtf.woff2": {
178
178
  "type": "font/woff2",
179
179
  "etag": '"280c-nBythjoDQ0+5wVAendJ6wU7Xz2M"',
180
- "mtime": "2026-03-07T13:57:35.439Z",
180
+ "mtime": "2026-03-08T06:10:25.333Z",
181
181
  "size": 10252,
182
182
  "path": "../public/assets/inter-vietnamese-wght-normal-CBcvBZtf.woff2"
183
183
  },
184
- "/assets/styles-yeMm8TG7.css": {
184
+ "/assets/styles-CezfLgHY.css": {
185
185
  "type": "text/css; charset=utf-8",
186
- "etag": '"166a9-I6btGAgVFRFRXGymDNZj4iIz93M"',
187
- "mtime": "2026-03-07T13:57:35.439Z",
188
- "size": 91817,
189
- "path": "../public/assets/styles-yeMm8TG7.css"
186
+ "etag": '"16c03-6spcY0zAbMWYIfx4MkgUIWefXb0"',
187
+ "mtime": "2026-03-08T06:10:25.333Z",
188
+ "size": 93187,
189
+ "path": "../public/assets/styles-CezfLgHY.css"
190
190
  },
191
- "/assets/main-_miJG8oz.js": {
191
+ "/assets/main-DzXOjsqg.js": {
192
192
  "type": "text/javascript; charset=utf-8",
193
- "etag": '"59339-CcbCeqp9UxyIUIPAD9lofPs515E"',
194
- "mtime": "2026-03-07T13:57:35.439Z",
193
+ "etag": '"59339-tSYXNZ6CNL1Zt0KVnL8TeohfAog"',
194
+ "mtime": "2026-03-08T06:10:25.336Z",
195
195
  "size": 365369,
196
- "path": "../public/assets/main-_miJG8oz.js"
196
+ "path": "../public/assets/main-DzXOjsqg.js"
197
197
  },
198
- "/assets/index-DVVq2QjK.js": {
198
+ "/assets/index-UQt6zCrS.js": {
199
199
  "type": "text/javascript; charset=utf-8",
200
- "etag": '"fa95d-3mgUdOunP1JUZweDUAkGUnl6d2w"',
201
- "mtime": "2026-03-07T13:57:35.439Z",
202
- "size": 1026397,
203
- "path": "../public/assets/index-DVVq2QjK.js"
200
+ "etag": '"fd680-MTEy4A2zDMKeSP7AuzGHvXOYiSs"',
201
+ "mtime": "2026-03-08T06:10:25.337Z",
202
+ "size": 1037952,
203
+ "path": "../public/assets/index-UQt6zCrS.js"
204
204
  }
205
205
  };
206
206
  function readAsset(id) {
package/LICENSE.md ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Bibek Timsina
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 CHANGED
@@ -2,38 +2,38 @@
2
2
 
3
3
  [![npm version](https://img.shields.io/npm/v/astro-md-editor.svg)](https://www.npmjs.com/package/astro-md-editor)
4
4
 
5
- Editor UI for Astro content collections (`src/content`) with schema-aware frontmatter editing and markdown body editing.
5
+ Schema-aware editor for Astro content collections. It lets you edit frontmatter and markdown/MDX together, validates on save, and can reuse values from file Git history.
6
6
 
7
- ## Run
7
+ Demo video: https://www.youtube.com/watch?v=-cI6ct8yLHM
8
8
 
9
- ```bash
10
- npx astro-md-editor
11
- ```
9
+ https://github.com/user-attachments/assets/7f38b6a4-fe1a-4e30-80c8-19a58aa860f1
10
+
11
+ ## Quick start
12
12
 
13
13
  ```bash
14
- pnpm dlx astro-md-editor
14
+ npx astro-md-editor
15
15
  # or
16
- pnpx astro-md-editor
16
+ pnpm dlx astro-md-editor
17
17
  ```
18
18
 
19
- ### Target project path
19
+ By default, it targets the current directory.
20
20
 
21
- ```bash
22
- # positional
23
- npx astro-md-editor /absolute/path/to/astro-project
21
+ Target a specific Astro project:
24
22
 
25
- # flag
26
- npx astro-md-editor --path /absolute/path/to/astro-project
23
+ ```bash
24
+ astro-md-editor /absolute/path/to/astro-project
25
+ # or
26
+ astro-md-editor --path /absolute/path/to/astro-project
27
27
  ```
28
28
 
29
- Path resolution order:
29
+ Root resolution order:
30
30
 
31
- 1. positional CLI arg (`astro-md-editor <astro-project-root>`)
32
- 2. `--path=<astro-project-root>`
31
+ 1. positional argument (`astro-md-editor <astro-project-root>`)
32
+ 2. `--path`
33
33
  3. `APP_ROOT_PATH`
34
34
  4. current working directory
35
35
 
36
- ### Port
36
+ Port can be set with `PORT` or `NITRO_PORT`.
37
37
 
38
38
  ```bash
39
39
  PORT=1234 npx astro-md-editor
@@ -41,60 +41,37 @@ PORT=1234 npx astro-md-editor
41
41
  NITRO_PORT=1234 npx astro-md-editor
42
42
  ```
43
43
 
44
- The runtime reads `PORT`/`NITRO_PORT` for server port.
45
-
46
44
  ## Requirements
47
45
 
48
- - Astro project with collections in `src/content`.
49
- - Collection schemas available in `.astro/collections/*.schema.json`.
46
+ - Astro project with collections under `src/content`
47
+ - Generated collection schemas in `.astro/collections/*.schema.json`
50
48
 
51
- If schema files are missing, run:
49
+ If schema files are missing, run in the target project:
52
50
 
53
51
  ```bash
54
52
  astro sync
55
53
  ```
56
54
 
57
- ## Features
58
-
59
- - Reads `.astro/collections/*.schema.json`.
60
- - Scans `src/content/<collection>/**/*.{md,mdx,markdown}`.
61
- - Renders controls for schema fields (`string`, `number`, `boolean`, `enum`, `date`, arrays).
62
- - Edits frontmatter and markdown/MDX body in one flow.
63
- - Validates frontmatter with AJV before save.
64
- - Supports image field inference from `src/content.config.*` (`image()`, `z.array(image())`).
65
- - Supports image pickers for both `src` assets and `public` assets.
66
- - Includes a per-file Git History tab in the right sidebar (timeline-style commit list).
67
- - Shows commit message, author, and time; expands each item to inspect revision frontmatter.
68
- - Can apply a selected revision into the current draft with schema-safe frontmatter reapply (incompatible keys are skipped).
69
- - Applying history updates only the in-editor draft; normal Save flow still controls disk writes.
70
-
71
- ## Local development
72
-
73
- ```bash
74
- pnpm dev
75
- ```
76
-
77
- `pnpm dev` uses `--path example-blog --port 3000`.
78
-
79
- Run dev against another project:
80
-
81
- ```bash
82
- node scripts/dev.mjs /absolute/path/to/astro-project --port 3000
83
- ```
84
-
85
- In dev mode, if schema files are missing, startup runs `astro sync` automatically for the selected project.
55
+ ## Highlights
86
56
 
87
- ## Field overrides
57
+ - Reads collection schemas and content files (`.md`, `.mdx`, `.markdown`)
58
+ - Renders schema-aware controls (`string`, `number`, `boolean`, `enum`, `date`, arrays)
59
+ - Validates frontmatter with AJV before saving
60
+ - Infers image fields from `src/content.config.*` (`image()` / `z.array(image())`)
61
+ - Supports image pickers for both `src` assets and `public` assets
62
+ - Shows per-file Git history and applies selected revision values to the current draft safely
88
63
 
89
- Create `astro-md-editor.fields.json` at the Astro project root.
64
+ ## Field overrides (optional)
90
65
 
91
- Example:
66
+ Create `astro-md-editor.fields.json` at the Astro project root to force specific UI controls.
92
67
 
93
68
  ```json
94
69
  {
95
70
  "blog": {
96
71
  "brandColor": { "type": "color" },
97
- "coverAssetPath": { "type": "image", "mode": "public" }
72
+ "coverAssetPath": { "type": "image", "mode": "public" },
73
+ "menuIconFiltered": { "type": "icon", "icon_libraries": ["lucide", "mdi"] },
74
+ "menuIconAny": { "type": "icon" }
98
75
  },
99
76
  "snippet": {
100
77
  "galleryPaths": { "type": "image", "multiple": true, "mode": "public" }
@@ -102,23 +79,48 @@ Example:
102
79
  }
103
80
  ```
104
81
 
105
- Rules:
82
+ Supported override types:
106
83
 
107
- - `type`: `image` or `color`
108
- - for `type: "image"`, optional `mode`: `asset` (default) or `public`
109
- - for `type: "image"`, optional `multiple: true` for array-style image input
110
- - shape: `{ [collectionName]: { [fieldName]: override } }`
84
+ - `image` (`mode: "asset" | "public"`, optional `multiple: true`)
85
+ - `color`
86
+ - `icon` (optional `icon_libraries: string[]`)
111
87
 
112
- Precedence:
88
+ Precedence: `astro-md-editor.fields.json` > inferred `image()` fields > schema defaults.
113
89
 
114
- 1. `astro-md-editor.fields.json` overrides
115
- 2. inferred `image()` / `array(image())` from content config
116
- 3. schema-based fallback rendering
117
-
118
- Invalid or incompatible overrides are ignored with bootstrap warnings.
90
+ Invalid or schema-incompatible overrides are ignored with startup warnings.
119
91
 
120
92
  ## Notes
121
93
 
122
- - `public` assets are saved as `/path/from/public`.
123
- - `src` assets are saved as relative paths from the content file (`./...` or `../...`).
124
- - Custom controls currently target top-level frontmatter fields.
94
+ - Applying Git history updates the in-editor draft only; files are written only on normal Save
95
+ - `public` assets are saved as `/path/from/public`
96
+ - `src` assets are saved relative to the edited content file (`./...` or `../...`)
97
+ - Icon values are saved as Iconify IDs (for example `lucide:messages-square`)
98
+ - Custom controls currently target top-level frontmatter fields
99
+
100
+ ## Add a custom UI handler in code
101
+
102
+ If you are extending the editor itself (not just using `astro-md-editor.fields.json`), add a new field kind in these places:
103
+
104
+ 1. `src/lib/schema-form.ts`
105
+ - Extend `FieldUiKind`, `FieldUiConfig`, and `ResolvedField`
106
+ - Update `resolveCustomFieldKind` to map your override kind to a resolved field
107
+
108
+ 2. `scripts/bootstrap-collections.mjs`
109
+ - Extend `fieldOverrideSchema` so config accepts your new `type`
110
+ - Update `getOverrideConfig` and `resolveSchemaCompatibilityKind`
111
+
112
+ 3. `src/components/editor/RightSidebar.tsx`
113
+ - Add your new field component
114
+ - Render it in the `resolvedFields.map(...)` switch (`field.kind === '<yourKind>'`)
115
+
116
+ 4. `src/lib/frontmatter-history-merge.ts`
117
+ - Update `isCompatibleValue` so Git-history apply validates your new kind correctly
118
+
119
+ Quick checklist for a new kind (example: `slug`):
120
+
121
+ - add `slug` to schema/field types
122
+ - parse `type: "slug"` in bootstrap overrides
123
+ - render `<SlugField />` in `RightSidebar`
124
+ - add compatibility logic in history merge
125
+
126
+ If any part is missing, the field falls back to default/unsupported handling.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "astro-md-editor",
3
- "version": "0.0.3",
3
+ "version": "0.0.5",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "bin": {
@@ -10,7 +10,8 @@
10
10
  ".output/",
11
11
  "index.mjs",
12
12
  "scripts/bootstrap-collections.mjs",
13
- "README.md"
13
+ "README.md",
14
+ "LICENSE.md"
14
15
  ],
15
16
  "imports": {
16
17
  "#/*": "./src/*"
@@ -36,6 +36,12 @@ const fieldOverrideSchema = z.discriminatedUnion('type', [
36
36
  type: z.literal('color'),
37
37
  })
38
38
  .strict(),
39
+ z
40
+ .object({
41
+ type: z.literal('icon'),
42
+ icon_libraries: z.array(z.string()).optional(),
43
+ })
44
+ .strict(),
39
45
  ]);
40
46
 
41
47
  function isRecord(value) {
@@ -673,7 +679,7 @@ async function runAstroSync(rootDir, env = process.env) {
673
679
  const astroCliPath = await resolveAstroCliPath(rootDir);
674
680
  if (!astroCliPath) {
675
681
  throw new Error(
676
- `Collections are missing and Astro CLI was not found at ${resolve(
682
+ `Astro CLI was not found at ${resolve(
677
683
  rootDir,
678
684
  'node_modules/.bin/astro',
679
685
  )}. Install dependencies in ${rootDir} and try again.`,
@@ -968,9 +974,29 @@ function resolveSchemaCompatibilityKind(propertySchema, desiredKind) {
968
974
  return 'color';
969
975
  }
970
976
 
977
+ if (desiredKind === 'icon' && propertySchema.type === 'string') {
978
+ return 'icon';
979
+ }
980
+
971
981
  return undefined;
972
982
  }
973
983
 
984
+ function normalizeIconLibraries(iconLibraries) {
985
+ if (!Array.isArray(iconLibraries)) {
986
+ return undefined;
987
+ }
988
+
989
+ const normalized = [
990
+ ...new Set(
991
+ iconLibraries
992
+ .map((value) => (typeof value === 'string' ? value.trim() : ''))
993
+ .filter((value) => value.length > 0)
994
+ .map((value) => value.toLowerCase()),
995
+ ),
996
+ ];
997
+ return normalized.length > 0 ? normalized : undefined;
998
+ }
999
+
974
1000
  function getOverrideConfig(override) {
975
1001
  if (!override) {
976
1002
  return undefined;
@@ -989,6 +1015,13 @@ function getOverrideConfig(override) {
989
1015
  };
990
1016
  }
991
1017
 
1018
+ if (override.type === 'icon') {
1019
+ return {
1020
+ kind: 'icon',
1021
+ iconLibraries: normalizeIconLibraries(override.icon_libraries),
1022
+ };
1023
+ }
1024
+
992
1025
  return undefined;
993
1026
  }
994
1027
 
@@ -1002,7 +1035,13 @@ function resolveCollectionFieldUiKinds(params) {
1002
1035
 
1003
1036
  const resolvedKinds = {};
1004
1037
 
1005
- const applyKind = (fieldKey, nextKind, sourceLabel, mode = 'asset') => {
1038
+ const applyKind = (
1039
+ fieldKey,
1040
+ nextKind,
1041
+ sourceLabel,
1042
+ mode = 'asset',
1043
+ iconLibraries,
1044
+ ) => {
1006
1045
  if (!nextKind) {
1007
1046
  return;
1008
1047
  }
@@ -1028,6 +1067,14 @@ function resolveCollectionFieldUiKinds(params) {
1028
1067
  return;
1029
1068
  }
1030
1069
 
1070
+ if (compatibleKind === 'icon') {
1071
+ resolvedKinds[fieldKey] = {
1072
+ kind: 'icon',
1073
+ iconLibraries,
1074
+ };
1075
+ return;
1076
+ }
1077
+
1031
1078
  resolvedKinds[fieldKey] = {
1032
1079
  kind: compatibleKind,
1033
1080
  };
@@ -1041,7 +1088,13 @@ function resolveCollectionFieldUiKinds(params) {
1041
1088
 
1042
1089
  for (const [fieldKey, override] of Object.entries(overrideEntries ?? {})) {
1043
1090
  const overrideConfig = getOverrideConfig(override);
1044
- applyKind(fieldKey, overrideConfig?.kind, 'custom', overrideConfig?.mode);
1091
+ applyKind(
1092
+ fieldKey,
1093
+ overrideConfig?.kind,
1094
+ 'custom',
1095
+ overrideConfig?.mode,
1096
+ overrideConfig?.iconLibraries,
1097
+ );
1045
1098
  }
1046
1099
 
1047
1100
  return Object.keys(resolvedKinds).length > 0 ? resolvedKinds : undefined;
@@ -1073,18 +1126,11 @@ export async function bootstrapCollections(argv = [], options = {}) {
1073
1126
  throw new Error(`Root path is not a directory: ${rootDir}`);
1074
1127
  }
1075
1128
 
1076
- let collectionsParsed = await buildCollectionsFromFilesystem(rootDir);
1077
- const hasAnySchema = collectionsParsed.collections.some(
1078
- (collection) => isRecord(collection) && collection.hasSchema === true,
1079
- );
1080
- if (!hasAnySchema && mode === 'development') {
1081
- const schemaDirPath = resolve(rootDir, COLLECTIONS_RELATIVE_PATH_PREFIX);
1082
- console.warn(
1083
- `[bootstrap] No schema files found in ${schemaDirPath}; running "astro sync" in ${rootDir}.`,
1084
- );
1129
+ if (mode === 'development') {
1130
+ console.warn(`[bootstrap] Running "astro sync" in ${rootDir}.`);
1085
1131
  await runAstroSync(rootDir, env);
1086
- collectionsParsed = await buildCollectionsFromFilesystem(rootDir);
1087
1132
  }
1133
+ let collectionsParsed = await buildCollectionsFromFilesystem(rootDir);
1088
1134
 
1089
1135
  if (
1090
1136
  !isRecord(collectionsParsed) ||