@burger-editor/local 4.0.0-alpha.46 → 4.0.0-alpha.47

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 CHANGED
@@ -120,6 +120,52 @@ title: 'New Page'
120
120
  - `interval` (number): チェック間隔(ミリ秒)(デフォルト: 10000)
121
121
  - `retryCount` (number): リトライ回数(デフォルト: 3)
122
122
 
123
+ ## Front Matter編集機能
124
+
125
+ BurgerEditorは、HTMLファイルのFront Matter(YAMLメタデータ)を編集するUIを提供します。編集ボックスはBurgerEditor編集領域の上部に表示されます。
126
+
127
+ ### 対応するデータ型
128
+
129
+ Front Matterエディターは、値の型を自動検出して適切な入力UIを表示します:
130
+
131
+ | 型 | 入力UI | 例 |
132
+ | ----------------- | ------------------ | ------------------------ |
133
+ | 文字列 | テキスト入力 | `title: 'Hello World'` |
134
+ | 数値 | 数値入力 | `order: 1` |
135
+ | 真偽値 | チェックボックス | `published: true` |
136
+ | 日付 | 日付ピッカー | `date: '2025-01-06'` |
137
+ | 配列/オブジェクト | JSONテキストエリア | `tags: ['blog', 'news']` |
138
+
139
+ ### 使用方法
140
+
141
+ 1. HTMLファイルの先頭にFront Matterを記述します:
142
+
143
+ ```html
144
+ ---
145
+ title: 'ページタイトル'
146
+ date: '2025-01-06'
147
+ published: true
148
+ ---
149
+
150
+ <div class="my-editor">
151
+ <!-- コンテンツ -->
152
+ </div>
153
+ ```
154
+
155
+ 2. BurgerEditorでファイルを開くと、編集領域の上にFront Matterエディターが表示されます
156
+
157
+ 3. フィールドの追加・削除、値の編集が可能です
158
+
159
+ 4. 変更は自動的に保存されます(500msのデバウンス付き)
160
+
161
+ ### 新規フィールドの追加
162
+
163
+ 「+ 追加」ボタンをクリックすると、新しいフィールドを追加できます:
164
+
165
+ 1. キー名を入力(例: `author`, `category`)
166
+ 2. 型を選択(テキスト、数値、真偽値、日付、JSON)
167
+ 3. 「追加」ボタンをクリック
168
+
123
169
  ## カスタムブロックカタログの追加
124
170
 
125
171
  既存のブロックカタログにカスタムブロックを追加できます:
package/dist/client.js CHANGED
@@ -1294,7 +1294,7 @@ var style$b = "/* No Styling */\n";
1294
1294
  var template$b = "<a href=\"\" data-kind=\"primary\" data-before-icon=\"none\" data-after-icon=\"none\" data-bge=\"link:href, target:target, kind:data-kind, before-icon:data-before-icon, after-icon:data-after-icon\">\n\t<div>\n\t\t<span data-bge=\"text\">ボタン</span>\n\t\t<span data-bge=\"subtext\">サブテキスト</span>\n\t</div>\n</a>\n";
1295
1295
 
