@poveste/app 0.13.0 → 0.14.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (110) hide show
  1. package/dist/bundled/App.vue_vue_type_script_setup_true_lang.js +26 -34
  2. package/dist/bundled/app.css +18 -24
  3. package/dist/bundled/components/HomeView.vue_vue_type_script_setup_true_lang.js +1 -1
  4. package/dist/bundled/components/app/AppLogo.vue_vue_type_script_setup_true_lang.js +2 -2
  5. package/dist/bundled/components/app/Breadcrumb.js +1 -1
  6. package/dist/bundled/components/app/Breadcrumb.vue_vue_type_script_setup_true_lang.js +2 -2
  7. package/dist/bundled/components/app/TopBar.vue_vue_type_script_setup_true_lang.js +4 -3
  8. package/dist/bundled/components/base/BaseCheckbox.vue_vue_type_script_setup_true_lang.js +1 -1
  9. package/dist/bundled/components/base/BaseListItemLink.js +6 -3
  10. package/dist/bundled/components/base/BaseListItemLink.vue_vue_type_script_lang.js +10 -4
  11. package/dist/bundled/components/base/BaseOverflowMenu.vue_vue_type_script_setup_true_lang.js +5 -2
  12. package/dist/bundled/components/base/BaseOverflowTab.js +6 -3
  13. package/dist/bundled/components/base/BaseOverflowTab.vue_vue_type_script_lang.js +4 -0
  14. package/dist/bundled/components/base/BaseSplitPane.js +1 -1
  15. package/dist/bundled/components/base/BaseSplitPane.vue_vue_type_script_setup_true_lang.js +6 -4
  16. package/dist/bundled/components/base/BaseTab.js +6 -3
  17. package/dist/bundled/components/base/BaseTab.vue_vue_type_script_lang.js +4 -0
  18. package/dist/bundled/components/command/CommandPrompts.vue_vue_type_script_setup_true_lang.js +1 -1
  19. package/dist/bundled/components/command/CommandPromptsModal.vue_vue_type_script_setup_true_lang.js +4 -3
  20. package/dist/bundled/components/command/PromptSelect.vue_vue_type_script_setup_true_lang.js +1 -1
  21. package/dist/bundled/components/command/PromptText.vue_vue_type_script_setup_true_lang.js +1 -1
  22. package/dist/bundled/components/panel/PaneTabs.vue_vue_type_script_setup_true_lang.js +4 -4
  23. package/dist/bundled/components/panel/StatePresets.vue_vue_type_script_setup_true_lang.js +30 -20
  24. package/dist/bundled/components/panel/StoryControls.vue_vue_type_script_setup_true_lang.js +1 -1
  25. package/dist/bundled/components/panel/StoryDocs.vue_vue_type_script_setup_true_lang.js +3 -1
  26. package/dist/bundled/components/panel/StoryEvent.vue_vue_type_script_setup_true_lang.js +5 -1
  27. package/dist/bundled/components/panel/StoryEvents.vue_vue_type_script_setup_true_lang.js +2 -1
  28. package/dist/bundled/components/panel/StorySidePanel.vue_vue_type_script_setup_true_lang.js +7 -5
  29. package/dist/bundled/components/panel/StorySourceCode.js +1 -1
  30. package/dist/bundled/components/panel/StorySourceCode.vue_vue_type_script_setup_true_lang.js +6 -5
  31. package/dist/bundled/components/search/SearchPane.vue_vue_type_script_setup_true_lang.js +15 -7
  32. package/dist/bundled/components/story/GenericMountStory.vue_vue_type_script_setup_true_lang.js +2 -1
  33. package/dist/bundled/components/story/GenericRenderStory.vue_vue_type_script_setup_true_lang.js +2 -1
  34. package/dist/bundled/components/story/StoryResponsivePreview.js +1 -1
  35. package/dist/bundled/components/story/StoryResponsivePreview.vue_vue_type_script_setup_true_lang.js +17 -6
  36. package/dist/bundled/components/story/StoryVariantGrid.js +1 -1
  37. package/dist/bundled/components/story/StoryVariantGrid.vue_vue_type_script_setup_true_lang.js +15 -10
  38. package/dist/bundled/components/story/StoryVariantSingle.js +1 -1
  39. package/dist/bundled/components/story/StoryVariantSingle.vue_vue_type_script_setup_true_lang.js +21 -33
  40. package/dist/bundled/components/story/StoryVariantSingleView.vue_vue_type_script_setup_true_lang.js +1 -1
  41. package/dist/bundled/components/story/StoryView.vue_vue_type_script_setup_true_lang.js +3 -2
  42. package/dist/bundled/components/story/StoryViewer.js +1 -1
  43. package/dist/bundled/components/story/StoryViewer.vue_vue_type_script_setup_true_lang.js +3 -3
  44. package/dist/bundled/components/toolbar/ToolbarBackground.js +1 -3
  45. package/dist/bundled/components/toolbar/ToolbarBackground.vue_vue_type_script_setup_true_lang.js +2 -6
  46. package/dist/bundled/components/toolbar/ToolbarResponsiveSize.vue_vue_type_script_setup_true_lang.js +17 -16
  47. package/dist/bundled/plugin.js +4 -3
  48. package/dist/bundled/sandbox.js +2 -2
  49. package/dist/bundled/stores/folder.js +1 -1
  50. package/dist/bundled/util/commands.js +20 -9
  51. package/dist/bundled/util/docs.js +2 -1
  52. package/dist/bundled/util/grid-slots.js +1 -1
  53. package/dist/bundled/util/highlighter.js +1 -1
  54. package/dist/bundled/util/icons.generated.js +0 -1
  55. package/dist/bundled/util/keyboard.js +1 -0
  56. package/dist/bundled/util/mapping.js +6 -2
  57. package/dist/bundled/util/preview-settings.js +1 -1
  58. package/dist/style.css +14 -2
  59. package/package.json +8 -6
  60. package/src/app/App.vue +36 -29
  61. package/src/app/components/HomeView.vue +1 -1
  62. package/src/app/components/app/AppLogo.vue +6 -2
  63. package/src/app/components/app/Breadcrumb.vue +2 -1
  64. package/src/app/components/app/TopBar.vue +7 -2
  65. package/src/app/components/base/BaseCheckbox.vue +1 -1
  66. package/src/app/components/base/BaseListItemLink.vue +12 -1
  67. package/src/app/components/base/BaseOverflowMenu.vue +9 -3
  68. package/src/app/components/base/BaseOverflowTab.vue +12 -1
  69. package/src/app/components/base/BaseSplitPane.vue +9 -4
  70. package/src/app/components/base/BaseTab.vue +12 -1
  71. package/src/app/components/command/CommandPrompts.vue +10 -2
  72. package/src/app/components/command/CommandPromptsModal.vue +1 -0
  73. package/src/app/components/command/PromptSelect.vue +2 -1
  74. package/src/app/components/command/PromptText.vue +7 -3
  75. package/src/app/components/panel/PaneTabs.vue +4 -4
  76. package/src/app/components/panel/StatePresets.vue +26 -5
  77. package/src/app/components/panel/StoryControls.vue +1 -1
  78. package/src/app/components/panel/StoryDocs.vue +3 -1
  79. package/src/app/components/panel/StoryEvent.vue +7 -2
  80. package/src/app/components/panel/StoryEvents.vue +4 -1
  81. package/src/app/components/panel/StorySidePanel.vue +2 -0
  82. package/src/app/components/panel/StorySourceCode.vue +11 -8
  83. package/src/app/components/search/SearchPane.vue +14 -5
  84. package/src/app/components/story/GenericMountStory.vue +2 -1
  85. package/src/app/components/story/GenericRenderStory.vue +2 -1
  86. package/src/app/components/story/StoryResponsivePreview.vue +30 -5
  87. package/src/app/components/story/StoryVariantGrid.vue +33 -16
  88. package/src/app/components/story/StoryVariantSingle.vue +44 -52
  89. package/src/app/components/story/StoryVariantSingleView.vue +1 -1
  90. package/src/app/components/story/StoryView.vue +7 -3
  91. package/src/app/components/story/StoryViewer.vue +3 -3
  92. package/src/app/components/toolbar/ToolbarBackground.vue +1 -8
  93. package/src/app/components/toolbar/ToolbarResponsiveSize.vue +23 -3
  94. package/src/app/components/tree/StoryGroup.vue +1 -1
  95. package/src/app/plugin.ts +7 -3
  96. package/src/app/sandbox.ts +1 -1
  97. package/src/app/stores/folder.ts +1 -1
  98. package/src/app/types.ts +2 -2
  99. package/src/app/util/commands.ts +26 -16
  100. package/src/app/util/docs.ts +2 -1
  101. package/src/app/util/grid-slots.ts +1 -1
  102. package/src/app/util/highlighter.ts +9 -1
  103. package/src/app/util/icons.generated.ts +1 -1
  104. package/src/app/util/keyboard.ts +3 -0
  105. package/src/app/util/mapping.ts +6 -2
  106. package/src/app/util/preview-settings.ts +1 -1
  107. package/src/app/util/scroll.ts +5 -1
  108. package/src/app/util/state.ts +1 -1
  109. package/src/app/util/variant.ts +4 -1
  110. package/src/app/components/base/BaseCopyIcon.vue +0 -34
