veryfront 0.1.27 → 0.1.28

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/esm/deno.js CHANGED
@@ -1,6 +1,6 @@
1
1
  export default {
2
2
  "name": "veryfront",
3
- "version": "0.1.27",
3
+ "version": "0.1.28",
4
4
  "license": "Apache-2.0",
5
5
  "nodeModulesDir": "auto",
6
6
  "exclude": [
@@ -1 +1 @@
1
- {"version":3,"file":"bridge-template.d.ts","sourceRoot":"","sources":["../../../src/src/studio/bridge-template.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,mBAAmB;IAClC,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,oBAAoB,CAAC,EAAE,OAAO,CAAC;CAChC;AAED,wBAAgB,0BAA0B,CAAC,OAAO,EAAE,mBAAmB,GAAG,MAAM,CAs+H/E"}
1
+ {"version":3,"file":"bridge-template.d.ts","sourceRoot":"","sources":["../../../src/src/studio/bridge-template.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,mBAAmB;IAClC,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,oBAAoB,CAAC,EAAE,OAAO,CAAC;CAChC;AAED,wBAAgB,0BAA0B,CAAC,OAAO,EAAE,mBAAmB,GAAG,MAAM,CAy/H/E"}
@@ -68,6 +68,7 @@ export function generateStudioBridgeScript(options) {
68
68
  let markdownLatestMdxImportMap = {};
69
69
  let markdownLatestPresenceUsers = [];
70
70
  let markdownLatestSelections = [];
71
+ let markdownHasUnsavedChanges = false;
71
72
 
72
73
  const MARKDOWN_SLASH_COMMANDS = [
73
74
  {
@@ -319,17 +320,15 @@ export function generateStudioBridgeScript(options) {
319
320
 
320
321
  .vf-markdown-editor__surface {
321
322
  width: 100%;
323
+ max-width: 980px;
324
+ margin: 0 auto;
322
325
  height: 100%;
323
326
  overflow: auto;
324
327
  outline: none;
325
328
  position: relative;
326
329
  z-index: 1;
327
- font-family: ui-sans-serif, system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;
328
- font-size: 16px;
329
- line-height: 1.6;
330
- color: #111827;
331
330
  background: transparent;
332
- padding: 16px;
331
+ padding: 32px 40px;
333
332
  box-sizing: border-box;
334
333
  }
335
334
 
@@ -563,28 +562,17 @@ export function generateStudioBridgeScript(options) {
563
562
  white-space: nowrap;
564
563
  }
565
564
 
566
- .vf-markdown-editor__surface p {
567
- margin: 0 0 12px;
568
- }
569
-
570
- .vf-markdown-editor__surface h1,
571
- .vf-markdown-editor__surface h2,
572
- .vf-markdown-editor__surface h3,
573
- .vf-markdown-editor__surface h4,
574
- .vf-markdown-editor__surface h5,
575
- .vf-markdown-editor__surface h6 {
576
- margin: 20px 0 12px;
577
- line-height: 1.35;
565
+ .vf-markdown-editor__surface [data-lexical-editor] {
566
+ outline: none;
578
567
  }
579
568
 
580
- .vf-markdown-editor__surface ul,
581
- .vf-markdown-editor__surface ol {
582
- margin: 0 0 12px 24px;
583
- padding: 0;
569
+ .vf-markdown-editor__surface p:empty::before {
570
+ content: '';
571
+ display: inline-block;
584
572
  }
585
573
 
586
- .vf-markdown-editor__surface code {
587
- font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, 'Liberation Mono', 'Courier New', monospace;
574
+ .vf-markdown-editor__surface p {
575
+ min-height: 1.5em;
588
576
  }
589
577
 
590
578
  .vf-markdown-editor__textarea {
@@ -660,10 +648,6 @@ export function generateStudioBridgeScript(options) {
660
648
  background: #111827;
661
649
  }
662
650
 
663
- [data-theme='dark'] .vf-markdown-editor__surface {
664
- color: #f9fafb;
665
- }
666
-
667
651
  [data-theme='dark'] .vf-markdown-editor__textarea {
668
652
  color: #f9fafb;
669
653
  }
@@ -831,13 +815,18 @@ export function generateStudioBridgeScript(options) {
831
815
  }
832
816
 
833
817
  function isFromStudio(event) {
834
- const origin = event.origin || '';
835
- return (
836
- origin.includes('veryfront.org') ||
837
- origin.includes('veryfront.com') ||
838
- origin.includes('veryfront.dev') ||
839
- origin.includes('localhost')
840
- );
818
+ try {
819
+ const url = new URL(event.origin || '');
820
+ const host = url.hostname;
821
+ return (
822
+ host === 'localhost' ||
823
+ host.endsWith('.veryfront.org') || host === 'veryfront.org' ||
824
+ host.endsWith('.veryfront.com') || host === 'veryfront.com' ||
825
+ host.endsWith('.veryfront.dev') || host === 'veryfront.dev'
826
+ );
827
+ } catch (e) {
828
+ return false;
829
+ }
841
830
  }
842
831
 
843
832
  const originalConsole = {};
@@ -1732,7 +1721,7 @@ export function generateStudioBridgeScript(options) {
1732
1721
 
1733
1722
  applyMarkdownContent(nextFullContent);
1734
1723
  if (hasChanged) {
1735
- setMarkdownPersistStatus('saving');
1724
+ markdownHasUnsavedChanges = true;
1736
1725
  scheduleMarkdownSync(nextFullContent);
1737
1726
  }
1738
1727
 
@@ -2918,11 +2907,30 @@ export function generateStudioBridgeScript(options) {
2918
2907
  return;
2919
2908
  }
2920
2909
  markdownCurrentContent = fullContent;
2921
- setMarkdownPersistStatus('saving');
2910
+ markdownHasUnsavedChanges = true;
2922
2911
  scheduleMarkdownSync(fullContent);
2923
2912
  scheduleMarkdownSelectionOverlayRender();
2924
2913
  }
2925
2914
 
2915
+ function saveMarkdownContent() {
2916
+ if (!markdownHasUnsavedChanges) {
2917
+ return;
2918
+ }
2919
+ setMarkdownPersistStatus('saving');
2920
+ if (markdownSyncTimer) {
2921
+ clearTimeout(markdownSyncTimer);
2922
+ markdownSyncTimer = null;
2923
+ }
2924
+ postToStudio({
2925
+ action: 'markdownContentChange',
2926
+ fileId: markdownFileId,
2927
+ filePath: PAGE_PATH,
2928
+ content: markdownCurrentContent,
2929
+ save: true
2930
+ });
2931
+ markdownHasUnsavedChanges = false;
2932
+ }
2933
+
2926
2934
  function setupMarkdownLexicalEditor() {
2927
2935
  if (!markdownEditorSurface || markdownLexicalApi || markdownLexicalSetupPromise) {
2928
2936
  return;
@@ -2972,10 +2980,15 @@ export function generateStudioBridgeScript(options) {
2972
2980
 
2973
2981
  let nextContent = '';
2974
2982
  update.editorState.read(function() {
2975
- nextContent = markdownModule.$convertToMarkdownString(markdownModule.TRANSFORMERS);
2983
+ nextContent = markdownModule.$convertToMarkdownString(markdownModule.TRANSFORMERS, true);
2976
2984
  });
2977
2985
  const restoredBody = restoreRawBlocksFromEditor(nextContent);
2978
- markdownLexicalRenderedContent = composeMarkdownContent(restoredBody);
2986
+ const fullContent = composeMarkdownContent(restoredBody);
2987
+
2988
+ if (fullContent === markdownLexicalRenderedContent) {
2989
+ return;
2990
+ }
2991
+ markdownLexicalRenderedContent = fullContent;
2979
2992
 
2980
2993
  handleMarkdownLocalChange(nextContent);
2981
2994
  scheduleMarkdownSlashMenuUpdate();
@@ -3055,6 +3068,15 @@ export function generateStudioBridgeScript(options) {
3055
3068
  return;
3056
3069
  }
3057
3070
 
3071
+ if (markdownLexicalApi && markdownLexicalRenderedContent === content) {
3072
+ markdownCurrentContent = content;
3073
+ scheduleMarkdownSelectionOverlayRender();
3074
+ scheduleMarkdownSlashMenuUpdate();
3075
+ scheduleMarkdownInlineToolbarUpdate();
3076
+ hideMarkdownBlockDropIndicator();
3077
+ return;
3078
+ }
3079
+
3058
3080
  const mdxImportMap = parseMdxImportMap(content);
3059
3081
  const parts = extractMarkdownParts(content);
3060
3082
  const extracted = extractRawBlocksForEditor(parts.body, mdxImportMap);
@@ -3066,35 +3088,26 @@ export function generateStudioBridgeScript(options) {
3066
3088
  markdownLatestMdxImportMap = mdxImportMap;
3067
3089
  setMarkdownMdxBlocks(mdxBlocks);
3068
3090
 
3069
- if (markdownLexicalApi && markdownLexicalRenderedContent === content) {
3070
- markdownCurrentContent = content;
3071
- markdownCurrentEditorContent = editorContent;
3072
- scheduleMarkdownSelectionOverlayRender();
3073
- scheduleMarkdownSlashMenuUpdate();
3074
- scheduleMarkdownInlineToolbarUpdate();
3075
- hideMarkdownBlockDropIndicator();
3076
- return;
3077
- }
3078
-
3079
3091
  markdownCurrentContent = content;
3080
3092
  markdownCurrentEditorContent = editorContent;
3081
3093
 
3082
3094
  if (markdownLexicalApi) {
3083
3095
  markdownApplyingRemoteUpdate = true;
3084
- markdownLexicalRenderedContent = content;
3085
- markdownLexicalApi.editor.update(function() {
3086
- const lexicalModule = markdownLexicalApi.lexicalModule;
3087
- const markdownModule = markdownLexicalApi.markdownModule;
3088
- const root = lexicalModule.$getRoot();
3089
- root.clear();
3090
- markdownModule.$convertFromMarkdownString(editorContent, markdownModule.TRANSFORMERS);
3091
- if (root.getChildrenSize() === 0) {
3092
- root.append(lexicalModule.$createParagraphNode());
3093
- }
3094
- });
3095
- setTimeout(function() {
3096
+ try {
3097
+ markdownLexicalRenderedContent = content;
3098
+ markdownLexicalApi.editor.update(function() {
3099
+ const lexicalModule = markdownLexicalApi.lexicalModule;
3100
+ const markdownModule = markdownLexicalApi.markdownModule;
3101
+ const root = lexicalModule.$getRoot();
3102
+ root.clear();
3103
+ markdownModule.$convertFromMarkdownString(editorContent, markdownModule.TRANSFORMERS, true);
3104
+ if (root.getChildrenSize() === 0) {
3105
+ root.append(lexicalModule.$createParagraphNode());
3106
+ }
3107
+ }, { discrete: true });
3108
+ } finally {
3096
3109
  markdownApplyingRemoteUpdate = false;
3097
- }, 0);
3110
+ }
3098
3111
  scheduleMarkdownSelectionOverlayRender();
3099
3112
  scheduleMarkdownSlashMenuUpdate();
3100
3113
  scheduleMarkdownInlineToolbarUpdate();
@@ -3297,8 +3310,8 @@ export function generateStudioBridgeScript(options) {
3297
3310
  const status = document.createElement('div');
3298
3311
  status.className = 'vf-markdown-editor__status';
3299
3312
  status.setAttribute(DATA_VF_IGNORE, 'true');
3300
- status.textContent = 'Saved';
3301
- status.setAttribute('data-state', 'saved');
3313
+ status.textContent = '';
3314
+ status.setAttribute('data-state', '');
3302
3315
 
3303
3316
  const presence = document.createElement('div');
3304
3317
  presence.className = 'vf-markdown-editor__presence';
@@ -3369,7 +3382,7 @@ export function generateStudioBridgeScript(options) {
3369
3382
  mdxBlocks.setAttribute(DATA_VF_IGNORE, 'true');
3370
3383
 
3371
3384
  const surface = document.createElement('div');
3372
- surface.className = 'vf-markdown-editor__surface';
3385
+ surface.className = 'vf-markdown-editor__surface markdown-body';
3373
3386
  surface.setAttribute(DATA_VF_IGNORE, 'true');
3374
3387
  surface.setAttribute('contenteditable', 'true');
3375
3388
  surface.setAttribute('aria-label', 'Markdown editor');
@@ -3400,6 +3413,12 @@ export function generateStudioBridgeScript(options) {
3400
3413
  }
3401
3414
  }
3402
3415
  });
3416
+ surface.addEventListener('keydown', function(event) {
3417
+ if ((event.metaKey || event.ctrlKey) && event.key === 's') {
3418
+ event.preventDefault();
3419
+ saveMarkdownContent();
3420
+ }
3421
+ });
3403
3422
  surface.addEventListener('scroll', scheduleMarkdownSelectionOverlayRender);
3404
3423
  surface.addEventListener('scroll', scheduleMarkdownSlashMenuUpdate);
3405
3424
  surface.addEventListener('keyup', scheduleMarkdownInlineToolbarUpdate);
@@ -3669,7 +3688,7 @@ export function generateStudioBridgeScript(options) {
3669
3688
  setupMarkdownLexicalEditor();
3670
3689
  markdownBody.style.display = 'none';
3671
3690
  markdownEditorRoot.style.display = 'block';
3672
- setMarkdownPersistStatus('saved');
3691
+ markdownHasUnsavedChanges = false;
3673
3692
  focusMarkdownEditor();
3674
3693
  scheduleMarkdownSelectionSync();
3675
3694
  scheduleMarkdownSelectionOverlayRender();
@@ -3910,9 +3929,6 @@ export function generateStudioBridgeScript(options) {
3910
3929
  if (!inspectMode) showHoverOverlay(message.id);
3911
3930
  return;
3912
3931
 
3913
- case 'toggleLayout':
3914
- return;
3915
-
3916
3932
  case 'setMarkdownContent':
3917
3933
  if (!isMarkdownPage()) {
3918
3934
  return;
@@ -3931,6 +3947,9 @@ export function generateStudioBridgeScript(options) {
3931
3947
  return;
3932
3948
  }
3933
3949
  setMarkdownPersistStatus(message.status || 'saved');
3950
+ if (message.status === 'saved') {
3951
+ markdownHasUnsavedChanges = false;
3952
+ }
3934
3953
  return;
3935
3954
 
3936
3955
  case 'setMarkdownPresence':
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "veryfront",
3
- "version": "0.1.27",
3
+ "version": "0.1.28",
4
4
  "description": "The simplest way to build AI-powered apps",
5
5
  "keywords": [
6
6
  "react",
package/src/deno.js CHANGED
@@ -1,6 +1,6 @@
1
1
  export default {
2
2
  "name": "veryfront",
3
- "version": "0.1.27",
3
+ "version": "0.1.28",
4
4
  "license": "Apache-2.0",
5
5
  "nodeModulesDir": "auto",
6
6
  "exclude": [
@@ -76,6 +76,7 @@ export function generateStudioBridgeScript(options: StudioBridgeOptions): string
76
76
  let markdownLatestMdxImportMap = {};
77
77
  let markdownLatestPresenceUsers = [];
78
78
  let markdownLatestSelections = [];
79
+ let markdownHasUnsavedChanges = false;
79
80
 
80
81
  const MARKDOWN_SLASH_COMMANDS = [
81
82
  {
@@ -327,17 +328,15 @@ export function generateStudioBridgeScript(options: StudioBridgeOptions): string
327
328
 
328
329
  .vf-markdown-editor__surface {
329
330
  width: 100%;
331
+ max-width: 980px;
332
+ margin: 0 auto;
330
333
  height: 100%;
331
334
  overflow: auto;
332
335
  outline: none;
333
336
  position: relative;
334
337
  z-index: 1;
335
- font-family: ui-sans-serif, system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;
336
- font-size: 16px;
337
- line-height: 1.6;
338
- color: #111827;
339
338
  background: transparent;
340
- padding: 16px;
339
+ padding: 32px 40px;
341
340
  box-sizing: border-box;
342
341
  }
343
342
 
@@ -571,28 +570,17 @@ export function generateStudioBridgeScript(options: StudioBridgeOptions): string
571
570
  white-space: nowrap;
572
571
  }
573
572
 
574
- .vf-markdown-editor__surface p {
575
- margin: 0 0 12px;
576
- }
577
-
578
- .vf-markdown-editor__surface h1,
579
- .vf-markdown-editor__surface h2,
580
- .vf-markdown-editor__surface h3,
581
- .vf-markdown-editor__surface h4,
582
- .vf-markdown-editor__surface h5,
583
- .vf-markdown-editor__surface h6 {
584
- margin: 20px 0 12px;
585
- line-height: 1.35;
573
+ .vf-markdown-editor__surface [data-lexical-editor] {
574
+ outline: none;
586
575
  }
587
576
 
588
- .vf-markdown-editor__surface ul,
589
- .vf-markdown-editor__surface ol {
590
- margin: 0 0 12px 24px;
591
- padding: 0;
577
+ .vf-markdown-editor__surface p:empty::before {
578
+ content: '';
579
+ display: inline-block;
592
580
  }
593
581
 
594
- .vf-markdown-editor__surface code {
595
- font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, 'Liberation Mono', 'Courier New', monospace;
582
+ .vf-markdown-editor__surface p {
583
+ min-height: 1.5em;
596
584
  }
597
585
 
598
586
  .vf-markdown-editor__textarea {
@@ -668,10 +656,6 @@ export function generateStudioBridgeScript(options: StudioBridgeOptions): string
668
656
  background: #111827;
669
657
  }
670
658
 
671
- [data-theme='dark'] .vf-markdown-editor__surface {
672
- color: #f9fafb;
673
- }
674
-
675
659
  [data-theme='dark'] .vf-markdown-editor__textarea {
676
660
  color: #f9fafb;
677
661
  }
@@ -839,13 +823,18 @@ export function generateStudioBridgeScript(options: StudioBridgeOptions): string
839
823
  }
840
824
 
841
825
  function isFromStudio(event) {
842
- const origin = event.origin || '';
843
- return (
844
- origin.includes('veryfront.org') ||
845
- origin.includes('veryfront.com') ||
846
- origin.includes('veryfront.dev') ||
847
- origin.includes('localhost')
848
- );
826
+ try {
827
+ const url = new URL(event.origin || '');
828
+ const host = url.hostname;
829
+ return (
830
+ host === 'localhost' ||
831
+ host.endsWith('.veryfront.org') || host === 'veryfront.org' ||
832
+ host.endsWith('.veryfront.com') || host === 'veryfront.com' ||
833
+ host.endsWith('.veryfront.dev') || host === 'veryfront.dev'
834
+ );
835
+ } catch (e) {
836
+ return false;
837
+ }
849
838
  }
850
839
 
851
840
  const originalConsole = {};
@@ -1740,7 +1729,7 @@ export function generateStudioBridgeScript(options: StudioBridgeOptions): string
1740
1729
 
1741
1730
  applyMarkdownContent(nextFullContent);
1742
1731
  if (hasChanged) {
1743
- setMarkdownPersistStatus('saving');
1732
+ markdownHasUnsavedChanges = true;
1744
1733
  scheduleMarkdownSync(nextFullContent);
1745
1734
  }
1746
1735
 
@@ -2926,11 +2915,30 @@ export function generateStudioBridgeScript(options: StudioBridgeOptions): string
2926
2915
  return;
2927
2916
  }
2928
2917
  markdownCurrentContent = fullContent;
2929
- setMarkdownPersistStatus('saving');
2918
+ markdownHasUnsavedChanges = true;
2930
2919
  scheduleMarkdownSync(fullContent);
2931
2920
  scheduleMarkdownSelectionOverlayRender();
2932
2921
  }
2933
2922
 
2923
+ function saveMarkdownContent() {
2924
+ if (!markdownHasUnsavedChanges) {
2925
+ return;
2926
+ }
2927
+ setMarkdownPersistStatus('saving');
2928
+ if (markdownSyncTimer) {
2929
+ clearTimeout(markdownSyncTimer);
2930
+ markdownSyncTimer = null;
2931
+ }
2932
+ postToStudio({
2933
+ action: 'markdownContentChange',
2934
+ fileId: markdownFileId,
2935
+ filePath: PAGE_PATH,
2936
+ content: markdownCurrentContent,
2937
+ save: true
2938
+ });
2939
+ markdownHasUnsavedChanges = false;
2940
+ }
2941
+
2934
2942
  function setupMarkdownLexicalEditor() {
2935
2943
  if (!markdownEditorSurface || markdownLexicalApi || markdownLexicalSetupPromise) {
2936
2944
  return;
@@ -2980,10 +2988,15 @@ export function generateStudioBridgeScript(options: StudioBridgeOptions): string
2980
2988
 
2981
2989
  let nextContent = '';
2982
2990
  update.editorState.read(function() {
2983
- nextContent = markdownModule.$convertToMarkdownString(markdownModule.TRANSFORMERS);
2991
+ nextContent = markdownModule.$convertToMarkdownString(markdownModule.TRANSFORMERS, true);
2984
2992
  });
2985
2993
  const restoredBody = restoreRawBlocksFromEditor(nextContent);
2986
- markdownLexicalRenderedContent = composeMarkdownContent(restoredBody);
2994
+ const fullContent = composeMarkdownContent(restoredBody);
2995
+
2996
+ if (fullContent === markdownLexicalRenderedContent) {
2997
+ return;
2998
+ }
2999
+ markdownLexicalRenderedContent = fullContent;
2987
3000
 
2988
3001
  handleMarkdownLocalChange(nextContent);
2989
3002
  scheduleMarkdownSlashMenuUpdate();
@@ -3063,6 +3076,15 @@ export function generateStudioBridgeScript(options: StudioBridgeOptions): string
3063
3076
  return;
3064
3077
  }
3065
3078
 
3079
+ if (markdownLexicalApi && markdownLexicalRenderedContent === content) {
3080
+ markdownCurrentContent = content;
3081
+ scheduleMarkdownSelectionOverlayRender();
3082
+ scheduleMarkdownSlashMenuUpdate();
3083
+ scheduleMarkdownInlineToolbarUpdate();
3084
+ hideMarkdownBlockDropIndicator();
3085
+ return;
3086
+ }
3087
+
3066
3088
  const mdxImportMap = parseMdxImportMap(content);
3067
3089
  const parts = extractMarkdownParts(content);
3068
3090
  const extracted = extractRawBlocksForEditor(parts.body, mdxImportMap);
@@ -3074,35 +3096,26 @@ export function generateStudioBridgeScript(options: StudioBridgeOptions): string
3074
3096
  markdownLatestMdxImportMap = mdxImportMap;
3075
3097
  setMarkdownMdxBlocks(mdxBlocks);
3076
3098
 
3077
- if (markdownLexicalApi && markdownLexicalRenderedContent === content) {
3078
- markdownCurrentContent = content;
3079
- markdownCurrentEditorContent = editorContent;
3080
- scheduleMarkdownSelectionOverlayRender();
3081
- scheduleMarkdownSlashMenuUpdate();
3082
- scheduleMarkdownInlineToolbarUpdate();
3083
- hideMarkdownBlockDropIndicator();
3084
- return;
3085
- }
3086
-
3087
3099
  markdownCurrentContent = content;
3088
3100
  markdownCurrentEditorContent = editorContent;
3089
3101
 
3090
3102
  if (markdownLexicalApi) {
3091
3103
  markdownApplyingRemoteUpdate = true;
3092
- markdownLexicalRenderedContent = content;
3093
- markdownLexicalApi.editor.update(function() {
3094
- const lexicalModule = markdownLexicalApi.lexicalModule;
3095
- const markdownModule = markdownLexicalApi.markdownModule;
3096
- const root = lexicalModule.$getRoot();
3097
- root.clear();
3098
- markdownModule.$convertFromMarkdownString(editorContent, markdownModule.TRANSFORMERS);
3099
- if (root.getChildrenSize() === 0) {
3100
- root.append(lexicalModule.$createParagraphNode());
3101
- }
3102
- });
3103
- setTimeout(function() {
3104
+ try {
3105
+ markdownLexicalRenderedContent = content;
3106
+ markdownLexicalApi.editor.update(function() {
3107
+ const lexicalModule = markdownLexicalApi.lexicalModule;
3108
+ const markdownModule = markdownLexicalApi.markdownModule;
3109
+ const root = lexicalModule.$getRoot();
3110
+ root.clear();
3111
+ markdownModule.$convertFromMarkdownString(editorContent, markdownModule.TRANSFORMERS, true);
3112
+ if (root.getChildrenSize() === 0) {
3113
+ root.append(lexicalModule.$createParagraphNode());
3114
+ }
3115
+ }, { discrete: true });
3116
+ } finally {
3104
3117
  markdownApplyingRemoteUpdate = false;
3105
- }, 0);
3118
+ }
3106
3119
  scheduleMarkdownSelectionOverlayRender();
3107
3120
  scheduleMarkdownSlashMenuUpdate();
3108
3121
  scheduleMarkdownInlineToolbarUpdate();
@@ -3305,8 +3318,8 @@ export function generateStudioBridgeScript(options: StudioBridgeOptions): string
3305
3318
  const status = document.createElement('div');
3306
3319
  status.className = 'vf-markdown-editor__status';
3307
3320
  status.setAttribute(DATA_VF_IGNORE, 'true');
3308
- status.textContent = 'Saved';
3309
- status.setAttribute('data-state', 'saved');
3321
+ status.textContent = '';
3322
+ status.setAttribute('data-state', '');
3310
3323
 
3311
3324
  const presence = document.createElement('div');
3312
3325
  presence.className = 'vf-markdown-editor__presence';
@@ -3377,7 +3390,7 @@ export function generateStudioBridgeScript(options: StudioBridgeOptions): string
3377
3390
  mdxBlocks.setAttribute(DATA_VF_IGNORE, 'true');
3378
3391
 
3379
3392
  const surface = document.createElement('div');
3380
- surface.className = 'vf-markdown-editor__surface';
3393
+ surface.className = 'vf-markdown-editor__surface markdown-body';
3381
3394
  surface.setAttribute(DATA_VF_IGNORE, 'true');
3382
3395
  surface.setAttribute('contenteditable', 'true');
3383
3396
  surface.setAttribute('aria-label', 'Markdown editor');
@@ -3408,6 +3421,12 @@ export function generateStudioBridgeScript(options: StudioBridgeOptions): string
3408
3421
  }
3409
3422
  }
3410
3423
  });
3424
+ surface.addEventListener('keydown', function(event) {
3425
+ if ((event.metaKey || event.ctrlKey) && event.key === 's') {
3426
+ event.preventDefault();
3427
+ saveMarkdownContent();
3428
+ }
3429
+ });
3411
3430
  surface.addEventListener('scroll', scheduleMarkdownSelectionOverlayRender);
3412
3431
  surface.addEventListener('scroll', scheduleMarkdownSlashMenuUpdate);
3413
3432
  surface.addEventListener('keyup', scheduleMarkdownInlineToolbarUpdate);
@@ -3677,7 +3696,7 @@ export function generateStudioBridgeScript(options: StudioBridgeOptions): string
3677
3696
  setupMarkdownLexicalEditor();
3678
3697
  markdownBody.style.display = 'none';
3679
3698
  markdownEditorRoot.style.display = 'block';
3680
- setMarkdownPersistStatus('saved');
3699
+ markdownHasUnsavedChanges = false;
3681
3700
  focusMarkdownEditor();
3682
3701
  scheduleMarkdownSelectionSync();
3683
3702
  scheduleMarkdownSelectionOverlayRender();
@@ -3918,9 +3937,6 @@ export function generateStudioBridgeScript(options: StudioBridgeOptions): string
3918
3937
  if (!inspectMode) showHoverOverlay(message.id);
3919
3938
  return;
3920
3939
 
3921
- case 'toggleLayout':
3922
- return;
3923
-
3924
3940
  case 'setMarkdownContent':
3925
3941
  if (!isMarkdownPage()) {
3926
3942
  return;
@@ -3939,6 +3955,9 @@ export function generateStudioBridgeScript(options: StudioBridgeOptions): string
3939
3955
  return;
3940
3956
  }
3941
3957
  setMarkdownPersistStatus(message.status || 'saved');
3958
+ if (message.status === 'saved') {
3959
+ markdownHasUnsavedChanges = false;
3960
+ }
3942
3961
  return;
3943
3962
 
3944
3963
  case 'setMarkdownPresence':