mirror of
https://github.com/grub-basket/SP.git
synced 2026-07-22 07:46:27 +00:00
0.183.4: mobile edit/split modal overhaul + pop-out fixes
Tabbed editor on phones, top-anchored sheet with the editor filling and the action bar staying above the keyboard, slimmer controls, copy button above the text, Pop-out/Advanced labels, full-width pop-out tab (editor no longer collapses), and a larger composer full-screen button.
This commit is contained in:
parent
b6f593e6b2
commit
8850970bb3
6 changed files with 146 additions and 42 deletions
2
main.js
2
main.js
File diff suppressed because one or more lines are too long
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"id": "stashpad",
|
||||
"name": "Stashpad",
|
||||
"version": "0.181.0",
|
||||
"version": "0.183.4",
|
||||
"minAppVersion": "1.13.0",
|
||||
"description": "A chat-style, nested-notes workspace: rapid capture, outliner navigation, fast search, tasks, and per-folder templates, with one-click Open Knowledge Format (OKF) export for LLMs and agents.",
|
||||
"author": "Human",
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "stashpad-obsidian",
|
||||
"version": "0.181.0",
|
||||
"version": "0.183.4",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "node esbuild.config.mjs",
|
||||
|
|
|
|||
30
release-notes/0.183.4.md
Normal file
30
release-notes/0.183.4.md
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
# 0.183.4
|
||||
|
||||
A focused pass on the mobile edit / split experience.
|
||||
|
||||
## Mobile edit & split modal
|
||||
|
||||
- **Tabbed editor.** On a phone, the Original / Changes / Your-edit sections now
|
||||
share one space as tabs (one visible at a time) instead of stacking, so the
|
||||
modal stays compact.
|
||||
- **Top-anchored sheet.** The modal anchors to the top of the screen (clear of the
|
||||
notch / Dynamic Island) with a consistent height, and the editor fills it and
|
||||
scrolls — the action bar stays put above the keyboard instead of being pushed
|
||||
off or lifted/distorted when the keyboard opens.
|
||||
- **Slimmer, cleaner controls.** Compact Edit/Split toggle, tabs, and buttons; the
|
||||
bottom action bar reads as part of the modal (no stray black box); the pop-out
|
||||
and Obsidian-editor buttons are labelled **"Pop-out"** and **"Advanced"**.
|
||||
- **Copy button** for the Original text moved to a small button above the text
|
||||
(it used to overlay the text's right edge).
|
||||
|
||||
## Pop-out tab
|
||||
|
||||
- Opening the editor **in its own tab** now uses the **full width** of the tab, the
|
||||
editor fills the leaf (it no longer collapses to a sliver when the keyboard is
|
||||
up), the content clears Obsidian's bottom toolbar, and all the action buttons
|
||||
stay reachable.
|
||||
|
||||
## Composer
|
||||
|
||||
- The composer's **full-screen button** (open your draft in the roomy in-app
|
||||
editor) is a bit larger and easier to tap.
|
||||
|
|
@ -652,7 +652,7 @@ export class NoteWorkbench {
|
|||
if (!this.cb.popOut) return;
|
||||
const pop = actions.createEl("button", { cls: "stashpad-split-popout-btn" });
|
||||
setIcon(pop.createSpan({ cls: "stashpad-split-popout-icon" }), "maximize-2");
|
||||
pop.createSpan({ text: "Open in a tab" });
|
||||
pop.createSpan({ text: Platform.isMobile ? "Pop-out" : "Open in a tab" });
|
||||
pop.setAttr("aria-label", "Open in a full tab");
|
||||
pop.onmousedown = (e) => e.preventDefault();
|
||||
pop.onclick = () => this.cb.popOut!(this.getState());
|
||||
|
|
@ -663,7 +663,7 @@ export class NoteWorkbench {
|
|||
if (!this.cb.onOpenExternal) return;
|
||||
const b = actions.createEl("button", { cls: "stashpad-split-popout-btn" });
|
||||
setIcon(b.createSpan({ cls: "stashpad-split-popout-icon" }), "pencil");
|
||||
b.createSpan({ text: "Obsidian editor" });
|
||||
b.createSpan({ text: Platform.isMobile ? "Advanced" : "Obsidian editor" });
|
||||
b.setAttr("aria-label", "Open in a full Obsidian editor tab");
|
||||
b.onmousedown = (e) => e.preventDefault();
|
||||
b.onclick = () => { this.cb.onOpenExternal!(); this.cb.close(); };
|
||||
|
|
@ -980,10 +980,12 @@ export class NoteWorkbench {
|
|||
ta.readOnly = false;
|
||||
this.cursorTextarea = ta;
|
||||
|
||||
// Original (read-only) — copy button lives inside the panel (top-right).
|
||||
// Original (read-only). 0.183.3: copy button in a right-aligned bar ABOVE the
|
||||
// text (it used to overlay the text's right edge as a full-height strip).
|
||||
const origPanel = bodyHost.createDiv({ cls: "stashpad-split-tabpanel" });
|
||||
const origCopyBar = origPanel.createDiv({ cls: "stashpad-split-tab-copybar" });
|
||||
origCopyBar.appendChild(this.makeCopyButton(() => this.body, "Copy the original text", "stashpad-split-copy-btn stashpad-split-tab-copy"));
|
||||
origPanel.createDiv({ cls: "stashpad-split-panel-body", text: this.body });
|
||||
origPanel.appendChild(this.makeCopyButton(() => this.body, "Copy the original text", "stashpad-split-copy-btn stashpad-split-tab-copy"));
|
||||
|
||||
// Changes (word diff).
|
||||
const changesPanel = bodyHost.createDiv({ cls: "stashpad-split-tabpanel" });
|
||||
|
|
@ -1027,26 +1029,20 @@ export class NoteWorkbench {
|
|||
if (!edited && (active === "orig" || active === "changes")) showTab("edit");
|
||||
};
|
||||
|
||||
// Firm height cap so the editor scrolls instead of pushing the action bar
|
||||
// past the keyboard (fewer lines than desktop — space is at a premium).
|
||||
const lineHeight = parseFloat(getComputedStyle(ta).lineHeight) || 22;
|
||||
const maxLines = this.surface === "edit" ? 8 : 4;
|
||||
ta.setCssStyles({ maxHeight: `${lineHeight * maxLines + 16}px`, overflowY: "auto" });
|
||||
const fit = (): void => {
|
||||
ta.setCssStyles({ height: "auto" });
|
||||
const needed = Math.min(ta.scrollHeight, lineHeight * maxLines + 16);
|
||||
ta.setCssStyles({ height: `${Math.max(needed, lineHeight * 2 + 16)}px` });
|
||||
};
|
||||
// 0.183.0: the editor FILLS the fixed-height modal (flex, via CSS) and scrolls
|
||||
// internally — no explicit line cap. With the modal at a fixed height it can't
|
||||
// grow past the keyboard anyway, so filling looks better than a hard cap that
|
||||
// leaves an empty gap under a short note.
|
||||
ta.setCssStyles({ overflowY: "auto" });
|
||||
|
||||
syncEdited();
|
||||
showTab("edit");
|
||||
requestAnimationFrame(() => {
|
||||
fit();
|
||||
ta.focus();
|
||||
const pos = this.surface === "edit" ? ta.value.length : Math.floor(ta.value.length / 2);
|
||||
ta.setSelectionRange(pos, pos);
|
||||
});
|
||||
ta.addEventListener("input", () => { this.cursorText = ta.value; fit(); syncEdited(); });
|
||||
ta.addEventListener("input", () => { this.cursorText = ta.value; syncEdited(); });
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
124
styles.css
124
styles.css
|
|
@ -1737,8 +1737,8 @@ body.is-phone .modal.stashpad-compact-modal.stashpad-breadcrumb-modal {
|
|||
position: absolute;
|
||||
top: 4px;
|
||||
right: 6px;
|
||||
width: 22px;
|
||||
height: 22px;
|
||||
width: 27px;
|
||||
height: 27px;
|
||||
padding: 0;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
|
|
@ -1752,7 +1752,7 @@ body.is-phone .modal.stashpad-compact-modal.stashpad-breadcrumb-modal {
|
|||
opacity: 0.65;
|
||||
}
|
||||
.stashpad-composer-fullscreen:hover { background: var(--background-modifier-hover); color: var(--text-normal); opacity: 1; }
|
||||
.stashpad-composer-fullscreen .svg-icon { width: 14px; height: 14px; }
|
||||
.stashpad-composer-fullscreen .svg-icon { width: 17px; height: 17px; }
|
||||
/* Keep typed text clear of the button. */
|
||||
.stashpad-composer-input { padding-right: 32px; }
|
||||
|
||||
|
|
@ -2266,6 +2266,33 @@ body.is-phone .modal.stashpad-compact-modal.stashpad-breadcrumb-modal {
|
|||
max-width: 860px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
/* 0.183.1: the popped-out editor VIEW on mobile. Make it a FLEX column so the
|
||||
editor fills the leaf — it collapsed to <1 line when the keyboard shrank the
|
||||
viewport because the host wasn't a flex container. Bottom padding clears
|
||||
Obsidian's mobile bottom toolbar + the home indicator. */
|
||||
.is-mobile .stashpad-split-view {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
/* 0.183.3: SCROLLABLE so no button is ever permanently tucked when the keyboard
|
||||
shrinks the leaf. 0.183.4: FULL WIDTH — the base rule's max-width:860px +
|
||||
margin:0 auto was centering it (auto side margins), leaving it inset. */
|
||||
overflow-y: auto;
|
||||
width: 100%;
|
||||
max-width: none;
|
||||
margin: 0;
|
||||
padding: 10px 8px calc(env(safe-area-inset-bottom, 0px) + 52px);
|
||||
}
|
||||
.is-mobile .stashpad-split-view .stashpad-split-tabpanel.is-active .stashpad-split-cursor-ta {
|
||||
min-height: calc(1.5em * 6);
|
||||
}
|
||||
/* Help text is empty on phones anyway — hide it so it doesn't eat vertical space. */
|
||||
.is-mobile .stashpad-split-help { display: none; }
|
||||
/* In the scrollable VIEW the editor already fills via flex; margin-top:auto
|
||||
misbehaves with overflow. 0.183.4: give it a fixed gap so the separator line
|
||||
isn't jammed against the text box / Case row — a touch more in split mode
|
||||
(text box edge) than edit mode (Case row). */
|
||||
.is-mobile .stashpad-split-view:not(.stashpad-edit-surface) .stashpad-split-actions { margin-top: 21px; }
|
||||
.is-mobile .stashpad-split-view.stashpad-edit-surface .stashpad-split-actions { margin-top: 18px; }
|
||||
/* In a full tab there's room — let the read-only previews and the preset list
|
||||
breathe (they stay short/scrolly inside the cramped modal). */
|
||||
.stashpad-split-view .stashpad-split-panel-body { max-height: calc(1.5em * 10 + 0.8em); }
|
||||
|
|
@ -2287,14 +2314,28 @@ body.is-phone .modal.stashpad-compact-modal.stashpad-breadcrumb-modal {
|
|||
end of the flow, so a long note (tall textarea) pushed Save/Cancel off-screen
|
||||
and — with the keyboard up — behind it. Now the content scrolls and the
|
||||
actions stick to the bottom of the visible area, always reachable. */
|
||||
.is-mobile .stashpad-split-modal {
|
||||
/* 0.182.0: anchor the sheet to the TOP (was centered) — with a notch / Dynamic
|
||||
Island offset — so it has more vertical room and its bottom sits well clear of
|
||||
the keyboard + the keyboard's autocomplete suggestion strip. */
|
||||
.is-mobile .modal-container:has(> .stashpad-split-modal) {
|
||||
align-items: flex-start;
|
||||
}
|
||||
/* FLOATING modal only (`:not(.stashpad-split-view)`) — the popped-out tab shares
|
||||
the .stashpad-split-modal class but is a full leaf, so it must NOT inherit the
|
||||
modal's min-height / margin / max-height (that collapsed it). */
|
||||
.is-mobile .stashpad-split-modal:not(.stashpad-split-view) {
|
||||
width: 100vw;
|
||||
max-width: 100vw;
|
||||
height: 100%;
|
||||
max-height: 100%;
|
||||
height: auto;
|
||||
/* Consistent, roomier height regardless of content; the editor fills it.
|
||||
0.183.1: 20px shorter so the action bar stays clear of the keyboard. */
|
||||
min-height: calc(58vh - 20px);
|
||||
/* Notch/Dynamic-Island offset, a quarter shorter (0.75× the inset). */
|
||||
margin-top: calc(env(safe-area-inset-top, 0px) * 0.75 + 8px);
|
||||
max-height: calc(100% - env(safe-area-inset-top, 0px) - 32px);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
border-radius: 0;
|
||||
border-radius: 0 0 14px 14px;
|
||||
}
|
||||
.is-mobile .stashpad-split-modal .modal-content {
|
||||
flex: 1 1 auto;
|
||||
|
|
@ -2305,18 +2346,17 @@ body.is-phone .modal.stashpad-compact-modal.stashpad-breadcrumb-modal {
|
|||
padding: 10px 12px;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
}
|
||||
/* Pinned, full-bleed action bar (Save/Cancel/pop-out). Sticks to the bottom of
|
||||
the scrollport so it stays visible above the keyboard; wraps on narrow width. */
|
||||
/* 0.183.0: action bar pinned to the BOTTOM of the modal via margin-top:auto —
|
||||
NOT position:sticky. Sticky made iOS lift/distort the row when the keyboard
|
||||
opened. No env(safe-area-inset-bottom) padding either (the sheet is top-anchored,
|
||||
not a bottom sheet — that padding drew empty space beneath the buttons on iOS). */
|
||||
.is-mobile .stashpad-split-modal .stashpad-split-actions {
|
||||
position: sticky;
|
||||
bottom: 0;
|
||||
margin: 8px -12px -10px;
|
||||
padding: 8px 12px calc(8px + env(safe-area-inset-bottom, 0px));
|
||||
background: var(--background-primary);
|
||||
margin-top: auto; /* pin to the bottom of the modal/leaf (not sticky) */
|
||||
padding-top: 10px;
|
||||
/* 0.183.2: NO opaque background — it read as a separate black box floating on
|
||||
the modal in iOS dark mode. Just a hairline separator. */
|
||||
border-top: 1px solid var(--background-modifier-border);
|
||||
flex-wrap: wrap;
|
||||
gap: 8px;
|
||||
z-index: 3;
|
||||
}
|
||||
/* On a phone the primary Save spans the row; secondary actions wrap under it. */
|
||||
.is-mobile .stashpad-split-modal .stashpad-split-actions-right {
|
||||
|
|
@ -2353,23 +2393,61 @@ body.is-phone .modal.stashpad-compact-modal.stashpad-breadcrumb-modal {
|
|||
.stashpad-split-tab.is-hidden { display: none; }
|
||||
.stashpad-split-tabpanel { position: relative; }
|
||||
.stashpad-split-tabpanel:not(.is-active) { display: none; }
|
||||
/* Copy button sits in the Original panel (top-right), scaled small. */
|
||||
.stashpad-split-tab-copy { position: absolute; top: 4px; right: 4px; z-index: 2; padding: 4px; }
|
||||
/* 0.183.0: the editor FILLS the fixed-height modal (no empty gap under short
|
||||
notes). Flex chain: modal-content → tabs → tabbody → active panel → textarea,
|
||||
each stretching; the textarea/preview scrolls internally. */
|
||||
.is-mobile .stashpad-split-tabs { flex: 1 1 auto; min-height: 0; display: flex; flex-direction: column; }
|
||||
.is-mobile .stashpad-split-tabbody { flex: 1 1 auto; min-height: 0; display: flex; }
|
||||
.is-mobile .stashpad-split-tabpanel.is-active { flex: 1 1 auto; min-height: 0; display: flex; flex-direction: column; }
|
||||
.is-mobile .stashpad-split-tabpanel.is-active .stashpad-split-cursor-ta { flex: 1 1 auto; min-height: 0; height: auto; }
|
||||
.is-mobile .stashpad-split-tabpanel.is-active .stashpad-split-panel-body { flex: 1 1 auto; min-height: 0; max-height: none; }
|
||||
/* 0.183.3: copy button lives in its own right-aligned bar ABOVE the Original text
|
||||
(no longer a full-height strip overlaying the text's right edge). */
|
||||
.stashpad-split-tab-copybar { display: flex; justify-content: flex-end; margin-bottom: 4px; flex: 0 0 auto; }
|
||||
.stashpad-split-tab-copy {
|
||||
position: static;
|
||||
height: auto;
|
||||
width: auto;
|
||||
padding: 5px 12px;
|
||||
gap: 5px;
|
||||
background: var(--background-secondary);
|
||||
border: 1px solid var(--background-modifier-border);
|
||||
}
|
||||
.stashpad-split-tab-copy svg { width: 1.15em; height: 1.15em; }
|
||||
/* Slimmer mobile modal buttons — the toggle / tabs / mode / action buttons were
|
||||
too chunky. Tight vertical padding + line-height, no min-height. */
|
||||
.is-mobile .stashpad-split-surface-btn,
|
||||
.is-mobile .stashpad-split-tab,
|
||||
.is-mobile .stashpad-split-mode-btn,
|
||||
.is-mobile .stashpad-split-cancel-btn,
|
||||
.is-mobile .stashpad-split-confirm-btn,
|
||||
.is-mobile .stashpad-split-popout-btn,
|
||||
.is-mobile .stashpad-split-case-btn {
|
||||
/* Obsidian mobile forces height: 44px on buttons — override to size to content. */
|
||||
height: auto;
|
||||
padding-top: 4px;
|
||||
padding-bottom: 4px;
|
||||
min-height: 0;
|
||||
line-height: 1.25;
|
||||
}
|
||||
.stashpad-split-tabpanel .stashpad-split-panel-body {
|
||||
max-height: calc(1.5em * 7 + 12px);
|
||||
overflow-y: auto;
|
||||
padding: 8px 10px;
|
||||
margin: 0;
|
||||
}
|
||||
/* Action bar: keep all buttons on ONE row, compact. Cancel drops its "(Esc)" hint,
|
||||
and pop-out + Obsidian-editor go icon-only, so Cancel / ⤢ / ✎ / Save fit. */
|
||||
.is-mobile .stashpad-split-modal .stashpad-split-actions { flex-wrap: nowrap; gap: 6px; margin-top: 6px; }
|
||||
/* Action bar: compact buttons; Cancel drops its "(Esc)" hint. 0.183.0: pop-out +
|
||||
Obsidian-editor now SHOW short text labels ("Pop-out" / "Advanced"), so the row
|
||||
is allowed to wrap. */
|
||||
.is-mobile .stashpad-split-modal .stashpad-split-actions { flex-wrap: wrap; gap: 6px; }
|
||||
.is-mobile .stashpad-split-cancel-btn,
|
||||
.is-mobile .stashpad-split-confirm-btn,
|
||||
.is-mobile .stashpad-split-popout-btn { padding: 6px 10px; }
|
||||
.is-mobile .stashpad-split-esc-hint { display: none; }
|
||||
.is-mobile .stashpad-split-popout-btn span:not([class*="icon"]) { display: none; }
|
||||
.is-mobile .stashpad-split-actions-right { margin-left: auto; gap: 8px; }
|
||||
/* Let Save flow inline with the others (wraps to a tidy grid) instead of being
|
||||
pushed to its own row by margin-left:auto. */
|
||||
.is-mobile .stashpad-split-actions-right { margin-left: 0; gap: 8px; }
|
||||
.is-mobile .stashpad-split-confirm-btn { flex: 1 1 auto; justify-content: center; }
|
||||
/* Tap-friendly line picker — make each line look interactable. */
|
||||
.stashpad-split-line { cursor: pointer; }
|
||||
.stashpad-split-line:hover { background: var(--background-modifier-hover); }
|
||||
|
|
|
|||
Loading…
Reference in a new issue