Improve mobile navigation and responsive layout
All checks were successful
Build Authoring Service / build (push) Successful in 9s
All checks were successful
Build Authoring Service / build (push) Successful in 9s
This commit is contained in:
@@ -216,7 +216,36 @@ APP_HTML = r"""<!doctype html>
|
||||
.queue-item span { color: #b9a789; overflow-wrap: anywhere; }
|
||||
pre { white-space: pre-wrap; overflow: auto; max-height: 280px; background: #0d0c0a; color: #ecdcbf; border: 1px solid rgba(217, 190, 124, 0.24); padding: 15px; border-radius: 8px; font-size: 12px; box-shadow: var(--shadow); }
|
||||
@media (max-width: 980px) { .content-layout.previewing { grid-template-columns: 1fr; } }
|
||||
@media (max-width: 860px) { .app { grid-template-columns: 1fr; } aside, main { max-height: none; } aside { border-right: 0; border-bottom: 1px solid var(--line); } .grid { grid-template-columns: 1fr; } .link-picker { grid-template-columns: 1fr; } }
|
||||
@media (max-width: 860px) {
|
||||
body { min-width: 0; }
|
||||
.app { display: block; min-height: 100dvh; }
|
||||
aside, main { max-height: none; }
|
||||
aside {
|
||||
border-right: 0;
|
||||
border-bottom: 1px solid var(--line);
|
||||
padding: 14px;
|
||||
box-shadow: 0 14px 36px rgba(0, 0, 0, 0.22);
|
||||
}
|
||||
main { padding: 16px 12px 28px; }
|
||||
aside .topbar { position: sticky; top: 0; z-index: 4; margin: -14px -14px 12px; padding: 12px 14px; background: rgba(24, 21, 17, 0.96); backdrop-filter: blur(14px); }
|
||||
aside .page-list { max-height: min(42dvh, 430px); overflow: auto; overscroll-behavior: contain; padding-right: 3px; }
|
||||
.grid, .link-picker { grid-template-columns: 1fr; }
|
||||
form { padding: 14px; gap: 14px; }
|
||||
form > .topbar { align-items: flex-start; flex-direction: column; }
|
||||
main h1 { font-size: clamp(25px, 8vw, 31px); }
|
||||
button { min-height: 44px; }
|
||||
input, textarea, select { min-height: 44px; font-size: 16px; }
|
||||
textarea { min-height: 42dvh; }
|
||||
.toolbar, .actions { gap: 8px; }
|
||||
.toolbar button, .actions button { flex: 1 1 132px; }
|
||||
.preview-panel { min-height: 36dvh; padding: 15px; }
|
||||
}
|
||||
@media (max-width: 420px) {
|
||||
.quick-link { margin-right: 10px; }
|
||||
.tree-children { margin-left: 7px; padding-left: 7px; }
|
||||
form { padding: 12px; }
|
||||
.toolbar button, .actions button { flex-basis: 100%; }
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
@@ -1704,6 +1733,7 @@ HIDDEN_APP_HTML = r"""<!doctype html>
|
||||
font-size: 13px;
|
||||
line-height: 1.4;
|
||||
}
|
||||
.mobile-nav { display: none; }
|
||||
.hidden { display: none !important; }
|
||||
@keyframes breathe {
|
||||
0%, 100% { opacity: 0.72; }
|
||||
@@ -1821,17 +1851,158 @@ HIDDEN_APP_HTML = r"""<!doctype html>
|
||||
.map-head .actions { justify-content: flex-start; max-width: none; }
|
||||
}
|
||||
@media (max-width: 720px) {
|
||||
.map-head { position: static; padding: 16px; display: grid; }
|
||||
#graph { top: 128px; height: calc(100% - 128px); }
|
||||
body { overflow: hidden; min-width: 0; }
|
||||
button { min-height: 44px; }
|
||||
input, textarea, select { min-height: 44px; font-size: 16px; }
|
||||
.observatory {
|
||||
display: block;
|
||||
height: 100dvh;
|
||||
min-height: 0;
|
||||
padding-bottom: calc(62px + env(safe-area-inset-bottom));
|
||||
}
|
||||
.left, .map, .drawer {
|
||||
display: none;
|
||||
width: 100%;
|
||||
height: calc(100dvh - 62px - env(safe-area-inset-bottom));
|
||||
max-height: none;
|
||||
border: 0;
|
||||
}
|
||||
.observatory[data-mobile-view="library"] .left,
|
||||
.observatory[data-mobile-view="sky"] .map,
|
||||
.observatory[data-mobile-view="editor"] .drawer { display: block; }
|
||||
.left, .drawer {
|
||||
overflow-y: auto;
|
||||
overscroll-behavior: contain;
|
||||
padding: 14px 12px 28px;
|
||||
}
|
||||
.left > h1 { margin-top: 0; font-size: 28px; }
|
||||
.drawer { border-left: 0; }
|
||||
.mobile-nav {
|
||||
position: fixed;
|
||||
z-index: 60;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
height: calc(62px + env(safe-area-inset-bottom));
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
gap: 6px;
|
||||
padding: 7px 8px calc(7px + env(safe-area-inset-bottom));
|
||||
border-top: 1px solid var(--line-strong);
|
||||
background: rgba(15, 13, 11, 0.96);
|
||||
backdrop-filter: blur(18px);
|
||||
box-shadow: 0 -12px 34px rgba(0, 0, 0, 0.34);
|
||||
}
|
||||
.mobile-nav button {
|
||||
min-width: 0;
|
||||
min-height: 48px;
|
||||
display: grid;
|
||||
place-items: center;
|
||||
padding: 4px 8px;
|
||||
border-color: transparent;
|
||||
color: var(--muted);
|
||||
font-size: 12px;
|
||||
font-weight: 850;
|
||||
}
|
||||
.mobile-nav button::before { display: block; font-size: 17px; line-height: 1; }
|
||||
.mobile-nav button[data-mobile-view="library"]::before { content: "☷"; }
|
||||
.mobile-nav button[data-mobile-view="sky"]::before { content: "✦"; }
|
||||
.mobile-nav button[data-mobile-view="editor"]::before { content: "✎"; }
|
||||
.mobile-nav button.active {
|
||||
color: #ffe2a6;
|
||||
border-color: rgba(211, 166, 77, 0.5);
|
||||
background: rgba(211, 166, 77, 0.16);
|
||||
}
|
||||
.map { position: relative; min-height: 0; overflow: hidden; }
|
||||
.map-head {
|
||||
position: static;
|
||||
height: 136px;
|
||||
padding: 12px;
|
||||
display: grid;
|
||||
gap: 8px;
|
||||
overflow: hidden;
|
||||
background: rgba(15, 13, 11, 0.86);
|
||||
border-bottom: 1px solid var(--line);
|
||||
}
|
||||
.map-head h1 { margin: 0; font-size: 22px; }
|
||||
.map-head .subtle { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
|
||||
.map-head .actions {
|
||||
flex-wrap: nowrap;
|
||||
justify-content: flex-start;
|
||||
max-width: none;
|
||||
overflow-x: auto;
|
||||
overscroll-behavior-x: contain;
|
||||
padding-bottom: 4px;
|
||||
scrollbar-width: thin;
|
||||
}
|
||||
.map-head .actions button { flex: 0 0 auto; }
|
||||
#graph { top: 136px; height: calc(100% - 136px); }
|
||||
.breadcrumb {
|
||||
left: 10px;
|
||||
top: 146px;
|
||||
max-width: calc(100% - 20px);
|
||||
flex-wrap: nowrap;
|
||||
overflow-x: auto;
|
||||
white-space: nowrap;
|
||||
padding: 6px;
|
||||
}
|
||||
.breadcrumb button { min-height: 36px; }
|
||||
.active-state {
|
||||
left: 10px;
|
||||
top: 198px;
|
||||
max-width: calc(100% - 20px);
|
||||
max-height: 92px;
|
||||
flex-wrap: nowrap;
|
||||
overflow: auto;
|
||||
white-space: nowrap;
|
||||
padding: 6px;
|
||||
}
|
||||
.minimap { right: 10px; bottom: 10px; width: 118px; }
|
||||
.minimap svg { height: 66px; }
|
||||
.map-note {
|
||||
left: 10px;
|
||||
right: 138px;
|
||||
bottom: 10px;
|
||||
max-width: none;
|
||||
max-height: 72px;
|
||||
overflow: auto;
|
||||
padding: 8px 9px;
|
||||
font-size: 12px;
|
||||
}
|
||||
.context-menu { left: 10px !important; right: 10px; bottom: calc(72px + env(safe-area-inset-bottom)); top: auto !important; min-width: 0; }
|
||||
.workspace-switcher, .editor-modebar { position: sticky; top: -14px; z-index: 5; padding: 10px 0; background: rgba(19, 16, 13, 0.96); backdrop-filter: blur(14px); }
|
||||
.modebar { display: grid; grid-template-columns: 1fr; }
|
||||
.filters { grid-template-columns: 1fr 1fr; }
|
||||
.filters #search { grid-column: 1 / -1; }
|
||||
.left > .actions button { flex: 1 1 120px; }
|
||||
.editor-grid { grid-template-columns: 1fr; }
|
||||
.appearance-grid { grid-template-columns: 1fr; }
|
||||
.story-flow-item { grid-template-columns: 30px minmax(0, 1fr); }
|
||||
.story-flow-item > :last-child { grid-column: 1 / -1; }
|
||||
.panel { padding: 11px; box-shadow: 0 14px 42px rgba(0, 0, 0, 0.24); }
|
||||
.panel + .panel { margin-top: 10px; }
|
||||
.actions { gap: 8px; }
|
||||
.actions button { flex: 1 1 128px; }
|
||||
.reader { padding: 18px; }
|
||||
.reader-head, .reader-foot { align-items: stretch; flex-direction: column; }
|
||||
.reader-stage { min-height: 0; overflow: auto; }
|
||||
}
|
||||
@media (max-width: 390px) {
|
||||
.filters { grid-template-columns: 1fr; }
|
||||
.filters #search { grid-column: auto; }
|
||||
.workspace-switcher, .editor-modebar { grid-template-columns: 1fr; }
|
||||
.map-note { right: 10px; }
|
||||
.minimap { display: none; }
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="observatory">
|
||||
<div class="observatory" data-mobile-view="sky">
|
||||
<nav class="mobile-nav" aria-label="Observatory views">
|
||||
<button type="button" data-mobile-view="library">Library</button>
|
||||
<button type="button" data-mobile-view="sky" class="active" aria-current="page">Sky</button>
|
||||
<button type="button" data-mobile-view="editor">Editor</button>
|
||||
</nav>
|
||||
<aside class="left">
|
||||
<h1>Memory Observatory</h1>
|
||||
<div class="subtle">A constellation map for the hidden soul of the website.</div>
|
||||
@@ -2110,6 +2281,19 @@ HIDDEN_APP_HTML = r"""<!doctype html>
|
||||
const arcFields = ["arcTitle","arcDomain","arcVisibility","arcTone","arcThemes","arcSummary","arcOrgWeb"];
|
||||
const chapterFields = ["chapterTitle","chapterTimeframe","chapterTone","chapterPurpose","chapterStoryDraft"];
|
||||
const $ = (id) => document.getElementById(id);
|
||||
const mobileViewport = window.matchMedia("(max-width: 720px)");
|
||||
function setMobileView(view) {
|
||||
const observatory = document.querySelector(".observatory");
|
||||
if (!observatory || !["library", "sky", "editor"].includes(view)) return;
|
||||
observatory.dataset.mobileView = view;
|
||||
document.querySelectorAll(".mobile-nav [data-mobile-view]").forEach((button) => {
|
||||
const active = button.dataset.mobileView === view;
|
||||
button.classList.toggle("active", active);
|
||||
if (active) button.setAttribute("aria-current", "page");
|
||||
else button.removeAttribute("aria-current");
|
||||
});
|
||||
if (view === "sky") window.requestAnimationFrame(() => applyCamera());
|
||||
}
|
||||
const toneColors = { warm: "#d3a64d", funny: "#7fb089", nostalgic: "#d06b78", wise: "#75a9bd", strange: "#a58ac9", soft: "#e6bd8c", hopeful: "#9fcf9f", protective: "#d98f71", melancholy: "#8fa7c6" };
|
||||
const appearanceLabels = {
|
||||
observatory: "Observatory stars",
|
||||
@@ -3384,7 +3568,7 @@ HIDDEN_APP_HTML = r"""<!doctype html>
|
||||
node.addEventListener("contextmenu", (event) => showContextMenu(event, node.dataset.id));
|
||||
});
|
||||
graph.querySelectorAll(".memory-node").forEach((node) => {
|
||||
node.addEventListener("click", () => selectNode(node.dataset.id));
|
||||
node.addEventListener("click", () => selectNode(node.dataset.id, { revealEditor: true }));
|
||||
node.addEventListener("dblclick", () => zoomToNode(node.dataset.id));
|
||||
node.addEventListener("pointerdown", (event) => beginNodeDrag(event, node.dataset.id));
|
||||
node.addEventListener("contextmenu", (event) => showContextMenu(event, node.dataset.id));
|
||||
@@ -3660,6 +3844,7 @@ HIDDEN_APP_HTML = r"""<!doctype html>
|
||||
if (options.keepEditorMode === "story") setEditorMode("story", "Story route is active. The selected memory is highlighted inside the sequence.");
|
||||
renderStoryBuilder();
|
||||
render();
|
||||
if (options.revealEditor && mobileViewport.matches) setMobileView("editor");
|
||||
}
|
||||
function relationForEvent(event) {
|
||||
return { key: "stories", kind: "story-path", label: "Add to story route" };
|
||||
@@ -4590,8 +4775,8 @@ HIDDEN_APP_HTML = r"""<!doctype html>
|
||||
render();
|
||||
});
|
||||
});
|
||||
$("newBtn").onclick = () => newEntry();
|
||||
$("newStoryBtn").onclick = () => newStory();
|
||||
$("newBtn").onclick = () => { newEntry(); if (mobileViewport.matches) setMobileView("editor"); };
|
||||
$("newStoryBtn").onclick = () => { newStory(); if (mobileViewport.matches) setMobileView("editor"); };
|
||||
$("newArcBtn").onclick = newArc;
|
||||
$("newChapterBtn").onclick = newChapter;
|
||||
$("deleteArcBtn").onclick = deleteArc;
|
||||
@@ -4633,6 +4818,7 @@ HIDDEN_APP_HTML = r"""<!doctype html>
|
||||
render();
|
||||
}));
|
||||
$("workspaceSwitcher").querySelectorAll("[data-workspace]").forEach((button) => button.addEventListener("click", () => setWorkspace(button.dataset.workspace)));
|
||||
document.querySelectorAll(".mobile-nav [data-mobile-view]").forEach((button) => button.addEventListener("click", () => setMobileView(button.dataset.mobileView)));
|
||||
$("editorModebar").querySelectorAll("[data-editor-mode]").forEach((button) => button.addEventListener("click", () => {
|
||||
if (button.dataset.editorMode === "story") {
|
||||
setEditorMode("story");
|
||||
|
||||
Reference in New Issue
Block a user