feat(layout): toggle between source, macroplan, and split panes
The chosen layout is kept in the URL (?view=) so it's shareable and bookmarkable rather than persisted. PNG export is disabled in source-only mode since the rendered node it captures isn't mounted then.
This commit is contained in:
65
src/App.vue
65
src/App.vue
@@ -1,5 +1,5 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, computed } from "vue"
|
||||
import { ref, computed, watch } from "vue"
|
||||
import { useMacroplan } from "./composables/useMacroplan"
|
||||
import { usePngExport, exportFilename } from "./composables/usePngExport"
|
||||
import { sourceFilename, downloadSource } from "./composables/useSourceExport"
|
||||
@@ -13,6 +13,28 @@ const { busy, toast, copyPng, downloadPng } = usePngExport()
|
||||
const exportRoot = ref<HTMLElement>()
|
||||
const confirmingDelete = ref(false)
|
||||
|
||||
// Which panes are visible: the Source editor, the rendered Macroplan, or both.
|
||||
// Kept in the URL (?view=…) so a layout is shareable/bookmarkable, not persisted.
|
||||
type ViewMode = "source" | "split" | "view"
|
||||
|
||||
function readViewMode(): ViewMode {
|
||||
const v = new URLSearchParams(location.search).get("view")
|
||||
return v === "source" || v === "view" ? v : "split"
|
||||
}
|
||||
|
||||
const mode = ref<ViewMode>(readViewMode())
|
||||
const showSource = computed(() => mode.value !== "view")
|
||||
const showView = computed(() => mode.value !== "source")
|
||||
|
||||
// Reflect the choice in the URL without stacking a history entry per toggle.
|
||||
// "split" is the default, so drop the param to keep the URL clean.
|
||||
watch(mode, (m) => {
|
||||
const url = new URL(location.href)
|
||||
if (m === "split") url.searchParams.delete("view")
|
||||
else url.searchParams.set("view", m)
|
||||
history.replaceState(history.state, "", url)
|
||||
})
|
||||
|
||||
const activeName = computed(
|
||||
() => plans.value.find((p) => p.id === activeId.value)?.name ?? "Untitled",
|
||||
)
|
||||
@@ -40,9 +62,38 @@ function confirmDelete() {
|
||||
@download="downloadToml"
|
||||
/>
|
||||
</div>
|
||||
<div class="join" role="group" aria-label="Choose which panes to show">
|
||||
<button
|
||||
class="btn btn-sm join-item"
|
||||
:class="{ 'btn-active': mode === 'source' }"
|
||||
:aria-pressed="mode === 'source'"
|
||||
title="Show only the TOML source"
|
||||
@click="mode = 'source'"
|
||||
>
|
||||
Source
|
||||
</button>
|
||||
<button
|
||||
class="btn btn-sm join-item"
|
||||
:class="{ 'btn-active': mode === 'split' }"
|
||||
:aria-pressed="mode === 'split'"
|
||||
title="Show the source and the Macroplan side by side"
|
||||
@click="mode = 'split'"
|
||||
>
|
||||
Split
|
||||
</button>
|
||||
<button
|
||||
class="btn btn-sm join-item"
|
||||
:class="{ 'btn-active': mode === 'view' }"
|
||||
:aria-pressed="mode === 'view'"
|
||||
title="Show only the rendered Macroplan"
|
||||
@click="mode = 'view'"
|
||||
>
|
||||
Macroplan
|
||||
</button>
|
||||
</div>
|
||||
<button
|
||||
class="btn btn-ghost btn-sm"
|
||||
:disabled="!plan || busy"
|
||||
:disabled="!plan || busy || !showView"
|
||||
title="Copy the rendered plan to the clipboard as a PNG"
|
||||
@click="copyPng(exportRoot)"
|
||||
>
|
||||
@@ -51,7 +102,7 @@ function confirmDelete() {
|
||||
</button>
|
||||
<button
|
||||
class="btn btn-ghost btn-sm"
|
||||
:disabled="!plan || busy"
|
||||
:disabled="!plan || busy || !showView"
|
||||
title="Download the rendered plan as a PNG"
|
||||
@click="downloadPng(exportRoot, exportFilename(plan?.title ?? ''))"
|
||||
>
|
||||
@@ -69,7 +120,11 @@ function confirmDelete() {
|
||||
|
||||
<main class="flex min-h-0 flex-1 flex-col md:flex-row">
|
||||
<section
|
||||
class="flex min-h-0 flex-col border-base-300 max-md:h-2/5 max-md:border-b md:w-1/3 md:max-w-md md:border-r"
|
||||
v-if="showSource"
|
||||
class="flex min-h-0 flex-col border-base-300"
|
||||
:class="
|
||||
showView ? 'max-md:h-2/5 max-md:border-b md:w-1/3 md:max-w-md md:border-r' : 'flex-1'
|
||||
"
|
||||
>
|
||||
<div class="px-4 py-2 text-xs font-semibold uppercase tracking-wide text-base-content/50">
|
||||
Source (TOML)
|
||||
@@ -77,7 +132,7 @@ function confirmDelete() {
|
||||
<PlanEditor v-model="source" :error="error" class="min-h-0 flex-1" />
|
||||
</section>
|
||||
|
||||
<section class="min-h-0 flex-1 overflow-auto p-4">
|
||||
<section v-if="showView" class="min-h-0 flex-1 overflow-auto p-4">
|
||||
<div v-if="plan" ref="exportRoot" class="export-root">
|
||||
<h2 class="mb-3 text-sm font-semibold text-base-content/70">{{ plan.title }}</h2>
|
||||
<MacroplanGrid :plan="plan" />
|
||||
|
||||
Reference in New Issue
Block a user