@@ -27,7 +27,8 @@ var StoryView_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ define
27
27
  const router = useRouter();
28
28
  const route = useRoute();
29
29
  watch(() => storyStore.currentVariant, (value) => {
30
- if (value) storyStore.currentStory.lastSelectedVariant = value;
30
+ const story = storyStore.currentStory;
31
+ if (value && story) story.lastSelectedVariant = value;
31
32
  }, { immediate: true });
32
33
  watch(() => [storyStore.currentStory, storyStore.currentVariant], () => {
33
34
  if (!storyStore.currentVariant && autoSelectsVariant(storyStore.currentStory)) setVariant((storyStore.currentStory.lastSelectedVariant ?? storyStore.currentStory.variants[0]).id);
@@ -41,7 +42,7 @@ var StoryView_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ define
41
42
  }
42
43
  });
43
44
  }
44
- const docsOnlyScroller = ref(null);
45
+ const docsOnlyScroller = ref();
45
46
  function scrollDocsToTop() {
46
47
  docsOnlyScroller.value?.scrollTo(0, 0);
47
48
  }
@@ -2,6 +2,6 @@ import _plugin_vue_export_helper_default from "../../_virtual/_plugin-vue_export
2
2
  import StoryViewer_vue_vue_type_script_setup_true_lang_default from "./StoryViewer.vue_vue_type_script_setup_true_lang.js";
3
3
  /* empty css */
4
4
  //#region src/app/components/story/StoryViewer.vue
5
- var StoryViewer_default = /*#__PURE__*/ _plugin_vue_export_helper_default(StoryViewer_vue_vue_type_script_setup_true_lang_default, [["__scopeId", "data-v-d5a7fe4c"]]);
5
+ var StoryViewer_default = /*#__PURE__*/ _plugin_vue_export_helper_default(StoryViewer_vue_vue_type_script_setup_true_lang_default, [["__scopeId", "data-v-5e1183de"]]);
6
6
  //#endregion
7
7
  export { StoryViewer_default as default };
