@poveste/app 0.1.2 → 0.2.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.
- package/dist/app/style/floating.pcss +4 -4
- package/dist/app/style/main.pcss +2 -2
- package/dist/app/style/sandbox.css +1 -1
- package/dist/bundle-main.js +2 -0
- package/dist/bundled/App.vue2.js +81 -37
- package/dist/bundled/app.css +51 -46
- package/dist/bundled/components/HomeView.vue2.js +3 -3
- package/dist/bundled/components/app/AppActions.vue.js +5 -0
- package/dist/bundled/components/app/AppActions.vue2.js +76 -0
- package/dist/bundled/components/app/AppHeader.vue2.js +5 -47
- package/dist/bundled/components/app/Breadcrumb.vue.js +1 -1
- package/dist/bundled/components/app/Breadcrumb.vue2.js +9 -9
- package/dist/bundled/components/app/HomeCounter.vue2.js +5 -5
- package/dist/bundled/components/app/InitialLoading.vue2.js +5 -5
- package/dist/bundled/components/app/MobileOverlay.vue2.js +5 -5
- package/dist/bundled/components/app/TopBar.vue.js +5 -0
- package/dist/bundled/components/app/TopBar.vue2.js +73 -0
- package/dist/bundled/components/app/TopBarChip.vue.js +13 -0
- package/dist/bundled/components/app/TopBarChipItem.vue.js +13 -0
- package/dist/bundled/components/base/BaseButton.vue2.js +3 -3
- package/dist/bundled/components/base/BaseCheckbox.vue2.js +8 -8
- package/dist/bundled/components/base/BaseEmpty.vue.js +2 -2
- package/dist/bundled/components/base/BaseKeyboardShortcut.vue2.js +1 -1
- package/dist/bundled/components/base/BaseListItem.vue2.js +2 -2
- package/dist/bundled/components/base/BaseListItemLink.vue.js +2 -2
- package/dist/bundled/components/base/BaseOverflowMenu.vue2.js +4 -4
- package/dist/bundled/components/base/BaseOverflowTab.vue.js +3 -3
- package/dist/bundled/components/base/BaseSelect.vue2.js +6 -6
- package/dist/bundled/components/base/BaseSplitPane.vue.js +1 -1
- package/dist/bundled/components/base/BaseSplitPane.vue2.js +20 -16
- package/dist/bundled/components/base/BaseTab.vue.js +3 -3
- package/dist/bundled/components/base/BaseTag.vue.js +1 -1
- package/dist/bundled/components/command/CommandPrompts.vue2.js +5 -5
- package/dist/bundled/components/command/CommandPromptsModal.vue2.js +3 -3
- package/dist/bundled/components/command/PromptSelect.vue2.js +12 -12
- package/dist/bundled/components/command/PromptText.vue2.js +4 -4
- package/dist/bundled/components/layout/LayoutItem.vue.js +5 -0
- package/dist/bundled/components/layout/LayoutItem.vue2.js +44 -0
- package/dist/bundled/components/layout/LayoutItemGroup.vue.js +5 -0
- package/dist/bundled/components/layout/LayoutItemGroup.vue2.js +25 -0
- package/dist/bundled/components/layout/LayoutModal.vue.js +5 -0
- package/dist/bundled/components/layout/LayoutModal.vue2.js +68 -0
- package/dist/bundled/components/layout/LayoutPane.vue.js +5 -0
- package/dist/bundled/components/layout/LayoutPane.vue2.js +59 -0
- package/dist/bundled/components/panel/ControlsComponentPropItem.vue2.js +2 -2
- package/dist/bundled/components/panel/ControlsComponentProps.vue2.js +4 -4
- package/dist/bundled/components/panel/ControlsComponentState.vue2.js +2 -2
- package/dist/bundled/components/panel/PaneTabs.vue2.js +4 -4
- package/dist/bundled/components/panel/StatePresets.vue2.js +13 -13
- package/dist/bundled/components/panel/StoryControls.vue2.js +6 -6
- package/dist/bundled/components/panel/StoryDocs.vue2.js +4 -4
- package/dist/bundled/components/panel/StoryEvent.vue2.js +7 -7
- package/dist/bundled/components/panel/StoryEvents.vue2.js +1 -1
- package/dist/bundled/components/panel/StorySidePanel.vue2.js +58 -47
- package/dist/bundled/components/panel/StorySourceCode.vue.js +1 -1
- package/dist/bundled/components/panel/StorySourceCode.vue2.js +19 -19
- package/dist/bundled/components/search/SearchItem.vue.js +1 -1
- package/dist/bundled/components/search/SearchItem.vue2.js +3 -3
- package/dist/bundled/components/search/SearchItemContent.vue2.js +9 -9
- package/dist/bundled/components/search/SearchLoading.vue2.js +2 -2
- package/dist/bundled/components/search/SearchModal.vue2.js +3 -3
- package/dist/bundled/components/search/SearchPane.vue2.js +4 -4
- package/dist/bundled/components/story/StoryResponsivePreview.vue.js +1 -1
- package/dist/bundled/components/story/StoryResponsivePreview.vue2.js +21 -21
- package/dist/bundled/components/story/StoryVariantGrid.vue2.js +7 -26
- package/dist/bundled/components/story/StoryVariantGridItem.vue.js +1 -1
- package/dist/bundled/components/story/StoryVariantGridItem.vue2.js +36 -21
- package/dist/bundled/components/story/StoryVariantListItem.vue.js +1 -1
- package/dist/bundled/components/story/StoryVariantListItem.vue2.js +5 -5
- package/dist/bundled/components/story/StoryVariantSingle.vue.js +1 -1
- package/dist/bundled/components/story/StoryVariantSingle.vue2.js +10 -10
- package/dist/bundled/components/story/StoryVariantSinglePreviewNative.vue2.js +6 -5
- package/dist/bundled/components/story/StoryVariantSinglePreviewRemote.vue2.js +33 -8
- package/dist/bundled/components/story/StoryVariantSingleView.vue2.js +8 -39
- package/dist/bundled/components/story/StoryView.vue2.js +21 -11
- package/dist/bundled/components/story/StoryViewer.vue.js +1 -1
- package/dist/bundled/components/story/StoryViewer.vue2.js +2 -2
- package/dist/bundled/components/toolbar/DevOnlyToolbarOpenInEditor.vue2.js +13 -9
- package/dist/bundled/components/toolbar/ToolbarBackground.vue.js +1 -1
- package/dist/bundled/components/toolbar/ToolbarBackground.vue2.js +62 -33
- package/dist/bundled/components/toolbar/ToolbarNewTab.vue2.js +13 -10
- package/dist/bundled/components/toolbar/ToolbarResponsiveSize.vue2.js +14 -14
- package/dist/bundled/components/toolbar/ToolbarTextDirection.vue2.js +13 -9
- package/dist/bundled/components/toolbar/ToolbarTitle.vue.js +4 -1
- package/dist/bundled/components/toolbar/ToolbarTitle.vue2.js +30 -7
- package/dist/bundled/components/tree/StoryGroup.vue2.js +5 -5
- package/dist/bundled/components/tree/StoryList.vue2.js +1 -1
- package/dist/bundled/components/tree/StoryListFolder.vue.js +1 -1
- package/dist/bundled/components/tree/StoryListFolder.vue2.js +7 -7
- package/dist/bundled/components/tree/StoryListItem.vue.js +1 -1
- package/dist/bundled/components/tree/StoryListItem.vue2.js +8 -8
- package/dist/bundled/index.js +3 -0
- package/dist/bundled/sandbox.js +41 -15
- package/dist/bundled/stores/layout.js +16 -0
- package/dist/bundled/stores/preview-settings.js +19 -4
- package/dist/bundled/util/color-scheme.js +27 -0
- package/dist/bundled/util/const.js +2 -0
- package/dist/bundled/util/dark.js +3 -3
- package/dist/bundled/util/keyboard.js +60 -32
- package/dist/bundled/util/preview-settings.js +11 -0
- package/dist/bundled/util/tooltip.js +2 -2
- package/dist/style.css +644 -529
- package/package.json +8 -8
- package/src/app/App.vue +109 -66
- package/src/app/components/HomeView.vue +3 -3
- package/src/app/components/app/AppActions.vue +58 -0
- package/src/app/components/app/AppHeader.vue +4 -47
- package/src/app/components/app/Breadcrumb.vue +8 -8
- package/src/app/components/app/HomeCounter.vue +5 -5
- package/src/app/components/app/InitialLoading.vue +5 -5
- package/src/app/components/app/MobileOverlay.vue +5 -5
- package/src/app/components/app/TopBar.vue +60 -0
- package/src/app/components/app/TopBarChip.vue +5 -0
- package/src/app/components/app/TopBarChipItem.vue +7 -0
- package/src/app/components/base/BaseButton.vue +3 -3
- package/src/app/components/base/BaseCheckbox.vue +9 -9
- package/src/app/components/base/BaseCopyIcon.vue +1 -1
- package/src/app/components/base/BaseEmpty.vue +4 -4
- package/src/app/components/base/BaseKeyboardShortcut.vue +1 -1
- package/src/app/components/base/BaseListItem.vue +3 -3
- package/src/app/components/base/BaseListItemLink.vue +3 -3
- package/src/app/components/base/BaseOverflowMenu.vue +4 -4
- package/src/app/components/base/BaseOverflowTab.vue +3 -3
- package/src/app/components/base/BaseSelect.vue +6 -6
- package/src/app/components/base/BaseSplitPane.vue +21 -16
- package/src/app/components/base/BaseTab.vue +3 -3
- package/src/app/components/base/BaseTag.vue +1 -1
- package/src/app/components/command/CommandPrompts.vue +5 -5
- package/src/app/components/command/CommandPromptsModal.vue +3 -3
- package/src/app/components/command/PromptSelect.vue +14 -14
- package/src/app/components/command/PromptText.vue +4 -4
- package/src/app/components/layout/LayoutItem.vue +36 -0
- package/src/app/components/layout/LayoutItemGroup.vue +18 -0
- package/src/app/components/layout/LayoutModal.vue +60 -0
- package/src/app/components/layout/LayoutPane.vue +45 -0
- package/src/app/components/panel/ControlsComponentPropItem.vue +2 -2
- package/src/app/components/panel/ControlsComponentProps.vue +3 -3
- package/src/app/components/panel/ControlsComponentState.vue +2 -2
- package/src/app/components/panel/PaneTabs.vue +4 -4
- package/src/app/components/panel/StatePresets.vue +13 -13
- package/src/app/components/panel/StoryControls.vue +6 -6
- package/src/app/components/panel/StoryDocs.vue +4 -4
- package/src/app/components/panel/StoryEvent.vue +7 -7
- package/src/app/components/panel/StoryEvents.vue +1 -1
- package/src/app/components/panel/StorySidePanel.vue +51 -41
- package/src/app/components/panel/StorySourceCode.vue +19 -19
- package/src/app/components/search/SearchItem.vue +2 -2
- package/src/app/components/search/SearchItemContent.vue +9 -9
- package/src/app/components/search/SearchLoading.vue +2 -2
- package/src/app/components/search/SearchModal.vue +3 -3
- package/src/app/components/search/SearchPane.vue +4 -4
- package/src/app/components/story/StoryResponsivePreview.vue +27 -27
- package/src/app/components/story/StoryVariantGrid.vue +5 -24
- package/src/app/components/story/StoryVariantGridItem.vue +36 -17
- package/src/app/components/story/StoryVariantListItem.vue +4 -4
- package/src/app/components/story/StoryVariantSingle.vue +9 -9
- package/src/app/components/story/StoryVariantSinglePreviewNative.vue +7 -5
- package/src/app/components/story/StoryVariantSinglePreviewRemote.vue +37 -7
- package/src/app/components/story/StoryVariantSingleView.vue +1 -34
- package/src/app/components/story/StoryView.vue +23 -8
- package/src/app/components/story/StoryViewer.vue +1 -1
- package/src/app/components/toolbar/DevOnlyToolbarOpenInEditor.vue +5 -4
- package/src/app/components/toolbar/ToolbarBackground.vue +62 -25
- package/src/app/components/toolbar/ToolbarNewTab.vue +5 -4
- package/src/app/components/toolbar/ToolbarResponsiveSize.vue +15 -15
- package/src/app/components/toolbar/ToolbarTextDirection.vue +5 -4
- package/src/app/components/toolbar/ToolbarTitle.vue +32 -8
- package/src/app/components/tree/StoryGroup.vue +5 -5
- package/src/app/components/tree/StoryList.vue +1 -1
- package/src/app/components/tree/StoryListFolder.vue +6 -6
- package/src/app/components/tree/StoryListItem.vue +6 -6
- package/src/app/index.ts +3 -0
- package/src/app/sandbox.ts +59 -15
- package/src/app/stores/layout.ts +22 -0
- package/src/app/stores/preview-settings.ts +26 -2
- package/src/app/style/floating.pcss +4 -4
- package/src/app/style/main.pcss +2 -2
- package/src/app/style/sandbox.css +1 -1
- package/src/app/types.ts +49 -0
- package/src/app/util/color-scheme.ts +36 -0
- package/src/app/util/const.ts +1 -0
- package/src/app/util/dark.ts +3 -3
- package/src/app/util/keyboard.ts +65 -32
- package/src/app/util/preview-settings.ts +16 -0
- package/src/app/util/tooltip.ts +2 -2
- package/src/bundle-main.js +2 -0
- package/src/shim.d.ts +2 -2
package/src/app/sandbox.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { StoryFile } from './types'
|
|
2
2
|
import { applyState } from '@poveste/shared'
|
|
3
|
+
import { usePreferredDark } from '@vueuse/core'
|
|
3
4
|
import { createPinia } from 'pinia'
|
|
4
5
|
import { files } from 'virtual:$poveste-stories'
|
|
5
6
|
import { computed, createApp, h, onMounted, ref, watch } from 'vue'
|
|
@@ -7,16 +8,44 @@ import { parseQuery } from 'vue-router'
|
|
|
7
8
|
import GenericMountStory from './components/story/GenericMountStory.vue'
|
|
8
9
|
import GenericRenderStory from './components/story/GenericRenderStory.vue'
|
|
9
10
|
import { setupPluginApi } from './plugin.js'
|
|
11
|
+
import { resolvePreviewDark } from './util/color-scheme.js'
|
|
10
12
|
import { povesteConfig } from './util/config.js'
|
|
11
|
-
import { PREVIEW_SETTINGS_SYNC, SANDBOX_READY, STATE_SYNC } from './util/const.js'
|
|
13
|
+
import { PREVIEW_SETTINGS_SYNC, SANDBOX_HEIGHT, SANDBOX_READY, STATE_SYNC } from './util/const.js'
|
|
12
14
|
import { isDark } from './util/dark.js'
|
|
13
15
|
import { mapFile } from './util/mapping'
|
|
14
|
-
import { applyPreviewSettings } from './util/preview-settings.js'
|
|
16
|
+
import { applyPreviewSettings, loadStoredPreviewSettings, receivedSettings } from './util/preview-settings.js'
|
|
15
17
|
import { toRawDeep } from './util/state.js'
|
|
16
18
|
|
|
17
19
|
const query = parseQuery(window.location.search)
|
|
18
20
|
const file = ref<StoryFile>(mapFile(files.find(f => f.id === query.storyId)))
|
|
19
21
|
|
|
22
|
+
// Sandboxes opened on their own (the "open in a new tab" toolbar button) never
|
|
23
|
+
// receive a PREVIEW_SETTINGS_SYNC, and inside the app the first one only lands
|
|
24
|
+
// after the iframe is already visible. Both read the same storage the app
|
|
25
|
+
// writes to, so seed from it and let the sync take over from there.
|
|
26
|
+
const storedSettings = loadStoredPreviewSettings()
|
|
27
|
+
if (storedSettings) {
|
|
28
|
+
applyPreviewSettings(storedSettings)
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// The story preview has its own color scheme, independent from the app chrome.
|
|
32
|
+
// The fallback covers settings stored before the option existed.
|
|
33
|
+
const prefersDark = usePreferredDark()
|
|
34
|
+
const previewDark = computed(() => resolvePreviewDark(receivedSettings.colorScheme, prefersDark.value, isDark.value))
|
|
35
|
+
|
|
36
|
+
watch(previewDark, (value) => {
|
|
37
|
+
if (value) {
|
|
38
|
+
document.documentElement.classList.add(povesteConfig.sandboxDarkClass) // @TODO remove
|
|
39
|
+
document.documentElement.classList.add(povesteConfig.theme.darkClass)
|
|
40
|
+
}
|
|
41
|
+
else {
|
|
42
|
+
document.documentElement.classList.remove(povesteConfig.sandboxDarkClass) // @TODO remove
|
|
43
|
+
document.documentElement.classList.remove(povesteConfig.theme.darkClass)
|
|
44
|
+
}
|
|
45
|
+
}, {
|
|
46
|
+
immediate: true,
|
|
47
|
+
})
|
|
48
|
+
|
|
20
49
|
const app = createApp({
|
|
21
50
|
name: 'SandboxApp',
|
|
22
51
|
|
|
@@ -64,7 +93,7 @@ const app = createApp({
|
|
|
64
93
|
|
|
65
94
|
render() {
|
|
66
95
|
return [
|
|
67
|
-
h('div', { class: '
|
|
96
|
+
h('div', { class: 'ptw-sandbox-hidden' }, [
|
|
68
97
|
h(GenericMountStory, {
|
|
69
98
|
key: file.value.story.id,
|
|
70
99
|
story: file.value.story,
|
|
@@ -83,18 +112,33 @@ const app = createApp({
|
|
|
83
112
|
app.use(createPinia())
|
|
84
113
|
app.mount('#app')
|
|
85
114
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
115
|
+
// Tagging body itself as a story render root puts components teleported to
|
|
116
|
+
// document.body (floating-vue popper, dialogs) inside the user-CSS @scope
|
|
117
|
+
// boundary applied in dev. The custom-controls class opts out of poveste-app
|
|
118
|
+
// source-level rules that add overflow/min-height to the render root.
|
|
119
|
+
document.body.classList.add('__poveste-render-story', '__poveste-render-custom-controls')
|
|
120
|
+
|
|
121
|
+
let pendingFrame: number | null = null
|
|
122
|
+
let lastReportedHeight = -1
|
|
123
|
+
function reportHeight() {
|
|
124
|
+
pendingFrame = null
|
|
125
|
+
const renderRoot = document.querySelector('.__poveste-render-story')
|
|
126
|
+
const h = renderRoot
|
|
127
|
+
? Math.ceil(renderRoot.getBoundingClientRect().height)
|
|
128
|
+
: Math.ceil(document.body.scrollHeight)
|
|
129
|
+
if (h === lastReportedHeight) return
|
|
130
|
+
lastReportedHeight = h
|
|
131
|
+
// Same-origin: parent and iframe are both served by the poveste dev /
|
|
132
|
+
// build server, so scope the target rather than broadcasting.
|
|
133
|
+
window.parent?.postMessage({ type: SANDBOX_HEIGHT, h }, window.location.origin)
|
|
134
|
+
}
|
|
135
|
+
function scheduleReport() {
|
|
136
|
+
if (pendingFrame !== null) return
|
|
137
|
+
pendingFrame = requestAnimationFrame(reportHeight)
|
|
138
|
+
}
|
|
139
|
+
const ro = new ResizeObserver(scheduleReport)
|
|
140
|
+
ro.observe(document.body)
|
|
141
|
+
requestAnimationFrame(scheduleReport)
|
|
98
142
|
|
|
99
143
|
if (import.meta.hot) {
|
|
100
144
|
/* #__PURE__ */ setupPluginApi()
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { useStorage } from '@vueuse/core'
|
|
2
|
+
import { defineStore } from 'pinia'
|
|
3
|
+
|
|
4
|
+
export type StoryOptionsPlacement = 'right' | 'bottom'
|
|
5
|
+
|
|
6
|
+
export interface LayoutSettings {
|
|
7
|
+
storyListVisible: boolean
|
|
8
|
+
storyOptionsVisible: boolean
|
|
9
|
+
storyOptionsPlacement: StoryOptionsPlacement
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export const useLayoutStore = defineStore('layout', () => {
|
|
13
|
+
const settings = useStorage<LayoutSettings>('_poveste-layout-v1', {
|
|
14
|
+
storyListVisible: true,
|
|
15
|
+
storyOptionsVisible: true,
|
|
16
|
+
storyOptionsPlacement: 'right',
|
|
17
|
+
})
|
|
18
|
+
|
|
19
|
+
return {
|
|
20
|
+
settings,
|
|
21
|
+
}
|
|
22
|
+
})
|
|
@@ -1,18 +1,42 @@
|
|
|
1
1
|
import type { PreviewSettings } from '../types'
|
|
2
2
|
import { useStorage } from '@vueuse/core'
|
|
3
3
|
import { defineStore } from 'pinia'
|
|
4
|
+
import { defaultPreviewColorScheme } from '../util/color-scheme'
|
|
5
|
+
import { povesteConfig } from '../util/config'
|
|
6
|
+
import { PREVIEW_SETTINGS_STORAGE_KEY } from '../util/preview-settings'
|
|
4
7
|
|
|
5
8
|
export const usePreviewSettingsStore = defineStore('preview-settings', () => {
|
|
6
|
-
const currentSettings = useStorage<PreviewSettings>(
|
|
9
|
+
const currentSettings = useStorage<PreviewSettings>(PREVIEW_SETTINGS_STORAGE_KEY, {
|
|
7
10
|
responsiveWidth: 720,
|
|
8
11
|
responsiveHeight: null,
|
|
9
12
|
rotate: false,
|
|
10
|
-
backgroundColor: 'transparent',
|
|
13
|
+
backgroundColor: povesteConfig.defaultBackgroundColor ?? 'transparent',
|
|
14
|
+
backgroundColorPicked: false,
|
|
11
15
|
checkerboard: false,
|
|
12
16
|
textDirection: 'ltr',
|
|
17
|
+
colorScheme: defaultPreviewColorScheme,
|
|
13
18
|
})
|
|
14
19
|
|
|
20
|
+
// useStorage honors its default only on first run, so already-stored settings
|
|
21
|
+
// would never see `defaultBackgroundColor` (nor a later change to it). Re-apply
|
|
22
|
+
// it on every load until the user picks a color from the toolbar; the flag lives
|
|
23
|
+
// in the settings object so it travels with the value it describes.
|
|
24
|
+
const configBg = povesteConfig.defaultBackgroundColor
|
|
25
|
+
if (configBg && !currentSettings.value.backgroundColorPicked) {
|
|
26
|
+
currentSettings.value.backgroundColor = configBg
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// Same reason: settings stored before `colorScheme` existed would leave it
|
|
30
|
+
// undefined, so the toolbar would show no active option.
|
|
31
|
+
currentSettings.value.colorScheme ??= defaultPreviewColorScheme
|
|
32
|
+
|
|
33
|
+
function setBackgroundColor(color: string) {
|
|
34
|
+
currentSettings.value.backgroundColor = color
|
|
35
|
+
currentSettings.value.backgroundColorPicked = true
|
|
36
|
+
}
|
|
37
|
+
|
|
15
38
|
return {
|
|
16
39
|
currentSettings,
|
|
40
|
+
setBackgroundColor,
|
|
17
41
|
}
|
|
18
42
|
})
|
|
@@ -5,17 +5,17 @@
|
|
|
5
5
|
}
|
|
6
6
|
|
|
7
7
|
.v-popper--theme-dropdown {
|
|
8
|
-
.
|
|
8
|
+
.ptw-dark & {
|
|
9
9
|
.v-popper__inner {
|
|
10
|
-
@apply
|
|
10
|
+
@apply ptw-bg-gray-700 ptw-border-gray-850 ptw-text-gray-100;
|
|
11
11
|
}
|
|
12
12
|
|
|
13
13
|
.v-popper__arrow-inner {
|
|
14
|
-
@apply
|
|
14
|
+
@apply ptw-border-gray-700;
|
|
15
15
|
}
|
|
16
16
|
|
|
17
17
|
.v-popper__arrow-outer {
|
|
18
|
-
@apply
|
|
18
|
+
@apply ptw-border-gray-850;
|
|
19
19
|
}
|
|
20
20
|
}
|
|
21
21
|
|
package/src/app/style/main.pcss
CHANGED
|
@@ -73,7 +73,7 @@ button {
|
|
|
73
73
|
background: transparent !important;
|
|
74
74
|
}
|
|
75
75
|
|
|
76
|
-
.
|
|
76
|
+
.ptw-prose {
|
|
77
77
|
h1, h2, h3, h4 {
|
|
78
78
|
&:not(:hover) {
|
|
79
79
|
.header-anchor[aria-hidden="true"] {
|
|
@@ -93,7 +93,7 @@ button {
|
|
|
93
93
|
background-repeat: no-repeat;
|
|
94
94
|
background-position: right;
|
|
95
95
|
|
|
96
|
-
.
|
|
96
|
+
.ptw-dark & {
|
|
97
97
|
background-image: linear-gradient(to left, rgba(0, 0, 0, 0.1), rgba(0, 0, 0, 0));
|
|
98
98
|
}
|
|
99
99
|
}
|
package/src/app/types.ts
CHANGED
|
@@ -45,13 +45,62 @@ export type SearchResult = SearchResultBase & ({
|
|
|
45
45
|
onActivate: () => unknown
|
|
46
46
|
})
|
|
47
47
|
|
|
48
|
+
export type SandboxColorScheme = 'auto' | 'light' | 'dark'
|
|
49
|
+
|
|
48
50
|
export interface PreviewSettings {
|
|
51
|
+
/**
|
|
52
|
+
* Width of the responsive preview, in pixels. `null` sizes it to the
|
|
53
|
+
* available space.
|
|
54
|
+
*
|
|
55
|
+
* @default 720
|
|
56
|
+
*/
|
|
49
57
|
responsiveWidth: number
|
|
58
|
+
/**
|
|
59
|
+
* Height of the responsive preview, in pixels. `null` sizes it to the
|
|
60
|
+
* available space.
|
|
61
|
+
*
|
|
62
|
+
* @default null
|
|
63
|
+
*/
|
|
50
64
|
responsiveHeight: number
|
|
65
|
+
/**
|
|
66
|
+
* Swap `responsiveWidth` and `responsiveHeight`, to preview a size in
|
|
67
|
+
* landscape without editing both values.
|
|
68
|
+
*
|
|
69
|
+
* @default false
|
|
70
|
+
*/
|
|
51
71
|
rotate: boolean
|
|
72
|
+
/**
|
|
73
|
+
* Background color of the story preview. Any CSS color, not only the ones
|
|
74
|
+
* from `backgroundPresets`.
|
|
75
|
+
*
|
|
76
|
+
* @default 'transparent'
|
|
77
|
+
*/
|
|
52
78
|
backgroundColor: string
|
|
79
|
+
/**
|
|
80
|
+
* Whether `backgroundColor` came from an explicit toolbar pick rather than
|
|
81
|
+
* from the `defaultBackgroundColor` config option.
|
|
82
|
+
*
|
|
83
|
+
* @default false
|
|
84
|
+
*/
|
|
85
|
+
backgroundColorPicked: boolean
|
|
86
|
+
/**
|
|
87
|
+
* Show a checkerboard pattern behind the story, to spot transparency.
|
|
88
|
+
*
|
|
89
|
+
* @default false
|
|
90
|
+
*/
|
|
53
91
|
checkerboard: boolean
|
|
92
|
+
/**
|
|
93
|
+
* `dir` attribute set on the story preview.
|
|
94
|
+
*
|
|
95
|
+
* @default 'ltr'
|
|
96
|
+
*/
|
|
54
97
|
textDirection: 'ltr' | 'rtl'
|
|
98
|
+
/**
|
|
99
|
+
* Color scheme of the story preview, independent from the app chrome.
|
|
100
|
+
*
|
|
101
|
+
* @default 'auto'
|
|
102
|
+
*/
|
|
103
|
+
colorScheme: SandboxColorScheme
|
|
55
104
|
}
|
|
56
105
|
|
|
57
106
|
declare module 'vue' {
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type { PreviewSettings, SandboxColorScheme } from '../types'
|
|
2
|
+
import { usePreferredDark } from '@vueuse/core'
|
|
3
|
+
import { computed } from 'vue'
|
|
4
|
+
import { povesteConfig } from './config'
|
|
5
|
+
import { isDark } from './dark'
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Color scheme the story preview starts on, before the user picks one in the
|
|
9
|
+
* toolbar. Shared with the app chrome so a book configured as dark starts dark
|
|
10
|
+
* on both sides.
|
|
11
|
+
*/
|
|
12
|
+
export const defaultPreviewColorScheme: SandboxColorScheme = povesteConfig.theme.defaultColorScheme ?? 'auto'
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* The story preview has its own color scheme, independent from the app chrome.
|
|
16
|
+
* `chromeDark` only covers settings that predate the option (and the sandbox
|
|
17
|
+
* boot window), where we keep the old behavior of following the chrome.
|
|
18
|
+
*/
|
|
19
|
+
export function resolvePreviewDark(colorScheme: SandboxColorScheme | undefined, prefersDark: boolean, chromeDark: boolean) {
|
|
20
|
+
switch (colorScheme) {
|
|
21
|
+
case 'light': return false
|
|
22
|
+
case 'dark': return true
|
|
23
|
+
case 'auto': return prefersDark
|
|
24
|
+
default: return chromeDark
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Whether the story preview should be rendered dark, for previews rendered by
|
|
30
|
+
* the app itself. The sandbox resolves the same thing from the settings it
|
|
31
|
+
* receives over `PREVIEW_SETTINGS_SYNC`.
|
|
32
|
+
*/
|
|
33
|
+
export function usePreviewDark(settings: PreviewSettings) {
|
|
34
|
+
const prefersDark = usePreferredDark()
|
|
35
|
+
return computed(() => resolvePreviewDark(settings.colorScheme, prefersDark.value, isDark.value))
|
|
36
|
+
}
|
package/src/app/util/const.ts
CHANGED
|
@@ -2,3 +2,4 @@ export const STATE_SYNC = '__poveste:state-sync'
|
|
|
2
2
|
export const SANDBOX_READY = '__poveste:sandbox-ready'
|
|
3
3
|
export const EVENT_SEND = '__poveste:event'
|
|
4
4
|
export const PREVIEW_SETTINGS_SYNC = '__poveste:preview-settings-sync'
|
|
5
|
+
export const SANDBOX_HEIGHT = '__poveste:sandbox-height'
|
package/src/app/util/dark.ts
CHANGED
|
@@ -3,7 +3,7 @@ import { watch } from 'vue'
|
|
|
3
3
|
import { povesteConfig } from './config.js'
|
|
4
4
|
|
|
5
5
|
export const isDark = useDark({
|
|
6
|
-
valueDark: '
|
|
6
|
+
valueDark: 'ptw-dark',
|
|
7
7
|
initialValue: povesteConfig.theme.defaultColorScheme,
|
|
8
8
|
storageKey: 'poveste-color-scheme',
|
|
9
9
|
storage: povesteConfig.theme.storeColorScheme ? localStorage : sessionStorage,
|
|
@@ -11,7 +11,7 @@ export const isDark = useDark({
|
|
|
11
11
|
export const toggleDark = useToggle(isDark)
|
|
12
12
|
|
|
13
13
|
function applyDarkToControls() {
|
|
14
|
-
window.
|
|
14
|
+
window.__pvt_controls_dark?.forEach((ref) => {
|
|
15
15
|
ref.value = isDark.value
|
|
16
16
|
})
|
|
17
17
|
}
|
|
@@ -22,6 +22,6 @@ watch(isDark, () => {
|
|
|
22
22
|
immediate: true,
|
|
23
23
|
})
|
|
24
24
|
|
|
25
|
-
window.
|
|
25
|
+
window.__pvt_controls_dark_ready = () => {
|
|
26
26
|
applyDarkToControls()
|
|
27
27
|
}
|
package/src/app/util/keyboard.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { Ref } from 'vue'
|
|
2
2
|
import { useEventListener } from '@vueuse/core'
|
|
3
|
-
import { isRef } from 'vue'
|
|
3
|
+
import { isRef, shallowRef } from 'vue'
|
|
4
4
|
import { isMac } from './env.js'
|
|
5
5
|
|
|
6
6
|
export type KeyboardShortcut = string[]
|
|
@@ -11,14 +11,6 @@ export interface KeyboardShortcutOptions {
|
|
|
11
11
|
event?: 'keyup' | 'keydown' | 'keypress'
|
|
12
12
|
}
|
|
13
13
|
|
|
14
|
-
export function onKeyboardShortcut(shortcut: KeyboardShortcut | Ref<KeyboardShortcut>, handler: KeyboardHandler, options: KeyboardShortcutOptions = {}) {
|
|
15
|
-
useEventListener(options.event ?? 'keydown', (event) => {
|
|
16
|
-
if (isMatchingShortcut(isRef(shortcut) ? shortcut.value : shortcut)) {
|
|
17
|
-
handler(event)
|
|
18
|
-
}
|
|
19
|
-
})
|
|
20
|
-
}
|
|
21
|
-
|
|
22
14
|
const modifiers: { [i: string]: { key: string, pressed: boolean } } = {
|
|
23
15
|
ctrl: { key: 'Control', pressed: false },
|
|
24
16
|
alt: { key: 'Alt', pressed: false },
|
|
@@ -28,37 +20,78 @@ const modifiers: { [i: string]: { key: string, pressed: boolean } } = {
|
|
|
28
20
|
|
|
29
21
|
const pressedKeys = new Set<string>()
|
|
30
22
|
|
|
31
|
-
|
|
32
|
-
for (const i in modifiers) {
|
|
33
|
-
const mod = modifiers[i]
|
|
34
|
-
if (mod.key === event.key) {
|
|
35
|
-
mod.pressed = true
|
|
36
|
-
return
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
pressedKeys.add(event.key.toLocaleLowerCase())
|
|
40
|
-
})
|
|
23
|
+
const trackedWindows = shallowRef<Window[]>([])
|
|
41
24
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
pressedKeys.clear()
|
|
25
|
+
function bindTracking(target: Window) {
|
|
26
|
+
const onKeydown = (event: KeyboardEvent) => {
|
|
45
27
|
for (const i in modifiers) {
|
|
46
28
|
const mod = modifiers[i]
|
|
47
29
|
if (mod.key === event.key) {
|
|
48
|
-
mod.pressed =
|
|
49
|
-
|
|
30
|
+
mod.pressed = true
|
|
31
|
+
return
|
|
50
32
|
}
|
|
51
33
|
}
|
|
52
|
-
|
|
53
|
-
}
|
|
34
|
+
pressedKeys.add(event.key.toLocaleLowerCase())
|
|
35
|
+
}
|
|
36
|
+
const onKeyup = (event: KeyboardEvent) => {
|
|
37
|
+
requestAnimationFrame(() => {
|
|
38
|
+
pressedKeys.clear()
|
|
39
|
+
for (const i in modifiers) {
|
|
40
|
+
const mod = modifiers[i]
|
|
41
|
+
if (mod.key === event.key) {
|
|
42
|
+
mod.pressed = false
|
|
43
|
+
break
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
})
|
|
47
|
+
}
|
|
48
|
+
const onBlur = () => {
|
|
49
|
+
pressedKeys.clear()
|
|
50
|
+
for (const i in modifiers) {
|
|
51
|
+
modifiers[i].pressed = false
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
target.addEventListener('keydown', onKeydown)
|
|
55
|
+
target.addEventListener('keyup', onKeyup)
|
|
56
|
+
target.addEventListener('blur', onBlur)
|
|
57
|
+
return () => {
|
|
58
|
+
target.removeEventListener('keydown', onKeydown)
|
|
59
|
+
target.removeEventListener('keyup', onKeyup)
|
|
60
|
+
target.removeEventListener('blur', onBlur)
|
|
61
|
+
}
|
|
62
|
+
}
|
|
54
63
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
64
|
+
/**
|
|
65
|
+
* Opt an additional window (typically a sandbox iframe `contentWindow`) into
|
|
66
|
+
* shortcut tracking, so keys pressed while it has focus still reach the app.
|
|
67
|
+
*/
|
|
68
|
+
export function trackWindow(target: Window): () => void {
|
|
69
|
+
if (trackedWindows.value.includes(target)) {
|
|
70
|
+
return () => {}
|
|
71
|
+
}
|
|
72
|
+
const cleanup = bindTracking(target)
|
|
73
|
+
trackedWindows.value = [...trackedWindows.value, target]
|
|
74
|
+
return () => {
|
|
75
|
+
cleanup()
|
|
76
|
+
trackedWindows.value = trackedWindows.value.filter(w => w !== target)
|
|
60
77
|
}
|
|
61
|
-
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
trackWindow(window)
|
|
81
|
+
|
|
82
|
+
export function onKeyboardShortcut(shortcut: KeyboardShortcut | Ref<KeyboardShortcut>, handler: KeyboardHandler, options: KeyboardShortcutOptions = {}) {
|
|
83
|
+
useEventListener(trackedWindows, options.event ?? 'keydown', (event: KeyboardEvent) => {
|
|
84
|
+
// Sync modifier state from the event so a blur-clear (e.g. focusing an
|
|
85
|
+
// iframe while holding a modifier) doesn't drop the shortcut.
|
|
86
|
+
modifiers.ctrl.pressed = event.ctrlKey
|
|
87
|
+
modifiers.alt.pressed = event.altKey
|
|
88
|
+
modifiers.shift.pressed = event.shiftKey
|
|
89
|
+
modifiers.meta.pressed = event.metaKey
|
|
90
|
+
if (isMatchingShortcut(isRef(shortcut) ? shortcut.value : shortcut)) {
|
|
91
|
+
handler(event)
|
|
92
|
+
}
|
|
93
|
+
})
|
|
94
|
+
}
|
|
62
95
|
|
|
63
96
|
function isMatchingShortcut(shortcut: KeyboardShortcut): boolean {
|
|
64
97
|
for (const combination of shortcut) {
|
|
@@ -2,8 +2,24 @@ import type { PreviewSettings } from '../types'
|
|
|
2
2
|
import { reactive } from 'vue'
|
|
3
3
|
import { povesteConfig } from './config'
|
|
4
4
|
|
|
5
|
+
export const PREVIEW_SETTINGS_STORAGE_KEY = '_poveste-sandbox-settings-v3'
|
|
6
|
+
|
|
5
7
|
export const receivedSettings = reactive<PreviewSettings>({} as PreviewSettings)
|
|
6
8
|
|
|
9
|
+
/**
|
|
10
|
+
* Read the settings the app persists, for sandboxes that can't wait for (or
|
|
11
|
+
* will never get) a `PREVIEW_SETTINGS_SYNC` message.
|
|
12
|
+
*/
|
|
13
|
+
export function loadStoredPreviewSettings(): PreviewSettings | null {
|
|
14
|
+
try {
|
|
15
|
+
const raw = localStorage.getItem(PREVIEW_SETTINGS_STORAGE_KEY)
|
|
16
|
+
return raw ? JSON.parse(raw) : null
|
|
17
|
+
}
|
|
18
|
+
catch {
|
|
19
|
+
return null
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
7
23
|
export function applyPreviewSettings(settings: PreviewSettings) {
|
|
8
24
|
Object.assign(receivedSettings, settings)
|
|
9
25
|
|
package/src/app/util/tooltip.ts
CHANGED
|
@@ -3,11 +3,11 @@ import { formatKey } from './keyboard.js'
|
|
|
3
3
|
|
|
4
4
|
export function makeTooltip(descriptionHtml: string, keyboardShortcut: (options: { isMac: boolean }) => string) {
|
|
5
5
|
return {
|
|
6
|
-
content: `<div>${descriptionHtml}</div><div class="
|
|
6
|
+
content: `<div>${descriptionHtml}</div><div class="ptw-flex ptw-items-center ptw-gap-1 ptw-mt-2 ptw-text-sm">${genKeyboardShortcutHtml(keyboardShortcut({ isMac }))}</div>`,
|
|
7
7
|
html: true,
|
|
8
8
|
}
|
|
9
9
|
}
|
|
10
10
|
|
|
11
11
|
function genKeyboardShortcutHtml(shortcut: string) {
|
|
12
|
-
return `<span class="
|
|
12
|
+
return `<span class="ptw-border ptw-border-gray-600 ptw-px-1 ptw-rounded-sm ptw-text-gray-400">${shortcut.split('+').map(k => formatKey(k.trim())).join(' ')}</span>`
|
|
13
13
|
}
|
package/src/bundle-main.js
CHANGED
package/src/shim.d.ts
CHANGED