1296
1296
  var button = createItem$1({
1297
- version: "4.0.0-alpha.45",
1297
+ version: "4.0.0-alpha.46",
1298
1298
  name: "button",
1299
1299
  template: template$b,
1300
1300
  style: style$b,
@@ -1350,7 +1350,7 @@ var style$a = "/* No Styling */\n";
1350
1350
  var template$a = "<details data-bge=\"open:open\">\n\t<summary data-bge=\"summary\">折りたたみコンテンツ</summary>\n\t<div data-bge=\"content\"><p>内容を入力してください</p></div>\n</details>\n";
1351
1351
 
1352
1352
  var details = createItem$1({
1353
- version: "4.0.0-alpha.45",
1353
+ version: "4.0.0-alpha.46",
1354
1354
  name: "details",
1355
1355
  template: template$a,
1356
1356
  style: style$a,
@@ -1364,7 +1364,7 @@ var style$9 = "[data-bgi='download-file'] {\n\t[data-bge*='size'] {\n\t\t&::befo
1364
1364
  var template$9 = "<a href=\"./files/bgeditor/bg-sample.pdf\" target=\"_blank\" data-bge=\"path:href, download:download\">\n\t<div>\n\t\t<span data-bge=\"name\">サンプルダウンロードファイル</span>\n\t\t<span data-bge=\"formated-size, size:data-size\" data-size=\"138158\">134.92kB</span>\n\t</div>\n</a>\n";
1365
1365
 
1366
1366
  var downloadFile = createItem$1({
1367
- version: "4.0.0-alpha.45",
1367
+ version: "4.0.0-alpha.46",
1368
1368
  name: "download-file",
1369
1369
  template: template$9,
1370
1370
  style: style$9,
@@ -1403,7 +1403,7 @@ var style$8 = "[data-bgi='google-maps'] {\n\tdiv {\n\t\tinline-size: 100%;\n\t\t
1403
1403
  var template$8 = "<div data-lat=\"35.681382\" data-lng=\"139.766084\" data-zoom=\"16\" data-bge=\"lat:data-lat, lng:data-lng, zoom:data-zoom\">\n\t<img data-bge=\"img:src\" src=\"https://maps.google.com/maps/api/staticmap?center=35.681382,139.766084&amp;zoom=16&amp;size=640x400&amp;markers=color:red|color:red|35.681382,139.766084&amp;scale=2&amp;key=%googleMapsApiKey%\" width=\"8\" height=\"5\" alt=\"Google Maps\" />\n</div>\n<a href=\"https://maps.apple.com/?q=35.681382,139.766084\" data-bge=\"url:href\" target=\"_blank\"><span>アプリで開く</span></a>\n";
1404
1404
 
1405
1405
  var googleMaps = createItem$1({
1406
- version: "4.0.0-alpha.45",
1406
+ version: "4.0.0-alpha.46",
1407
1407
  name: "google-maps",
1408
1408
  template: template$8,
1409
1409
  style: style$8,
@@ -1538,7 +1538,7 @@ var style$7 = "[data-bgi='hr'] {\n\t--inline-size: 100%;\n\t--border-color: #000
1538
1538
  var template$7 = "<div data-bgi-hr-kind=\"primary\" data-bge=\"kind:data-bgi-hr-kind\">\n\t<hr />\n</div>\n";
1539
1539
 
1540
1540
  var hr = createItem$1({
1541
- version: "4.0.0-alpha.45",
1541
+ version: "4.0.0-alpha.46",
1542
1542
  name: "hr",
1543
1543
  template: template$7,
1544
1544
  style: style$7,
@@ -1628,7 +1628,7 @@ function createWidthState() {
1628
1628
 
1629
1629
  const ORIGIN = "__org";
1630
1630
  var image = createItem$1({
1631
- version: "4.0.0-alpha.45",
1631
+ version: "4.0.0-alpha.46",
1632
1632
  name: "image",
1633
1633
  template: template$6,
1634
1634
  style: style$6,
@@ -1826,7 +1826,7 @@ var style$5 = "[data-bgi='import'] {\n\tbge-import {\n\t\t&::before {\n\t\t\tfon
1826
1826
  var template$5 = "<bge-import data-bge=\":src\" src=\"\"></bge-import>\n";
1827
1827
 
1828
1828
  var importItem = createItem$1({
1829
- version: "4.0.0-alpha.45",
1829
+ version: "4.0.0-alpha.46",
1830
1830
  name: "import",
1831
1831
  template: template$5,
1832
1832
  style: style$5,
@@ -1840,7 +1840,7 @@ var style$4 = "/* No Styling */\n";
1840
1840
  var template$4 = "<div data-bge=\":scrollable\" data-bge-scrollable=\"false\">\n\t<table>\n\t\t<caption data-bge=\"caption\">\n\t\t\tキャプションを入力してください\n\t\t</caption>\n\t\t<tbody data-bge-list>\n\t\t\t<tr>\n\t\t\t\t<th data-bge=\"th\">表組の見出し</th>\n\t\t\t\t<td data-bge=\"td\">表組の内容を入力してください</td>\n\t\t\t</tr>\n\t\t</tbody>\n\t</table>\n</div>\n";
1841
1841
 
1842
1842
  var table = createItem$1({
1843
- version: "4.0.0-alpha.45",
1843
+ version: "4.0.0-alpha.46",
1844
1844
  name: "table",
1845
1845
  template: template$4,
1846
1846
  style: style$4,
@@ -1868,7 +1868,7 @@ var style$3 = "/* No Styling */\n";
1868
1868
  var template$3 = "<h2 data-bge=\"title-h2\">見出しを入力してください</h2>\n";
1869
1869
 
1870
1870
  var titleH2 = createItem$1({
1871
- version: "4.0.0-alpha.45",
1871
+ version: "4.0.0-alpha.46",
1872
1872
  name: "title-h2",
1873
1873
  template: template$3,
1874
1874
  style: style$3,
@@ -1882,7 +1882,7 @@ var style$2 = "/* No Styling */\n";
1882
1882
  var template$2 = "<h3 data-bge=\"title-h3\">見出しを入力してください</h3>\n";
1883
1883
 
1884
1884
  var titleH3 = createItem$1({
1885
- version: "4.0.0-alpha.45",
1885
+ version: "4.0.0-alpha.46",
1886
1886
  name: "title-h3",
1887
1887
  template: template$2,
1888
1888
  style: style$2,
@@ -1896,7 +1896,7 @@ var style$1 = "/* No Styling */\n";
1896
1896
  var template$1 = "<div data-bge=\"wysiwyg\"><p>本文を入力してください</p></div>\n";
1897
1897
 
1898
1898
  var wysiwyg = createItem$1({
1899
- version: "4.0.0-alpha.45",
1899
+ version: "4.0.0-alpha.46",
1900
1900
  name: "wysiwyg",
1901
1901
  template: template$1,
1902
1902
  style: style$1,
@@ -1911,7 +1911,7 @@ var template = "<div data-id=\"3KtWfp0UopM\" data-title=\"YouTube動画\" data-w
1911
1911
 
1912
1912
  const FALLBACK_TITLE = "YouTube\u52D5\u753B";
1913
1913
  var youtube = createItem$1({
1914
- version: "4.0.0-alpha.45",
1914
+ version: "4.0.0-alpha.46",
1915
1915
  name: "youtube",
1916
1916
  template,
1917
1917
  style: style$c,
@@ -40232,7 +40232,368 @@ function $upload(request) {
40232
40232
  };
40233
40233
  }
40234
40234
 
40235
+ class FrontMatterEditor {
40236
+ #container;
40237
+ #fields = [];
40238
+ #hasFrontMatter;
40239
+ #isCollapsed = false;
40240
+ #onUpdated;
40241
+ #originalFrontMatter;
40242
+ constructor(options) {
40243
+ this.#container = options.container;
40244
+ this.#onUpdated = options.onUpdated;
40245
+ this.#hasFrontMatter = options.hasFrontMatter;
40246
+ this.#fields = this.#parseInitialData(options.initialData);
40247
+ if (options.hasFrontMatter) {
40248
+ this.#originalFrontMatter = JSON.stringify(options.initialData);
40249
+ }
40250
+ this.#render();
40251
+ }
40252
+ /**
40253
+ * Get current Front Matter data
40254
+ */
40255
+ getData() {
40256
+ const data = {};
40257
+ for (const field of this.#fields) {
40258
+ data[field.key] = field.value;
40259
+ }
40260
+ return data;
40261
+ }
40262
+ /**
40263
+ * Get original Front Matter string for format preservation
40264
+ */
40265
+ getOriginalFrontMatter() {
40266
+ return this.#originalFrontMatter;
40267
+ }
40268
+ /**
40269
+ * Check if Front Matter has been modified
40270
+ */
40271
+ hasChanges() {
40272
+ if (!this.#hasFrontMatter && this.#fields.length === 0) {
40273
+ return false;
40274
+ }
40275
+ const currentData = JSON.stringify(this.getData());
40276
+ return currentData !== this.#originalFrontMatter;
40277
+ }
40278
+ /**
40279
+ * Add a new field
40280
+ * @param key
40281
+ * @param type
40282
+ */
40283
+ #addField(key, type) {
40284
+ const defaultValue = this.#getDefaultValue(type);
40285
+ this.#fields.push({ key, type, value: defaultValue });
40286
+ this.#hasFrontMatter = true;
40287
+ this.#render();
40288
+ this.#notifyUpdate();
40289
+ }
40290
+ /**
40291
+ * Create a field element
40292
+ * @param field
40293
+ * @param index
40294
+ */
40295
+ #createFieldElement(field, index) {
40296
+ const fieldEl = document.createElement("div");
40297
+ fieldEl.className = "fm-editor-field";
40298
+ fieldEl.dataset.type = field.type;
40299
+ const labelEl = document.createElement("label");
40300
+ labelEl.className = "fm-editor-field-label";
40301
+ labelEl.textContent = field.key;
40302
+ fieldEl.append(labelEl);
40303
+ const inputWrapper = document.createElement("div");
40304
+ inputWrapper.className = "fm-editor-field-input";
40305
+ const inputEl = this.#createInputElement(field, index);
40306
+ inputWrapper.append(inputEl);
40307
+ fieldEl.append(inputWrapper);
40308
+ const deleteBtn = document.createElement("button");
40309
+ deleteBtn.type = "button";
40310
+ deleteBtn.className = "fm-editor-field-delete";
40311
+ deleteBtn.title = "フィールドを削除";
40312
+ deleteBtn.textContent = "×";
40313
+ deleteBtn.addEventListener("click", () => {
40314
+ this.#deleteField(index);
40315
+ });
40316
+ fieldEl.append(deleteBtn);
40317
+ return fieldEl;
40318
+ }
40319
+ /**
40320
+ * Create input element based on field type
40321
+ * @param field
40322
+ * @param index
40323
+ */
40324
+ #createInputElement(field, index) {
40325
+ switch (field.type) {
40326
+ case "boolean": {
40327
+ const checkbox = document.createElement("input");
40328
+ checkbox.type = "checkbox";
40329
+ checkbox.checked = Boolean(field.value);
40330
+ checkbox.addEventListener("change", () => {
40331
+ this.#updateFieldValue(index, checkbox.checked);
40332
+ });
40333
+ return checkbox;
40334
+ }
40335
+ case "number": {
40336
+ const input = document.createElement("input");
40337
+ input.type = "number";
40338
+ input.value = String(field.value ?? "");
40339
+ input.addEventListener("input", () => {
40340
+ const numValue = input.value === "" ? null : Number(input.value);
40341
+ this.#updateFieldValue(index, numValue);
40342
+ });
40343
+ return input;
40344
+ }
40345
+ case "date": {
40346
+ const input = document.createElement("input");
40347
+ input.type = "date";
40348
+ if (field.value) {
40349
+ const dateValue = field.value instanceof Date ? field.value : new Date(String(field.value));
40350
+ if (!Number.isNaN(dateValue.getTime())) {
40351
+ const isoDate = dateValue.toISOString().split("T")[0] ?? "";
40352
+ input.value = isoDate;
40353
+ }
40354
+ }
40355
+ input.addEventListener("input", () => {
40356
+ this.#updateFieldValue(index, input.value);
40357
+ });
40358
+ return input;
40359
+ }
40360
+ case "json": {
40361
+ const textarea = document.createElement("textarea");
40362
+ textarea.rows = 3;
40363
+ try {
40364
+ textarea.value = JSON.stringify(field.value, null, 2);
40365
+ } catch {
40366
+ textarea.value = String(field.value);
40367
+ }
40368
+ textarea.addEventListener("input", () => {
40369
+ try {
40370
+ const parsed = JSON.parse(textarea.value);
40371
+ textarea.classList.remove("fm-editor-error");
40372
+ this.#updateFieldValue(index, parsed);
40373
+ } catch {
40374
+ textarea.classList.add("fm-editor-error");
40375
+ }
40376
+ });
40377
+ return textarea;
40378
+ }
40379
+ default: {
40380
+ const input = document.createElement("input");
40381
+ input.type = "text";
40382
+ input.value = String(field.value ?? "");
40383
+ input.addEventListener("input", () => {
40384
+ this.#updateFieldValue(index, input.value);
40385
+ });
40386
+ return input;
40387
+ }
40388
+ }
40389
+ }
40390
+ /**
40391
+ * Delete a field
40392
+ * @param index
40393
+ */
40394
+ #deleteField(index) {
40395
+ this.#fields = this.#fields.filter((_, i) => i !== index);
40396
+ this.#render();
40397
+ this.#notifyUpdate();
40398
+ }
40399
+ /**
40400
+ * Detect the type of a value
40401
+ * @param value
40402
+ */
40403
+ #detectType(value) {
40404
+ if (typeof value === "boolean") {
40405
+ return "boolean";
40406
+ }
40407
+ if (typeof value === "number") {
40408
+ return "number";
40409
+ }
40410
+ if (typeof value === "string") {
40411
+ if (/^\d{4}-\d{2}-\d{2}(?:T\d{2}:\d{2}:\d{2})?/.test(value)) {
40412
+ const parsed = Date.parse(value);
40413
+ if (!Number.isNaN(parsed)) {
40414
+ return "date";
40415
+ }
40416
+ }
40417
+ return "text";
40418
+ }
40419
+ if (value instanceof Date) {
40420
+ return "date";
40421
+ }
40422
+ if (typeof value === "object" && value !== null) {
40423
+ return "json";
40424
+ }
40425
+ return "text";
40426
+ }
40427
+ /**
40428
+ * Get default value for a type
40429
+ * @param type
40430
+ */
40431
+ #getDefaultValue(type) {
40432
+ switch (type) {
40433
+ case "boolean": {
40434
+ return false;
40435
+ }
40436
+ case "number": {
40437
+ return 0;
40438
+ }
40439
+ case "date": {
40440
+ return (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
40441
+ }
40442
+ case "json": {
40443
+ return [];
40444
+ }
40445
+ default: {
40446
+ return "";
40447
+ }
40448
+ }
40449
+ }
40450
+ /**
40451
+ * Notify update callback
40452
+ */
40453
+ #notifyUpdate() {
40454
+ if (this.#onUpdated) {
40455
+ this.#onUpdated(this.getData());
40456
+ }
40457
+ }
40458
+ /**
40459
+ * Parse initial data and detect field types
40460
+ * @param data
40461
+ */
40462
+ #parseInitialData(data) {
40463
+ const fields = [];
40464
+ for (const [key, value] of Object.entries(data)) {
40465
+ fields.push({
40466
+ key,
40467
+ type: this.#detectType(value),
40468
+ value
40469
+ });
40470
+ }
40471
+ return fields;
40472
+ }
40473
+ /**
40474
+ * Render the editor UI
40475
+ */
40476
+ #render() {
40477
+ this.#container.innerHTML = "";
40478
+ this.#container.classList.add("fm-editor");
40479
+ const header = document.createElement("div");
40480
+ header.className = "fm-editor-header";
40481
+ header.innerHTML = `
40482
+ <button type="button" class="fm-editor-toggle" aria-expanded="${!this.#isCollapsed}">
40483
+ <span class="fm-editor-toggle-icon">${this.#isCollapsed ? "▶" : "▼"}</span>
40484
+ <span>Front Matter</span>
40485
+ </button>
40486
+ <button type="button" class="fm-editor-add" title="フィールドを追加">+ 追加</button>
40487
+ `;
40488
+ this.#container.append(header);
40489
+ const toggleBtn = header.querySelector(".fm-editor-toggle");
40490
+ toggleBtn?.addEventListener("click", () => {
40491
+ this.#isCollapsed = !this.#isCollapsed;
40492
+ this.#render();
40493
+ });
40494
+ const addBtn = header.querySelector(".fm-editor-add");
40495
+ addBtn?.addEventListener("click", () => {
40496
+ this.#showAddFieldDialog();
40497
+ });
40498
+ if (!this.#isCollapsed) {
40499
+ const fieldsContainer = document.createElement("div");
40500
+ fieldsContainer.className = "fm-editor-fields";
40501
+ if (this.#fields.length === 0) {
40502
+ const emptyMessage = document.createElement("div");
40503
+ emptyMessage.className = "fm-editor-empty";
40504
+ emptyMessage.textContent = "フィールドがありません。「+ 追加」ボタンでフィールドを追加してください。";
40505
+ fieldsContainer.append(emptyMessage);
40506
+ } else {
40507
+ for (const [index, field] of this.#fields.entries()) {
40508
+ const fieldEl = this.#createFieldElement(field, index);
40509
+ fieldsContainer.append(fieldEl);
40510
+ }
40511
+ }
40512
+ this.#container.append(fieldsContainer);
40513
+ }
40514
+ }
40515
+ /**
40516
+ * Show dialog to add a new field
40517
+ */
40518
+ #showAddFieldDialog() {
40519
+ const dialog = document.createElement("dialog");
40520
+ dialog.className = "fm-editor-dialog";
40521
+ dialog.innerHTML = `
40522
+ <form method="dialog">
40523
+ <h3>フィールドを追加</h3>
40524
+ <div class="fm-editor-dialog-field">
40525
+ <label for="fm-new-key">キー名</label>
40526
+ <input type="text" id="fm-new-key" name="key" required placeholder="例: title, author, date" />
40527
+ </div>
40528
+ <div class="fm-editor-dialog-field">
40529
+ <label for="fm-new-type">型</label>
40530
+ <select id="fm-new-type" name="type">
40531
+ <option value="text">テキスト</option>
40532
+ <option value="number">数値</option>
40533
+ <option value="boolean">真偽値</option>
40534
+ <option value="date">日付</option>
40535
+ <option value="json">JSON(配列/オブジェクト)</option>
40536
+ </select>
40537
+ </div>
40538
+ <div class="fm-editor-dialog-actions">
40539
+ <button type="button" class="fm-editor-dialog-cancel">キャンセル</button>
40540
+ <button type="submit" class="fm-editor-dialog-submit">追加</button>
40541
+ </div>
40542
+ </form>
40543
+ `;
40544
+ const cancelBtn = dialog.querySelector(".fm-editor-dialog-cancel");
40545
+ cancelBtn?.addEventListener("click", () => {
40546
+ dialog.close();
40547
+ dialog.remove();
40548
+ });
40549
+ const form = dialog.querySelector("form");
40550
+ form?.addEventListener("submit", (e) => {
40551
+ e.preventDefault();
40552
+ const formData = new FormData(form);
40553
+ const key = formData.get("key");
40554
+ const type = formData.get("type");
40555
+ if (key && !this.#fields.some((f) => f.key === key)) {
40556
+ this.#addField(key, type);
40557
+ }
40558
+ dialog.close();
40559
+ dialog.remove();
40560
+ });
40561
+ document.body.append(dialog);
40562
+ dialog.showModal();
40563
+ const keyInput = dialog.querySelector("#fm-new-key");
40564
+ keyInput?.focus();
40565
+ }
40566
+ /**
40567
+ * Update field value
40568
+ * @param index
40569
+ * @param value
40570
+ */
40571
+ #updateFieldValue(index, value) {
40572
+ const field = this.#fields[index];
40573
+ if (field) {
40574
+ this.#fields[index] = { ...field, value };
40575
+ this.#notifyUpdate();
40576
+ }
40577
+ }
40578
+ }
40579
+ function createFrontMatterEditor(options) {
40580
+ return new FrontMatterEditor(options);
40581
+ }
40582
+
40235
40583
  const client = hc(location.origin);
40584
+ const FRONT_MATTER_SAVE_DEBOUNCE_DELAY = 500;
40585
+ function debounce(fn, delay) {
40586
+ let timeoutId = null;
40587
+ return (...args) => {
40588
+ if (timeoutId !== null) {
40589
+ clearTimeout(timeoutId);
40590
+ }
40591
+ timeoutId = setTimeout(() => {
40592
+ fn(...args);
40593
+ timeoutId = null;
40594
+ }, delay);
40595
+ };
40596
+ }
40236
40597
  async function createEditor() {
40237
40598
  const configRes = await client["config.json"].$get();
40238
40599
  const config = await configRes.json();
@@ -40244,6 +40605,57 @@ async function createEditor() {
40244
40605
  console.warn("Editable area not found");
40245
40606
  return;
40246
40607
  }
40608
+ const frontMatterContainer = document.querySelector(".front-matter-editor");
40609
+ const frontMatterInput = document.getElementById(
40610
+ "front-matter"
40611
+ );
40612
+ const hasFrontMatterInput = document.getElementById(
40613
+ "has-front-matter"
40614
+ );
40615
+ let frontMatterEditor = null;
40616
+ async function saveContent(content, frontMatterData, originalFrontMatter) {
40617
+ const res = await client.api.content.$post({
40618
+ json: {
40619
+ path: location.pathname,
40620
+ content,
40621
+ frontMatter: frontMatterData,
40622
+ originalFrontMatter
40623
+ }
40624
+ });
40625
+ const json = await res.json();
40626
+ if (!json.saved) {
40627
+ console.error(`Failed to save: ${json.path}`);
40628
+ return;
40629
+ }
40630
+ console.log(
40631
+ `Saved: ${json.path}${json.hasFrontMatter ? " (with Front Matter)" : ""}`
40632
+ );
40633
+ }
40634
+ const debouncedSaveFrontMatter = debounce(() => {
40635
+ if (!frontMatterEditor) {
40636
+ return;
40637
+ }
40638
+ const content = mainInput.value;
40639
+ const frontMatterData = frontMatterEditor.getData();
40640
+ const originalFrontMatter = frontMatterEditor.getOriginalFrontMatter();
40641
+ void saveContent(content, frontMatterData, originalFrontMatter);
40642
+ }, FRONT_MATTER_SAVE_DEBOUNCE_DELAY);
40643
+ if (frontMatterContainer && frontMatterInput) {
40644
+ const initialData = JSON.parse(frontMatterInput.value || "{}");
40645
+ const hasFrontMatter = hasFrontMatterInput?.value === "true";
40646
+ frontMatterEditor = createFrontMatterEditor({
40647
+ container: frontMatterContainer,
40648
+ initialData,
40649
+ hasFrontMatter,
40650
+ onUpdated: debouncedSaveFrontMatter
40651
+ });
40652
+ {
40653
+ console.log("Front Matter editor initialized:", {
40654
+ initialData,
40655
+ hasFrontMatter
40656
+ });
40657
+ }
40658
+ }
40247
40659
  const catalog = config.enableImportBlock ? {
40248
40660
  ...config.catalog,
40249
40661
  import: [
@@ -40293,18 +40705,9 @@ async function createEditor() {
40293
40705
  return;
40294
40706
  }
40295
40707
  mainInput.value = content;
40296
- const res = await client.api.content.$post({
40297
- json: {
40298
- path: location.pathname,
40299
- content
40300
- }
40301
- });
40302
- const json = await res.json();
40303
- if (!json.saved) {
40304
- console.error(`Failed to save: ${json.path}`);
40305
- return;
40306
- }
40307
- console.log(`Saved: ${json.path}`);
40708
+ const frontMatterData = frontMatterEditor?.getData();
40709
+ const originalFrontMatter = frontMatterEditor?.getOriginalFrontMatter();
40710
+ await saveContent(content, frontMatterData, originalFrontMatter);
40308
40711
  },
40309
40712
  fileIO: {
40310
40713
  async getFileList(fileType, options) {