@@ -9,7 +9,7 @@ var _hoisted_1 = { class: "poveste-story-viewer bg-gray-50 h-full dark:bg-gray-7
9
9
  var StoryViewer_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ defineComponent({
10
10
  __name: "StoryViewer",
11
11
  setup(__props) {
12
- useCssVars((_ctx) => ({ "v4fc4017e": variant.value?.iconColor }));
12
+ useCssVars((_ctx) => ({ "v96d17610": variant.value?.iconColor }));
13
13
  const storyStore = useStoryStore();
14
14
  const variant = computed(() => storyStore.currentVariant);
15
15
  const isMenuOpened = ref(false);
@@ -20,7 +20,7 @@ var StoryViewer_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ defi
20
20
  isMenuOpened.value = false;
21
21
  });
22
22
  return (_ctx, _cache) => {
23
- return openBlock(), createElementBlock(Fragment, null, [createElementVNode("div", _hoisted_1, [unref(storyStore).currentStory.layout.type === "grid" ? (openBlock(), createBlock(StoryVariantGrid_default, { key: 0 })) : unref(storyStore).currentStory.layout.type === "single" ? (openBlock(), createBlock(StoryVariantSingle_default, {
23
+ return openBlock(), createElementBlock(Fragment, null, [createElementVNode("div", _hoisted_1, [unref(storyStore).currentStory?.layout?.type === "grid" ? (openBlock(), createBlock(StoryVariantGrid_default, { key: 0 })) : unref(storyStore).currentStory?.layout?.type === "single" ? (openBlock(), createBlock(StoryVariantSingle_default, {
24
24
  key: 1,
25
25
  onOpenVariantMenu: _cache[0] || (_cache[0] = ($event) => isMenuOpened.value = true)
26
26
  })) : createCommentVNode("", true)]), createVNode(MobileOverlay_default, {
@@ -28,7 +28,7 @@ var StoryViewer_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ defi
28
28
  opened: isMenuOpened.value,
29
29
  onClose: closeMenu
30
30
  }, {
31
- default: withCtx(() => [(openBlock(true), createElementBlock(Fragment, null, renderList(unref(storyStore).currentStory.variants, (v, index) => {
31
+ default: withCtx(() => [(openBlock(true), createElementBlock(Fragment, null, renderList(unref(storyStore).currentStory?.variants ?? [], (v, index) => {
32
32
  return openBlock(), createBlock(StoryVariantListItem_default, {
33
33
  key: index,
34
34
  variant: v
@@ -1,7 +1,5 @@
1
- import _plugin_vue_export_helper_default from "../../_virtual/_plugin-vue_export-helper.js";
2
1
  import ToolbarBackground_vue_vue_type_script_setup_true_lang_default from "./ToolbarBackground.vue_vue_type_script_setup_true_lang.js";
3
- /* empty css */
4
2
  //#region src/app/components/toolbar/ToolbarBackground.vue
5
- var ToolbarBackground_default = /*#__PURE__*/ _plugin_vue_export_helper_default(ToolbarBackground_vue_vue_type_script_setup_true_lang_default, [["__scopeId", "data-v-ed785ad4"]]);
3
+ var ToolbarBackground_default = ToolbarBackground_vue_vue_type_script_setup_true_lang_default;
6
4
  //#endregion
7
5
  export { ToolbarBackground_default as default };
@@ -1,7 +1,7 @@
1
1
  import { povesteConfig } from "../../util/config.js";
2
2
  import { usePreviewSettingsStore } from "../../stores/preview-settings.js";
3
3
  import BaseCheckbox_default from "../base/BaseCheckbox.js";
4
- import { Fragment, createBlock, createCommentVNode, createElementBlock, createElementVNode, createTextVNode, createVNode, defineComponent, normalizeClass, normalizeStyle, openBlock, renderList, resolveComponent, resolveDirective, toDisplayString, unref, useCssVars, withCtx, withDirectives } from "@poveste/vendors/vue";
4
+ import { Fragment, createBlock, createCommentVNode, createElementBlock, createElementVNode, createTextVNode, createVNode, defineComponent, normalizeClass, normalizeStyle, openBlock, renderList, resolveComponent, resolveDirective, toDisplayString, unref, withCtx, withDirectives } from "@poveste/vendors/vue";
5
5
  import { Icon } from "@poveste/vendors/iconify";
6
6
  //#region src/app/components/toolbar/ToolbarBackground.vue?vue&type=script&setup=true&lang.ts
7
7
  var _hoisted_1 = { class: "flex items-center gap-1 px-2.5 py-1.5 hover:bg-white/50 dark:hover:bg-white/10 hover:text-primary-500 dark:hover:text-primary-400 cursor-pointer text-gray-900 dark:text-gray-100 transition-colors group" };
@@ -22,10 +22,6 @@ var _hoisted_8 = { key: 0 };
22
22
  var ToolbarBackground_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ defineComponent({
23
23
  __name: "ToolbarBackground",
24
24
  setup(__props) {
25
- useCssVars((_ctx) => ({
26
- "ca113a48": unref(settings).backgroundColor,
27
- "v0083f4ef": _ctx.contrastColor
28
- }));
29
25
  const previewSettings = usePreviewSettingsStore();
30
26
  const settings = previewSettings.currentSettings;
31
27
  const colorSchemeOptions = [
@@ -46,7 +42,7 @@ var ToolbarBackground_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*
46
42
  }
47
43
  ];
48
44
  const showColorScheme = !povesteConfig.theme.hideColorSchemeSwitch;
49
- const showDropdown = showColorScheme || !!povesteConfig.backgroundPresets.length;
45
+ const showDropdown = showColorScheme || !!povesteConfig.backgroundPresets?.length;
50
46
  return (_ctx, _cache) => {
51
47
  const _component_VDropdown = resolveComponent("VDropdown");
52
48
  const _directive_tooltip = resolveDirective("tooltip");
@@ -1,7 +1,7 @@
1
1
  import { povesteConfig } from "../../util/config.js";
2
2
  import { usePreviewSettingsStore } from "../../stores/preview-settings.js";
3
3
  import BaseCheckbox_default from "../base/BaseCheckbox.js";
4
- import { Fragment, createBlock, createCommentVNode, createElementBlock, createElementVNode, createTextVNode, createVNode, defineComponent, normalizeClass, openBlock, renderList, resolveComponent, resolveDirective, toDisplayString, unref, vModelText, withCtx, withDirectives } from "@poveste/vendors/vue";
4
+ import { Fragment, computed, createBlock, createCommentVNode, createElementBlock, createElementVNode, createTextVNode, createVNode, defineComponent, isRef, normalizeClass, openBlock, renderList, resolveComponent, resolveDirective, toDisplayString, unref, vModelText, withCtx, withDirectives } from "@poveste/vendors/vue";
5
5
  import { Icon } from "@poveste/vendors/iconify";
6
6
  //#region src/app/components/toolbar/ToolbarResponsiveSize.vue?vue&type=script&setup=true&lang.ts
7
7
  var _hoisted_1 = { class: "flex flex-col items-stretch" };
@@ -17,6 +17,17 @@ var ToolbarResponsiveSize_vue_vue_type_script_setup_true_lang_default = /*@__PUR
17
17
  __name: "ToolbarResponsiveSize",
18
18
  setup(__props) {
19
19
  const settings = usePreviewSettingsStore().currentSettings;
20
+ function autoWhenEmpty(key) {
21
+ return computed({
22
+ get: () => settings[key],
23
+ set: (value) => {
24
+ const parsed = typeof value === "number" ? value : Number.parseFloat(value ?? "");
25
+ settings[key] = Number.isFinite(parsed) ? parsed : null;
26
+ }
27
+ });
28
+ }
29
+ const responsiveWidth = autoWhenEmpty("responsiveWidth");
30
+ const responsiveHeight = autoWhenEmpty("responsiveHeight");
20
31
  return (_ctx, _cache) => {
21
32
  const _component_VDropdown = resolveComponent("VDropdown");
22
33
  const _directive_tooltip = resolveDirective("tooltip");
@@ -36,30 +47,20 @@ var ToolbarResponsiveSize_vue_vue_type_script_setup_true_lang_default = /*@__PUR
36
47
  }, 8, ["modelValue"]),
37
48
  createElementVNode("div", _hoisted_2, [
38
49
  withDirectives(createElementVNode("input", {
39
- "onUpdate:modelValue": _cache[1] || (_cache[1] = ($event) => unref(settings).responsiveWidth = $event),
50
+ "onUpdate:modelValue": _cache[1] || (_cache[1] = ($event) => isRef(responsiveWidth) ? responsiveWidth.value = $event : null),
40
51
  type: "number",
41
52
  class: "bg-transparent border border-gray-200 dark:border-gray-850 rounded w-20 opacity-50 focus:opacity-100 flex-1 min-w-0",
42
53
  step: "16",
43
54
  placeholder: "Auto"
44
- }, null, 512), [[
45
- vModelText,
46
- unref(settings).responsiveWidth,
47
- void 0,
48
- { number: true }
49
- ], [_directive_tooltip, "Responsive width (px)"]]),
55
+ }, null, 512), [[vModelText, unref(responsiveWidth)], [_directive_tooltip, "Responsive width (px)"]]),
50
56
  _cache[4] || (_cache[4] = createElementVNode("span", { class: "opacity-50" }, "×", -1)),
51
57
  withDirectives(createElementVNode("input", {
52
- "onUpdate:modelValue": _cache[2] || (_cache[2] = ($event) => unref(settings).responsiveHeight = $event),
58
+ "onUpdate:modelValue": _cache[2] || (_cache[2] = ($event) => isRef(responsiveHeight) ? responsiveHeight.value = $event : null),
53
59
  type: "number",
54
60
  class: "bg-transparent border border-gray-200 dark:border-gray-850 rounded w-20 opacity-50 focus:opacity-100 flex-1 min-w-0",
55
61
  step: "16",
56
62
  placeholder: "Auto"
57
- }, null, 512), [[
58
- vModelText,
59
- unref(settings).responsiveHeight,
60
- void 0,
61
- { number: true }
62
- ], [_directive_tooltip, "Responsive height (px)"]])
63
+ }, null, 512), [[vModelText, unref(responsiveHeight)], [_directive_tooltip, "Responsive height (px)"]])
63
64
  ]),
64
65
  (openBlock(true), createElementBlock(Fragment, null, renderList(unref(povesteConfig).responsivePresets, (preset, index) => {
65
66
  return openBlock(), createElementBlock("button", {
@@ -67,7 +68,7 @@ var ToolbarResponsiveSize_vue_vue_type_script_setup_true_lang_default = /*@__PUR
67
68
  class: normalizeClass(["px-4 py-3 cursor-pointer text-left flex gap-4", [unref(settings).responsiveWidth === preset.width && unref(settings).responsiveHeight === preset.height ? "bg-primary-500 hover:bg-primary-600 text-white dark:text-black" : "bg-transparent hover:bg-primary-100 dark:hover:bg-primary-700"]]),
68
69
  onClick: ($event) => {
69
70
  unref(settings).responsiveWidth = preset.width;
70
- unref(settings).responsiveHeight = preset.height;
71
+ unref(settings).responsiveHeight = preset.height ?? null;
71
72
  hide();
72
73
  }
73
74
  }, [createTextVNode(toDisplayString(preset.label) + " ", 1), createElementVNode("span", _hoisted_4, [
@@ -1,9 +1,10 @@
1
1
  import { router } from "./router.js";
2
2
  //#region src/app/plugin.ts
3
3
  function setupPluginApi() {
4
- if (!import.meta.hot) return;
4
+ const hot = import.meta.hot;
5
+ if (!hot) return;
5
6
  const listeners = {};
6
- import.meta.hot.on("poveste:dev-event-result", ({ event, result }) => {
7
+ hot.on("poveste:dev-event-result", ({ event, result }) => {
7
8
  const set = listeners[event];
8
9
  if (set) for (const listener of set) listener(result);
9
10
  });
@@ -21,7 +22,7 @@ function setupPluginApi() {
21
22
  window.__HST_PLUGIN_API__ = {
22
23
  sendEvent: (event, payload) => {
23
24
  return new Promise((resolve) => {
24
- import.meta.hot.send(`poveste:dev-event`, {
25
+ hot.send(`poveste:dev-event`, {
25
26
  event,
26
27
  payload
27
28
  });
@@ -38,8 +38,8 @@ if (storedSettings) applyPreviewSettings(storedSettings);
38
38
  if (window.parent && window.parent !== window) window.parent.postMessage({ type: PREVIEW_SETTINGS_REQUEST }, window.location.origin);
39
39
  function reportUncaught(error) {
40
40
  reportStoryError(error, {
41
- storyId: storyId.value,
42
- variantId: variantId.value
41
+ storyId: storyId.value ?? void 0,
42
+ variantId: variantId.value ?? void 0
43
43
  });
44
44
  }
45
45
  window.addEventListener("error", (event) => {
@@ -29,7 +29,7 @@ var useFolderStore = defineStore("folder", () => {
29
29
  }
30
30
  const storyStore = useStoryStore();
31
31
  watch(() => storyStore.currentStory, (story) => {
32
- if (story) openFileFolders(story.file.path);
32
+ if (story?.file) openFileFolders(story.file.path);
33
33
  });
34
34
  return {
35
35
  isFolderOpened,
@@ -2,20 +2,31 @@ import { router } from "../router.js";
2
2
  import { useStoryStore } from "../stores/story.js";
3
3
  import { openInEditor } from "./open-in-editor.js";
4
4
  //#region src/app/util/commands.ts
5
+ /**
6
+ * The path "Open file in editor" should open, or undefined when there is none.
7
+ *
8
+ * `Story.file` is optional, and `let file: string` used to be assigned
9
+ * `story.file?.filePath` in both branches — so a story without a file produced
10
+ * `file: undefined`, and `openInEditor` sent
11
+ * `__open-in-editor?file=undefined` to the dev server: a request to open a file
12
+ * literally named "undefined", with nothing said in the UI (#667).
13
+ *
14
+ * A docs-only story prefers its markdown, falling back to the component file
15
+ * when the story has both.
16
+ */
17
+ function editableFilePath(story) {
18
+ const file = story?.file;
19
+ if (!file) return void 0;
20
+ return story?.docsOnly ? file.docsFilePath ?? file.filePath : file.filePath;
21
+ }
5
22
  var builtinCommands = [{
6
23
  id: "builtin:open-in-editor",
7
24
  label: "Open file in editor",
8
25
  icon: "carbon:script-reference",
9
- showIf: ({ route }) => route.name === "story" && !!useStoryStore().currentStory,
10
- getParams: () => {
11
- const story = useStoryStore().currentStory;
12
- let file;
13
- if (story.docsOnly) file = story.file?.docsFilePath ?? story.file?.filePath;
14
- else file = story.file?.filePath;
15
- return { file };
16
- },
26
+ showIf: ({ route }) => route.name === "story" && !!editableFilePath(useStoryStore().currentStory),
27
+ getParams: () => ({ file: editableFilePath(useStoryStore().currentStory) }),
17
28
  clientAction: ({ file }) => {
18
- openInEditor(file);
29
+ if (file) openInEditor(file);
19
30
  }
20
31
  }, {
21
32
  id: "builtin:poveste-docs",
@@ -7,7 +7,8 @@ async function getSourceCode(story, variant) {
7
7
  const source = variant.slots?.().source()[0].children;
8
8
  if (source) return unindent(source);
9
9
  } else {
10
- const clientPlugin = clientSupportPlugins[story.file?.supportPluginId];
10
+ const supportPluginId = story.file?.supportPluginId;
11
+ const clientPlugin = supportPluginId ? clientSupportPlugins[supportPluginId] : void 0;
11
12
  if (clientPlugin) return (await clientPlugin()).generateSourceCode(variant);
12
13
  }
13
14
  const sourceLoader = story.file?.source;
@@ -11,7 +11,7 @@ function assignSlots(slots, storyId, visible) {
11
11
  const next = slots.map((slot) => slot.storyId === storyId && position.has(slot.variant.id) ? {
12
12
  ...slot,
13
13
  visible: true,
14
- order: position.get(slot.variant.id)
14
+ order: position.get(slot.variant.id) ?? 0
15
15
  } : {
16
16
  ...slot,
17
17
  visible: false,
@@ -18,7 +18,7 @@ var highlighterPromise;
18
18
  function getHighlighter() {
19
19
  highlighterPromise ??= createHighlighterCore({
20
20
  langs: [import("shiki/langs/html.mjs"), import("shiki/langs/jsx.mjs")],
21
- themes: [import("shiki/themes/github-light.mjs"), import("shiki/themes/github-dark.mjs")],
21
+ themes: [import("shiki/themes/github-light-high-contrast.mjs"), import("shiki/themes/github-dark.mjs")],
22
22
  engine: createOnigurumaEngine(import("shiki/wasm"))
23
23
  }).catch((e) => {
24
24
  highlighterPromise = void 0;
@@ -6,7 +6,6 @@ var iconCollections = [
6
6
  "add-alt": { "body": "<path fill=\"currentColor\" d=\"M16 4c6.6 0 12 5.4 12 12s-5.4 12-12 12S4 22.6 4 16S9.4 4 16 4m0-2C8.3 2 2 8.3 2 16s6.3 14 14 14s14-6.3 14-14S23.7 2 16 2\"/><path fill=\"currentColor\" d=\"M24 15h-7V8h-2v7H8v2h7v7h2v-7h7z\"/>" },
7
7
  "area": { "body": "<path fill=\"currentColor\" d=\"M27 22.142V9.858A3.992 3.992 0 1 0 22.142 5H9.858A3.992 3.992 0 1 0 5 9.858v12.284A3.992 3.992 0 1 0 9.858 27h12.284A3.992 3.992 0 1 0 27 22.142M26 4a2 2 0 1 1-2 2a2 2 0 0 1 2-2M4 6a2 2 0 1 1 2 2a2 2 0 0 1-2-2m2 22a2 2 0 1 1 2-2a2 2 0 0 1-2 2m16.142-3H9.858A4 4 0 0 0 7 22.142V9.858A4 4 0 0 0 9.858 7h12.284A4 4 0 0 0 25 9.858v12.284A3.99 3.99 0 0 0 22.142 25M26 28a2 2 0 1 1 2-2a2.003 2.003 0 0 1-2 2\"/>" },
8
8
  "audio-console": { "body": "<path fill=\"currentColor\" d=\"M28 14.005h-1v-12h-2v12h-1a2 2 0 0 0-2 2v2a2 2 0 0 0 2 2h1v10h2v-10h1a2.003 2.003 0 0 0 2-2v-2a2 2 0 0 0-2-2m0 4h-4v-2h4Zm-10-12h-1v-4h-2v4h-1a2 2 0 0 0-2 2v2a2 2 0 0 0 2 2h1v18h2v-18h1a2 2 0 0 0 2-2v-2a2 2 0 0 0-2-2m0 4h-4v-2h4Zm-10 10H7v-18H5v18H4a2 2 0 0 0-2 2v2a2 2 0 0 0 2 2h1v4h2v-4h1a2 2 0 0 0 2-2v-2a2 2 0 0 0-2-2m0 4H4v-2h4Z\"/>" },
9
- "bring-forward": { "body": "<path fill=\"currentColor\" d=\"M12 15h-2v-3a2 2 0 0 1 2-2h3v2h-3Zm3 15h-3a2 2 0 0 1-2-2v-3h2v3h3Zm3-2h4v2h-4zm10 2h-3v-2h3v-3h2v3a2 2 0 0 1-2 2M10 18h2v4h-2zm18 0h2v4h-2zm2-3h-2v-3h-3v-2h3a2 2 0 0 1 2 2Zm-12-5h4v2h-4z\"/><path fill=\"currentColor\" d=\"M8 22H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h16a2 2 0 0 1 2 2v4h-2V4H4v16h4Z\"/>" },
10
9
  "caret-down": { "body": "<path fill=\"currentColor\" d=\"m24 12l-8 10l-8-10z\"/>" },
11
10
  "caret-right": { "body": "<path fill=\"currentColor\" d=\"m12 8l10 8l-10 8z\"/>" },
12
11
  "checkbox": { "body": "<path fill=\"currentColor\" d=\"M26 4H6a2 2 0 0 0-2 2v20a2 2 0 0 0 2 2h20a2 2 0 0 0 2-2V6a2 2 0 0 0-2-2M6 26V6h20v20Z\"/>" },
@@ -88,6 +88,7 @@ function isMatchingShortcut(shortcut) {
88
88
  function isMatchingCombination(combination) {
89
89
  const splitted = combination.split("+").map((key) => key.trim());
90
90
  const targetKey = splitted.pop();
91
+ if (!targetKey) return false;
91
92
  for (const mod in modifiers) if (splitted.includes(mod) !== modifiers[mod].pressed) return false;
92
93
  return pressedKeys.has(targetKey);
93
94
  }
@@ -45,8 +45,12 @@ function mapVariant(variant, existingVariant) {
45
45
  _hPropState: {},
46
46
  _hPropDefs: {}
47
47
  }),
48
- setupApp: null,
49
- slots: () => ({}),
48
+ setupApp: void 0,
49
+ slots: () => ({
50
+ default: void 0,
51
+ controls: void 0,
52
+ source: void 0
53
+ }),
50
54
  previewReady: false
51
55
  };
52
56
  return result;
@@ -24,7 +24,7 @@ function applyPreviewSettings(settings) {
24
24
  if (povesteConfig.autoApplyContrastColor) document.documentElement.style.color = contrastColor;
25
25
  }
26
26
  function getContrastColor(setting) {
27
- return povesteConfig.backgroundPresets.find((preset) => preset.color === setting.backgroundColor)?.contrastColor ?? "unset";
27
+ return povesteConfig.backgroundPresets?.find((preset) => preset.color === setting.backgroundColor)?.contrastColor ?? "unset";
28
28
  }
29
29
  //#endregion
30
30
  export { PREVIEW_SETTINGS_STORAGE_KEY, applyPreviewSettings, getContrastColor, loadStoredPreviewSettings, receivedSettings };
package/dist/style.css CHANGED
@@ -1625,6 +1625,9 @@
1625
1625
  background-color: color-mix(in oklab, var(--color-primary-500) 25%, transparent);
1626
1626
  }
1627
1627
  }
1628
+ .bg-primary-700 {
1629
+ background-color: var(--color-primary-700);
1630
+ }
1628
1631
  .bg-transparent {
1629
1632
  background-color: transparent;
1630
1633
  }
@@ -1861,8 +1864,8 @@
1861
1864
  .text-primary-500 {
1862
1865
  color: var(--color-primary-500);
1863
1866
  }
1864
- .text-primary-600 {
1865
- color: var(--color-primary-600);
1867
+ .text-primary-700 {
1868
+ color: var(--color-primary-700);
1866
1869
  }
1867
1870
  .text-primary-800 {
1868
1871
  color: var(--color-primary-800);
@@ -2130,6 +2133,9 @@
2130
2133
  .hover\:bg-primary-600:hover {
2131
2134
  background-color: var(--color-primary-600);
2132
2135
  }
2136
+ .hover\:bg-primary-800:hover {
2137
+ background-color: var(--color-primary-800);
2138
+ }
2133
2139
  .hover\:bg-white\/50:hover {
2134
2140
  background-color: color-mix(in srgb, #fff 50%, transparent);
2135
2141
  }
@@ -2288,6 +2294,9 @@
2288
2294
  .dark\:bg-primary-400:where(.ptw-dark, .ptw-dark *) {
2289
2295
  background-color: var(--color-primary-400);
2290
2296
  }
2297
+ .dark\:bg-primary-500:where(.ptw-dark, .ptw-dark *) {
2298
+ background-color: var(--color-primary-500);
2299
+ }
2291
2300
  .dark\:bg-primary-600:where(.ptw-dark, .ptw-dark *) {
2292
2301
  background-color: var(--color-primary-600);
2293
2302
  }
@@ -2385,6 +2394,9 @@
2385
2394
  .dark\:hover\:bg-gray-800:where(.ptw-dark, .ptw-dark *):hover {
2386
2395
  background-color: var(--color-gray-800);
2387
2396
  }
2397
+ .dark\:hover\:bg-primary-600:where(.ptw-dark, .ptw-dark *):hover {
2398
+ background-color: var(--color-primary-600);
2399
+ }
2388
2400
  .dark\:hover\:bg-primary-700:where(.ptw-dark, .ptw-dark *):hover {
2389
2401
  background-color: var(--color-primary-700);
2390
2402
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@poveste/app",
3
3
  "type": "module",
4
- "version": "0.13.0",
4
+ "version": "0.14.0",
5
5
  "description": "Pre-bundled UI for Poveste",
6
6
  "author": {
7
7
  "name": "Sorin Gitlan",
@@ -38,9 +38,9 @@
38
38
  "node": "^22.22.2 || ^24.15.0 || >=26.0.0"
39
39
  },
40
40
  "dependencies": {
41
- "@poveste/controls": "^0.13.0",
42
- "@poveste/shared": "^0.13.0",
43
- "@poveste/vendors": "^0.13.0",
41
+ "@poveste/controls": "^0.14.0",
42
+ "@poveste/shared": "^0.14.0",
43
+ "@poveste/vendors": "^0.14.0",
44
44
  "fuse.js": "^7.0.0",
45
45
  "shiki": "^3.0.0"
46
46
  },
@@ -66,11 +66,13 @@
66
66
  "tailwindcss": "^4.3.3",
67
67
  "typescript": "5.6.3",
68
68
  "vite": "^8.2.0",
69
- "vitest": "^4.1.10",
70
- "vue": "^3.5.26"
69
+ "vitest": "^5.0.0",
70
+ "vue": "^3.5.26",
71
+ "vue-tsc": "^2.1.10"
71
72
  },
72
73
  "scripts": {
73
74
  "build": "pnpm run icons && rimraf dist && vite build && pnpm run build:css && node ./scripts/copy.mjs",
75
+ "typecheck": "vue-tsc --noEmit",
74
76
  "build:css": "postcss src/app/style/main.pcss -o ./dist/style.css",
75
77
  "icons": "node ./scripts/icons.mjs",
76
78
  "test": "vitest run",
package/src/app/App.vue CHANGED
@@ -5,7 +5,7 @@ export default {
5
5
  </script>
6
6
 
7
7
  <script lang="ts" setup>
8
- import type { StoryFile, Tree } from './types'
8
+ import type { Story, StoryFile, Tree } from './types'
9
9
  import { useTitle } from '@vueuse/core'
10
10
  import { onUpdate, files as rawFiles, tree as rawTree } from 'virtual:$poveste-stories'
11
11
  import { computed, onMounted, ref, watch } from 'vue'
@@ -41,8 +41,11 @@ onUpdate((newFiles: StoryFile[], newTree: Tree) => {
41
41
  tree.value = newTree
42
42
  })
43
43
 
44
- const stories = computed(() => files.value.reduce((acc, file) => {
45
- acc.push(file.story)
44
+ // The accumulator needs a type: `[]` alone infers `never[]`, so the push was an
45
+ // error about the seed rather than about the story. A file whose story has not
46
+ // been collected yet contributes nothing.
47
+ const stories = computed(() => files.value.reduce<Story[]>((acc, file) => {
48
+ if (file.story) acc.push(file.story)
46
49
  return acc
47
50
  }, []))
48
51
 
@@ -141,52 +144,55 @@ const storyListVisible = computed(() => !!layoutStore.settings.storyListVisible)
141
144
  }"
142
145
  >
143
146
  <div
144
- v-if="isMobile"
145
- class="h-full flex flex-col divide-y divide-gray-100 dark:divide-gray-800"
147
+ class="h-full flex flex-col"
148
+ :class="{ 'divide-y divide-gray-100 dark:divide-gray-800': isMobile }"
146
149
  >
147
- <div class="flex items-center gap-2 pr-4">
148
- <AppHeader class="flex-1" />
149
- <AppActions
150
- class="flex-none"
151
- @layout="isLayoutOpen = true"
152
- @search="isSearchOpen = true"
150
+ <!--
151
+ The mobile chrome is a sibling of `main`, not a child: `AppHeader`
152
+ carries the page's banner and its only `h1`, and a `header` inside
153
+ `main` is neither (#310).
154
+ -->
155
+ <template v-if="isMobile">
156
+ <div class="flex items-center gap-2 pr-4">
157
+ <AppHeader class="flex-1" />
158
+ <AppActions
159
+ class="flex-none"
160
+ @layout="isLayoutOpen = true"
161
+ @search="isSearchOpen = true"
162
+ />
163
+ </div>
164
+ <Breadcrumb
165
+ :tree="tree"
166
+ :stories="stories"
153
167
  />
154
- </div>
155
- <Breadcrumb
156
- :tree="tree"
157
- :stories="stories"
158
- />
159
- <main class="grow flex flex-col min-h-0">
160
- <RouterView class="grow" />
161
- </main>
162
- </div>
168
+ </template>
163
169
 
164
- <template v-else>
165
170
  <!--
166
171
  With the story list hidden `AppHeader` goes with it, taking the page's
167
172
  only `h1` and its banner. A heading has to exist in every layout, and
168
173
  it has to sit inside a landmark, so this stands in for it (#310).
169
174
  -->
170
175
  <header
171
- v-if="!storyListVisible"
176
+ v-else-if="!storyListVisible"
172
177
  class="sr-only"
173
178
  >
174
179
  <h1>{{ povesteConfig.theme?.title ?? 'Poveste' }}</h1>
175
180
  </header>
176
181
 
177
182
  <!--
178
- One split pane whether or not the story list is showing. Two branches
179
- each carrying their own `RouterView` moved the whole view in the tree
180
- when the list was toggled, cold-booting the sandbox under it — the same
181
- remount #328 removed from sidebar clicks (#596).
183
+ One split pane for every layout, mobile included. Each chrome used to
184
+ be a branch carrying its own `RouterView`, so switching between them
185
+ moved the routed view in the tree and cold-booted the sandbox under
186
+ it. The settings toggles were #596; `isMobile` is a live media query,
187
+ so this one fired on a window resize (#600).
182
188
  -->
183
189
  <BaseSplitPane
184
190
  save-id="main-horiz"
185
191
  :min="5"
186
192
  :max="50"
187
193
  :default-split="15"
188
- :show-first="storyListVisible"
189
- class="h-full"
194
+ :show-first="!isMobile && storyListVisible"
195
+ class="flex-1 min-h-0"
190
196
  >
191
197
  <template #first>
192
198
  <div class="flex flex-col h-full bg-gray-100 dark:bg-gray-750 __poveste-pane-shadow-from-right">
@@ -207,6 +213,7 @@ const storyListVisible = computed(() => !!layoutStore.settings.storyListVisible)
207
213
  <template #last>
208
214
  <main class="flex flex-col h-full">
209
215
  <TopBar
216
+ v-if="!isMobile"
210
217
  @layout="isLayoutOpen = true"
211
218
  @search="isSearchOpen = true"
212
219
  />
@@ -214,7 +221,7 @@ const storyListVisible = computed(() => !!layoutStore.settings.storyListVisible)
214
221
  </main>
215
222
  </template>
216
223
  </BaseSplitPane>
217
- </template>
224
+ </div>
218
225
 
219
226
  <LayoutModal
220
227
  v-if="!isMobile"
@@ -5,7 +5,7 @@ import { useStoryStore } from '../stores/story'
5
5
  import { customLogos, povesteConfig } from '../util/config'
6
6
  import HomeCounter from './app/HomeCounter.vue'
7
7
 
8
- const logoUrl = computed(() => povesteConfig.theme?.logo?.square ? customLogos.square : PovesteLogo)
8
+ const logoUrl = computed(() => povesteConfig.theme?.logo?.square ? customLogos?.square ?? PovesteLogo : PovesteLogo)
9
9
  const storyStore = useStoryStore()
10
10
 
11
11
  const stats = computed(() => {
@@ -6,11 +6,15 @@ import PovesteLogoLight from '../../assets/poveste-text.svg'
6
6
  import { customLogos, povesteConfig } from '../../util/config'
7
7
  import { isDark } from '../../util/dark'
8
8
 
9
+ // `theme.logo` is what the config asked for; `customLogos` is the resolved URL
10
+ // map from the virtual module. Two objects, so a check on one narrows nothing
11
+ // about the other — and falling back to the bundled logo beats rendering an
12
+ // `<img>` with no `src` if they ever disagree.
9
13
  const logoUrl = computed(() => {
10
14
  if (isDark.value) {
11
- return povesteConfig.theme.logo?.dark ? customLogos.dark : PovesteLogoDark
15
+ return povesteConfig.theme.logo?.dark ? customLogos?.dark ?? PovesteLogoDark : PovesteLogoDark
12
16
  }
13
- return povesteConfig.theme.logo?.light ? customLogos.light : PovesteLogoLight
17
+ return povesteConfig.theme.logo?.light ? customLogos?.light ?? PovesteLogoLight : PovesteLogoLight
14
18
  })
15
19
 
16
20
  const altText = computed(() => `${povesteConfig.theme.title} logo`)
@@ -16,7 +16,8 @@ const storyStore = useStoryStore()
16
16
  const story = computed(() => storyStore.currentStory)
17
17
 
18
18
  const folders = computed(() => {
19
- return story.value.file.path.slice(0, -1)
19
+ // No story, or a story with no file behind it, means no trail to draw.
20
+ return story.value?.file?.path.slice(0, -1) ?? []
20
21
  })
21
22
 
22
23
  const isMenuOpened = ref(false)
@@ -1,4 +1,5 @@
1
1
  <script lang="ts" setup>
2
+ import { computed } from 'vue'
2
3
  import { useStoryStore } from '../../stores/story'
3
4
  import DevOnlyToolbarOpenInEditor from '../toolbar/DevOnlyToolbarOpenInEditor.vue'
4
5
  import ToolbarBackground from '../toolbar/ToolbarBackground.vue'
@@ -15,6 +16,10 @@ defineEmits({
15
16
  })
16
17
 
17
18
  const storyStore = useStoryStore()
19
+
20
+ // Named once so the `v-if` and the binding below read the same value; a story
21
+ // without a file has nothing to open.
22
+ const editableFile = computed(() => storyStore.currentStory?.file?.filePath)
18
23
  </script>
19
24
 
20
25
  <template>
@@ -51,8 +56,8 @@ const storyStore = useStoryStore()
51
56
  :story="storyStore.currentStory"
52
57
  />
53
58
  <DevOnlyToolbarOpenInEditor
54
- v-if="__POVESTE_DEV__"
55
- :file="storyStore.currentStory.file?.filePath"
59
+ v-if="__POVESTE_DEV__ && editableFile"
60
+ :file="editableFile"
56
61
  tooltip="Edit story in editor"
57
62
  />
58
63
  </TopBarChip>
@@ -28,7 +28,7 @@ const progress = computed(() => props.modelValue ? 1 : 0)
28
28
  const dashoffset = computed(() => (1 - progress.value) * dasharray.value)
29
29
 
30
30
  watch(path, () => {
31
- dasharray.value = path.value.getTotalLength?.() ?? 21.21
31
+ dasharray.value = path.value?.getTotalLength?.() ?? 21.21
32
32
  })
33
33
  </script>
34
34