mirror of
https://github.com/grub-basket/SP.git
synced 2026-07-22 07:46:27 +00:00
0.121.1: mobile layout overhaul, collapsed-on-load, store-review fix
This commit is contained in:
parent
d5250fe6e4
commit
0510541589
9 changed files with 3105 additions and 107 deletions
38
eslint.config.mjs
Normal file
38
eslint.config.mjs
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
// ESLint flat config — surfaces the rules the Obsidian community-plugin review
|
||||
// bot actually acts on (eslint-plugin-obsidianmd). Treat a clean
|
||||
// `pnpm run lint` as a pre-publish gate so a store rejection is caught here.
|
||||
//
|
||||
// IMPORTANT: the `obsidianmd/recommended` preset also pulls in
|
||||
// typescript-eslint's *type-checked* layer (no-unsafe-*, no-explicit-any, …),
|
||||
// which fires thousands of times on this codebase's pre-existing `as any`
|
||||
// usage. The store does NOT gate on those — historically only the obsidianmd/*
|
||||
// rules block publishing. So we turn off the type-aware @typescript-eslint
|
||||
// noise, leaving the obsidianmd/* rules (the real gate) front and centre.
|
||||
//
|
||||
// Order matters: `disableTypeChecked` also clears parserOptions.project, so the
|
||||
// parser+project block is re-asserted AFTER it — several obsidianmd rules
|
||||
// (no-plugin-as-component, …) require type information to run.
|
||||
import tseslint from "typescript-eslint";
|
||||
import obsidianmd from "eslint-plugin-obsidianmd";
|
||||
|
||||
export default [
|
||||
{
|
||||
ignores: ["main.js", "node_modules/**", "dist/**", "*.mjs"],
|
||||
},
|
||||
...obsidianmd.configs.recommended,
|
||||
tseslint.configs.disableTypeChecked,
|
||||
{
|
||||
files: ["src/**/*.ts"],
|
||||
languageOptions: {
|
||||
parser: tseslint.parser,
|
||||
parserOptions: { project: "./tsconfig.json" },
|
||||
},
|
||||
rules: {
|
||||
// Stylistic base rules the store ignores; off so obsidianmd findings
|
||||
// aren't buried.
|
||||
"@typescript-eslint/no-explicit-any": "off",
|
||||
"@typescript-eslint/no-unused-vars": "off",
|
||||
"no-unused-vars": "off",
|
||||
},
|
||||
},
|
||||
];
|
||||
127
main.js
127
main.js
File diff suppressed because one or more lines are too long
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"id": "stashpad",
|
||||
"name": "Stashpad",
|
||||
"version": "0.118.14",
|
||||
"version": "0.121.1",
|
||||
"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",
|
||||
|
|
|
|||
11
package.json
11
package.json
|
|
@ -1,19 +1,24 @@
|
|||
{
|
||||
"name": "stashpad-obsidian",
|
||||
"version": "0.118.14",
|
||||
"version": "0.121.1",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "node esbuild.config.mjs",
|
||||
"build": "node esbuild.config.mjs production",
|
||||
"deploy:files": "node scripts/deploy.mjs",
|
||||
"deploy": "pnpm run build && node scripts/deploy.mjs",
|
||||
"typecheck": "tsc --noEmit"
|
||||
"typecheck": "tsc --noEmit",
|
||||
"lint": "eslint src",
|
||||
"lint:store": "eslint src --max-warnings 0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^20.10.0",
|
||||
"esbuild": "^0.21.0",
|
||||
"eslint": "^10.5.0",
|
||||
"eslint-plugin-obsidianmd": "^0.3.0",
|
||||
"obsidian": "latest",
|
||||
"typescript": "^5.4.0"
|
||||
"typescript": "^5.4.0",
|
||||
"typescript-eslint": "^8.61.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"fflate": "^0.8.3",
|
||||
|
|
|
|||
2641
pnpm-lock.yaml
2641
pnpm-lock.yaml
File diff suppressed because it is too large
Load diff
66
release-notes/0.121.1.md
Normal file
66
release-notes/0.121.1.md
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
# 0.121.1 — Mobile layout overhaul, collapsed-on-load, and a store-review fix
|
||||
|
||||
A mobile-focused release: the toolbars and navigation controls are rearranged
|
||||
for one-handed use, note rows get a tighter layout, compact mode is reworked,
|
||||
and the list now starts fully collapsed on every load. Also includes a fix for
|
||||
the plugin-store review and an internal lint gate.
|
||||
|
||||
## Mobile: toolbars to the bottom, nav into the composer (0.119.0–0.119.6)
|
||||
On mobile the chrome is reorganized so the controls sit near your thumb:
|
||||
|
||||
- **Toolbar moved below the list**, just above the composer, instead of at the
|
||||
top (0.119.0). The breadcrumb trail stays at the top.
|
||||
- **Folder picker + search** move into the composer's bottom-left (0.119.0), and
|
||||
are anchored to the left of the composer so Send stays on the right (0.119.3).
|
||||
The folder button shows the folder **name** (capped, with ellipsis) so you can
|
||||
see which folder you're posting to — not just an icon (0.119.4, 0.119.5).
|
||||
- **Back / forward / select / ⚡ actions** move from the breadcrumb into the
|
||||
bottom toolbar, alongside the ⋯ filters and compact buttons (0.119.1).
|
||||
- **Jump-to-level (route)** sits right after the forward button in the bottom
|
||||
toolbar (0.119.6); on Home it simply lists Home. Its picker opens as a
|
||||
**bottom sheet** that slides up from the bottom of the screen (0.119.6).
|
||||
- The ⋯ filters popover now opens **upward** (with a max-height and scroll) when
|
||||
there isn't room below, since its button now lives at the bottom (0.119.0).
|
||||
- Balanced/compact spacing for the bottom toolbar row (0.119.2).
|
||||
|
||||
## Mobile: tighter, fixed-position note rows (0.119.7, 0.119.8, 0.120.0–0.120.2)
|
||||
- **3-section note rows** (0.120.0): each row stacks into three parts — a top
|
||||
line with the timestamp (spread out), the expand/collapse toggle, and the ⋯
|
||||
menu; the note text in the middle; metadata at the bottom — for better
|
||||
vertical flow.
|
||||
- The top-row buttons are **sized to match the timestamp** so they read at the
|
||||
same weight (0.120.1).
|
||||
- The composer's **expand-toggle chevron stays in a fixed spot** whether the
|
||||
secondary button group is collapsed or expanded — it no longer jumps as the
|
||||
group opens (0.119.8).
|
||||
- **Compact mode cells** no longer clip or sit low: the single-line text now
|
||||
centers cleanly in the row (0.119.7).
|
||||
- **List/composer animations are disabled on mobile** to cut repaint flicker on
|
||||
re-render and scroll (0.120.2). The composer's keyboard-reflow timing is
|
||||
preserved.
|
||||
|
||||
## Mobile compact mode reworked (0.121.0, 0.121.1)
|
||||
- In compact mode the **bottom toolbar is now shown** (carrying the actions
|
||||
cluster and the compact/exit toggle) and the **breadcrumb is hidden**, so the
|
||||
un-compact button is reachable and the list reads as a tight, single-line view
|
||||
(0.121.0).
|
||||
- The **focused-note header is hidden in mobile compact** too (matching desktop
|
||||
compact), removing the empty block that used to sit at the top — the compact
|
||||
list now starts at the first row (0.121.1).
|
||||
|
||||
## Notes start collapsed on load (0.118.16)
|
||||
- On every load / reload / refresh, **all rows render collapsed**. The
|
||||
"expand the cursor row automatically" behaviour now only kicks in once you
|
||||
actively move the cursor (arrow keys or tapping a row) — a fresh view no
|
||||
longer opens with one row already expanded. Applies on desktop and mobile.
|
||||
|
||||
## Store-review fix (0.118.15)
|
||||
- Removed a static inline-style assignment flagged by the Obsidian
|
||||
community-plugin review (it was dead code — the element was cleared on the
|
||||
next line before it could render).
|
||||
|
||||
## Internal
|
||||
- Added an `eslint-plugin-obsidianmd` lint gate run before publishing, so
|
||||
store-review issues are caught locally. No effect on the shipped plugin.
|
||||
- A short-lived change that hid Obsidian's own view-header on mobile was trialed
|
||||
and **reverted** before this release — the header is unchanged.
|
||||
|
|
@ -2425,7 +2425,8 @@ export default class StashpadPlugin extends Plugin {
|
|||
}
|
||||
if (r.authors > 0) parts.push(`${r.authors} author${r.authors === 1 ? "" : "s"} in registry`);
|
||||
// Replace the progress notice in place with a persistent success notice.
|
||||
fill.style.width = "100%";
|
||||
// (No need to animate the bar to 100% — el.empty() below removes it
|
||||
// synchronously before the browser can paint, so it would never show.)
|
||||
el.empty();
|
||||
el.removeClass("stashpad-progress-notice");
|
||||
el.addClass("stashpad-progress-done");
|
||||
|
|
|
|||
152
src/view.ts
152
src/view.ts
|
|
@ -294,6 +294,12 @@ export class StashpadView extends ItemView {
|
|||
* `.is-cursor-expanded` so an explicit collapse sticks; cleared when the
|
||||
* cursor leaves the row (then it auto-expands again next time). */
|
||||
private cursorExpandOverride = new Set<StashpadId>();
|
||||
/** Has the user moved the cursor (arrow nav / row click) since this view
|
||||
* instance loaded? Gates the cursor auto-expand (autoExpandCursorRow) so a
|
||||
* fresh load/reload/refresh renders EVERY row collapsed — the cursor row
|
||||
* only auto-expands once the user actively navigates. Per-instance, so an
|
||||
* app/view reload (new instance) starts collapsed again. */
|
||||
private cursorHasMoved = false;
|
||||
/** Sheet versions: which version (note id) of a `sheet:` group is currently
|
||||
* shown as the row. View-state only (not persisted) — falls back to the
|
||||
* final pick / first-by-order when unset. */
|
||||
|
|
@ -1642,7 +1648,11 @@ export class StashpadView extends ItemView {
|
|||
this.pendingCursorId = null;
|
||||
}
|
||||
}
|
||||
if (focused.file && Platform.isMobile) {
|
||||
// 0.121.1 (item 5 follow-up): mobile appends the focused-note header into
|
||||
// the list. In COMPACT mode skip it (matching desktop compact, which omits
|
||||
// the focused header) — with the breadcrumb also hidden, leaving the header
|
||||
// in left an empty-looking block at the top. Compact = tight list, no header.
|
||||
if (focused.file && Platform.isMobile && !this.compactMode) {
|
||||
this.renderFocusedHeaderMini(list, focused);
|
||||
this.renderFocusedHeader(list, focused);
|
||||
}
|
||||
|
|
@ -1760,7 +1770,7 @@ export class StashpadView extends ItemView {
|
|||
else this.renderLockedPlaceholder(list, it.lk);
|
||||
}
|
||||
}
|
||||
if (focused.file && Platform.isMobile) this.installFocusedMiniObserver(list);
|
||||
if (focused.file && Platform.isMobile && !this.compactMode) this.installFocusedMiniObserver(list);
|
||||
}
|
||||
|
||||
/** 0.98.1: a locked-subtree placeholder row. Click → unlock (prompts for the
|
||||
|
|
@ -2349,13 +2359,19 @@ export class StashpadView extends ItemView {
|
|||
if (this.tinyMode) {
|
||||
this.renderTinyHeader(root);
|
||||
} else {
|
||||
// 0.61.2: compact mode skips the time-filter row (folder switcher,
|
||||
// tag/color/sort/view dropdowns, time-window buttons, the three
|
||||
// view-mode buttons). Breadcrumb stays — it's the smallest signal
|
||||
// of "where am I" worth keeping, and the breadcrumb is where the
|
||||
// actions cluster (select-mode toggle + ⚡ actions menu) lives.
|
||||
if (!this.compactMode) this.renderTimeFilterBar(root);
|
||||
this.renderBreadcrumb(root);
|
||||
// 0.61.2: compact mode skips the time-filter row on DESKTOP (folder
|
||||
// switcher, tag/color/sort/view dropdowns, time-window buttons, the three
|
||||
// view-mode buttons); the breadcrumb stays there and hosts the
|
||||
// exit-compact button + the desktop actions cluster.
|
||||
// 0.121.0 (item 5): on MOBILE compact mode, do the opposite — surface the
|
||||
// bottom toolbar (it already carries the actions cluster + the
|
||||
// compact/exit toggle on mobile) and HIDE the breadcrumb, so the
|
||||
// un-compact button is reachable and compact reads as a tight list. On
|
||||
// desktop compact, behavior is unchanged (toolbar hidden, breadcrumb +
|
||||
// its exit button shown).
|
||||
const mobileCompact = this.compactMode && Platform.isMobile;
|
||||
if (!this.compactMode || Platform.isMobile) this.renderTimeFilterBar(root);
|
||||
if (!mobileCompact) this.renderBreadcrumb(root);
|
||||
}
|
||||
|
||||
const focused = this.tree.get(this.focusId) ?? this.tree.getRoot();
|
||||
|
|
@ -2654,28 +2670,37 @@ export class StashpadView extends ItemView {
|
|||
private renderTimeFilterBar(parent: HTMLElement): void {
|
||||
const bar = parent.createDiv({ cls: "stashpad-time-filter-bar" });
|
||||
|
||||
// Folder switcher
|
||||
const folderBtn = bar.createEl("button", { cls: "stashpad-folder-btn" });
|
||||
const isOverride = !!this.folderOverride;
|
||||
const displayName = (this.noteFolder.split("/").pop() || this.noteFolder) || "Stashpad";
|
||||
// 0.118.0: per-folder icon (settings) on the switcher too, falling back to
|
||||
// the generic folder glyph.
|
||||
setIcon(folderBtn.createSpan({ cls: "stashpad-btn-icon" }), this.plugin.getFolderIcon(this.noteFolder) ?? "folder");
|
||||
folderBtn.createSpan({ text: displayName, cls: "stashpad-btn-text" });
|
||||
folderBtn.title = isOverride
|
||||
? `Folder (override): ${this.noteFolder}\nClick to change or revert to default.`
|
||||
: `Folder: ${this.noteFolder}\nClick to override for this tab.`;
|
||||
if (isOverride) folderBtn.addClass("is-override");
|
||||
folderBtn.onclick = (e) => { e.preventDefault(); this.openFolderPicker(); };
|
||||
// 0.119.0 (mobile-ui-changes-2): on mobile the folder switcher + search move
|
||||
// into the composer's bottom-left nav cluster (renderComposerNavCluster), so
|
||||
// skip them here. Desktop keeps them in the toolbar as before.
|
||||
if (!Platform.isMobile) {
|
||||
// Folder switcher
|
||||
const folderBtn = bar.createEl("button", { cls: "stashpad-folder-btn" });
|
||||
const isOverride = !!this.folderOverride;
|
||||
const displayName = (this.noteFolder.split("/").pop() || this.noteFolder) || "Stashpad";
|
||||
// 0.118.0: per-folder icon (settings) on the switcher too, falling back to
|
||||
// the generic folder glyph.
|
||||
setIcon(folderBtn.createSpan({ cls: "stashpad-btn-icon" }), this.plugin.getFolderIcon(this.noteFolder) ?? "folder");
|
||||
folderBtn.createSpan({ text: displayName, cls: "stashpad-btn-text" });
|
||||
folderBtn.title = isOverride
|
||||
? `Folder (override): ${this.noteFolder}\nClick to change or revert to default.`
|
||||
: `Folder: ${this.noteFolder}\nClick to override for this tab.`;
|
||||
if (isOverride) folderBtn.addClass("is-override");
|
||||
folderBtn.onclick = (e) => { e.preventDefault(); this.openFolderPicker(); };
|
||||
|
||||
// 0.68.4: icon-only Search button between the folder switcher and
|
||||
// the tags dropdown. Mirrors the Mod+F binding for mouse users.
|
||||
const searchBtn = bar.createEl("button", { cls: "stashpad-search-btn" });
|
||||
setIconSafe(searchBtn, "search", "🔍");
|
||||
searchBtn.title = "Search notes (Mod+F)";
|
||||
searchBtn.onclick = (e) => { e.preventDefault(); this.openSearchModal(); };
|
||||
// 0.68.4: icon-only Search button between the folder switcher and
|
||||
// the tags dropdown. Mirrors the Mod+F binding for mouse users.
|
||||
const searchBtn = bar.createEl("button", { cls: "stashpad-search-btn" });
|
||||
setIconSafe(searchBtn, "search", "🔍");
|
||||
searchBtn.title = "Search notes (Mod+F)";
|
||||
searchBtn.onclick = (e) => { e.preventDefault(); this.openSearchModal(); };
|
||||
}
|
||||
|
||||
if (Platform.isMobile) {
|
||||
// 0.119.1 (mobile-ui-changes-2): the actions cluster (back / forward /
|
||||
// select / ⚡) lives here at the start of the bottom toolbar, next to the
|
||||
// ⋯ filters + compact buttons (moved out of the breadcrumb).
|
||||
this.renderActionsCluster(bar);
|
||||
// Mobile: collapse the four filter/view buttons into a single
|
||||
// entry-point button. Tapping it opens a vertical accordion with
|
||||
// one section per former button — keeps the header bar uncluttered
|
||||
|
|
@ -3094,6 +3119,16 @@ export class StashpadView extends ItemView {
|
|||
if (!canGoFwd) fwdBtn.addClass("is-disabled");
|
||||
fwdBtn.onclick = (e) => { e.preventDefault(); this.navigateForward(); };
|
||||
|
||||
// 0.119.6 (mobile-ui-changes-2): jump-to-level (route) sits right after the
|
||||
// forward button on mobile (the actions cluster lives in the bottom toolbar
|
||||
// there). Always shown — at Home the picker just lists Home.
|
||||
if (Platform.isMobile) {
|
||||
const routeBtn = actions.createEl("button", { cls: "stashpad-mobile-action-btn" });
|
||||
setIconSafe(routeBtn, "route", "⋔");
|
||||
routeBtn.title = "Jump to a level in the path";
|
||||
routeBtn.onclick = (e) => { e.preventDefault(); this.openBreadcrumbLevelsModal(); };
|
||||
}
|
||||
|
||||
const selectBtn = actions.createEl("button", { cls: "stashpad-mobile-action-btn" });
|
||||
const inSelect = this.mobileSelectMode;
|
||||
setIconSafe(selectBtn, inSelect ? "check-square" : "square", inSelect ? "☑" : "☐");
|
||||
|
|
@ -3589,13 +3624,21 @@ export class StashpadView extends ItemView {
|
|||
// instead of off the right side of the screen. Min 8px gutter
|
||||
// from the viewport right edge as a safety margin if the button
|
||||
// is itself off-screen for any reason.
|
||||
// 0.119.0 (mobile-ui-changes-2): the filters button now sits at the BOTTOM
|
||||
// of the screen (toolbar moved above the composer), so opening downward
|
||||
// would run off-screen. Open UPWARD when there isn't room below.
|
||||
const openUp = r.bottom + 280 > win.innerHeight;
|
||||
pop.setCssStyles({
|
||||
right: `${Math.max(8, win.innerWidth - r.right)}px`,
|
||||
left: "auto",
|
||||
top: `${r.bottom + 4}px`,
|
||||
...(openUp
|
||||
? { bottom: `${Math.max(8, win.innerHeight - r.top + 4)}px`, top: "auto" }
|
||||
: { top: `${r.bottom + 4}px`, bottom: "auto" }),
|
||||
// Wider than the per-button popovers so accordion section headers +
|
||||
// option rows have room to breathe. Capped to viewport width.
|
||||
maxWidth: "min(360px, calc(100vw - 16px))",
|
||||
maxHeight: "min(60vh, 420px)",
|
||||
overflowY: "auto",
|
||||
width: "max-content",
|
||||
minWidth: "260px",
|
||||
});
|
||||
|
|
@ -4424,7 +4467,10 @@ export class StashpadView extends ItemView {
|
|||
// gives the time-filter row more horizontal real estate.
|
||||
// 0.117.0: the "jump to level" button now lives inside the actions
|
||||
// cluster (rendered above), grouped with back/forward/select/⚡.
|
||||
this.renderActionsCluster(bar);
|
||||
// 0.119.1 (mobile-ui-changes-2): on mobile the actions cluster
|
||||
// (back/forward/select/⚡) moves into the bottom toolbar (time-filter-bar),
|
||||
// next to the ⋯ filters + compact buttons; the breadcrumb keeps just the trail.
|
||||
if (!Platform.isMobile) this.renderActionsCluster(bar);
|
||||
const homeBtn = bar.createSpan({ cls: "stashpad-crumb stashpad-crumb-home" });
|
||||
if (Platform.isMobile) {
|
||||
// Mobile: render as a house icon to save horizontal space.
|
||||
|
|
@ -4542,7 +4588,9 @@ export class StashpadView extends ItemView {
|
|||
// (by the crumbs, not the nav cluster). CSS absolutely-positions it at the
|
||||
// right edge with reserved padding, so it stays visible even when the
|
||||
// inline crumbs clip — which is exactly when it's needed.
|
||||
this.renderBreadcrumbLevelsButton(bar);
|
||||
// 0.119.0 (mobile-ui-changes-2): on mobile it moves into the composer's
|
||||
// bottom-left nav cluster instead.
|
||||
if (!Platform.isMobile) this.renderBreadcrumbLevelsButton(bar);
|
||||
}
|
||||
|
||||
/** 0.117.0: the breadcrumb "all levels" button. Pinned at the right end of
|
||||
|
|
@ -4840,7 +4888,7 @@ export class StashpadView extends ItemView {
|
|||
if (isCursor) row.addClass("is-cursor");
|
||||
// 0.73.14: auto-expand the cursor row on initial render too (not
|
||||
// just on arrow-key repaints). Settings-gated.
|
||||
if (isCursor && this.plugin.settings.autoExpandCursorRow && !this.cursorExpandOverride.has(node.id)) row.addClass("is-cursor-expanded");
|
||||
if (isCursor && this.cursorHasMoved && this.plugin.settings.autoExpandCursorRow && !this.cursorExpandOverride.has(node.id)) row.addClass("is-cursor-expanded");
|
||||
if (isPickTarget) row.addClass("is-pick-target");
|
||||
if (this.isCompleted(node)) row.addClass("is-completed");
|
||||
if (this.isListPinned(node.id)) row.addClass("is-list-pinned");
|
||||
|
|
@ -5290,6 +5338,34 @@ export class StashpadView extends ItemView {
|
|||
}
|
||||
}
|
||||
|
||||
/** 0.119.0 (mobile-ui-changes-2): bottom-left nav cluster in the composer —
|
||||
* folder picker + search + jump-to-level (route). Mobile only; these moved
|
||||
* out of the top toolbar / breadcrumb. Always visible (no collapse, per the
|
||||
* request that always-visible is fine). */
|
||||
private renderComposerNavCluster(rail: HTMLElement): void {
|
||||
const nav = rail.createDiv({ cls: "stashpad-composer-nav" });
|
||||
// Folder picker (shows the per-folder icon if set, else the folder glyph).
|
||||
const folderBtn = nav.createEl("button", { cls: "stashpad-composer-btn stashpad-composer-nav-folder" });
|
||||
setIcon(folderBtn, this.plugin.getFolderIcon(this.noteFolder) ?? "folder");
|
||||
// 0.119.4: show the folder NAME (capped via CSS) so you know which folder
|
||||
// you're in — not just the icon.
|
||||
const fname = (this.noteFolder.split("/").pop() || this.noteFolder) || "Stashpad";
|
||||
folderBtn.createSpan({ cls: "stashpad-btn-text", text: fname });
|
||||
folderBtn.title = `Folder: ${this.noteFolder}\nTap to switch / create.`;
|
||||
if (this.folderOverride) folderBtn.addClass("is-active");
|
||||
folderBtn.onmousedown = (e) => e.preventDefault();
|
||||
folderBtn.onclick = (e) => { e.preventDefault(); this.openFolderPicker(); };
|
||||
// Search.
|
||||
const searchBtn = nav.createEl("button", { cls: "stashpad-composer-btn" });
|
||||
setIconSafe(searchBtn, "search", "🔍");
|
||||
searchBtn.title = "Search notes (Mod+F)";
|
||||
searchBtn.onmousedown = (e) => e.preventDefault();
|
||||
searchBtn.onclick = (e) => { e.preventDefault(); this.openSearchModal(); };
|
||||
// 0.119.6: route (jump-to-level) moved to the actions cluster (after the
|
||||
// forward button) — see renderActionsCluster. The composer nav is now just
|
||||
// folder + search.
|
||||
}
|
||||
|
||||
private renderComposer(parent: HTMLElement): void {
|
||||
const settings = getSettings();
|
||||
const enterSubmits = this.modeEnterSubmits;
|
||||
|
|
@ -5480,6 +5556,10 @@ export class StashpadView extends ItemView {
|
|||
fileInput.setCssStyles({ display: "none" });
|
||||
|
||||
const btnRail = composer.createDiv({ cls: "stashpad-composer-btn-rail" });
|
||||
// 0.119.0 (mobile-ui-changes-2): on mobile, the folder picker + search +
|
||||
// jump-to-level (route) controls live here at the bottom-left of the
|
||||
// composer (moved out of the top toolbar / breadcrumb).
|
||||
if (Platform.isMobile) this.renderComposerNavCluster(btnRail);
|
||||
// Mobile: secondary buttons (split/dest/enter/clip) live inside a
|
||||
// collapsible group. A chevron-left button at the head of the rail
|
||||
// toggles their visibility — collapsed at rest to keep the composer
|
||||
|
|
@ -5830,6 +5910,9 @@ export class StashpadView extends ItemView {
|
|||
// Normal (settled) tap — drop any stale aimed target so it can't leak into
|
||||
// a later unrelated double-tap.
|
||||
this.aimedTapTargetId = null;
|
||||
// A real tap on a row counts as a cursor move — arm the cursor auto-expand
|
||||
// (kept off through the initial post-load render).
|
||||
this.cursorHasMoved = true;
|
||||
const targetEl = e.target as HTMLElement | null;
|
||||
// Tag click → open global search filtered by that tag.
|
||||
const tag = targetEl?.closest?.(".tag") as HTMLElement | null;
|
||||
|
|
@ -6330,6 +6413,9 @@ export class StashpadView extends ItemView {
|
|||
private selectCursor(shift: boolean): void {
|
||||
const node = this.currentChildren[this.cursorIdx];
|
||||
if (!node) return;
|
||||
// First real cursor move since load — arm the cursor auto-expand (so the
|
||||
// initial render stayed fully collapsed).
|
||||
this.cursorHasMoved = true;
|
||||
if (!shift) {
|
||||
this.selection.clear();
|
||||
this.selection.add(node.id);
|
||||
|
|
@ -6388,7 +6474,7 @@ export class StashpadView extends ItemView {
|
|||
// 0.118.10: respect a manual-collapse override; clear it once the cursor
|
||||
// leaves the row so the auto-expand resumes on the next visit.
|
||||
if (!isCursor) this.cursorExpandOverride.delete(id);
|
||||
row.classList.toggle("is-cursor-expanded", autoExpand && isCursor && !this.cursorExpandOverride.has(id));
|
||||
row.classList.toggle("is-cursor-expanded", autoExpand && isCursor && this.cursorHasMoved && !this.cursorExpandOverride.has(id));
|
||||
// 0.73.15: pick-target class. Used by the in-list parent picker
|
||||
// so its arrow-key nav also avoids the full-render rebuild.
|
||||
row.classList.toggle("is-pick-target", idx === pickIdx);
|
||||
|
|
|
|||
172
styles.css
172
styles.css
|
|
@ -953,6 +953,24 @@
|
|||
min-width: 260px;
|
||||
max-width: min(440px, 94vw) !important;
|
||||
}
|
||||
/* 0.119.6: on mobile the jump-to-level picker is a full-width bottom SHEET
|
||||
(slides up from the bottom, like Obsidian's mobile pickers) instead of a
|
||||
centered box — anchor the modal container to the bottom and stretch the modal. */
|
||||
body.is-mobile .modal-container:has(.stashpad-breadcrumb-modal),
|
||||
body.is-phone .modal-container:has(.stashpad-breadcrumb-modal) {
|
||||
align-items: flex-end;
|
||||
}
|
||||
body.is-mobile .modal.stashpad-compact-modal.stashpad-breadcrumb-modal,
|
||||
body.is-phone .modal.stashpad-compact-modal.stashpad-breadcrumb-modal {
|
||||
width: 100% !important;
|
||||
max-width: 100% !important;
|
||||
min-width: 0;
|
||||
margin: 0;
|
||||
border-radius: 14px 14px 0 0;
|
||||
max-height: 75vh;
|
||||
animation: stashpad-sheet-up 0.18s ease;
|
||||
}
|
||||
@keyframes stashpad-sheet-up { from { transform: translateY(100%); } to { transform: translateY(0); } }
|
||||
.stashpad-breadcrumb-modal .stashpad-bc-levels { display: flex; flex-direction: column; gap: 2px; }
|
||||
.stashpad-bc-level-row {
|
||||
display: flex;
|
||||
|
|
@ -1692,6 +1710,61 @@
|
|||
grid-template-columns: auto 1fr auto;
|
||||
}
|
||||
|
||||
/* 0.120.0 (trial): 3-section mobile note row. Instead of [meta | body |
|
||||
actions] side-by-side columns, stack three rows for vertical tightness +
|
||||
better reading flow:
|
||||
row 1: timestamp (spread left) · expand toggle · ⋯ menu
|
||||
row 2: the text body (full width)
|
||||
row 3: metadata (children-count, pin, task checkbox)
|
||||
Pure CSS — the DOM already has these pieces; we just regroup them with
|
||||
grid-template-areas. `display: contents` on the meta/actions wrappers
|
||||
promotes their children to direct grid items so they can be placed into
|
||||
areas. Scoped to NON-compact mobile (compact has its own dense layout). */
|
||||
.stashpad-view.is-mobile:not(.is-compact) .stashpad-note,
|
||||
.stashpad-view.is-mobile:not(.is-compact) .stashpad-note.is-task {
|
||||
grid-template-columns: 1fr auto auto;
|
||||
grid-template-areas:
|
||||
"meta toggle more"
|
||||
"body body body"
|
||||
"foot foot foot";
|
||||
align-items: center;
|
||||
row-gap: 2px;
|
||||
column-gap: 8px;
|
||||
}
|
||||
.stashpad-view.is-mobile:not(.is-compact) .stashpad-note-meta,
|
||||
.stashpad-view.is-mobile:not(.is-compact) .stashpad-note-actions {
|
||||
display: contents;
|
||||
}
|
||||
.stashpad-view.is-mobile:not(.is-compact) .stashpad-note-meta-top { grid-area: meta; }
|
||||
.stashpad-view.is-mobile:not(.is-compact) .stashpad-note-body { grid-area: body; }
|
||||
.stashpad-view.is-mobile:not(.is-compact) .stashpad-note-meta-bottom { grid-area: foot; }
|
||||
.stashpad-view.is-mobile:not(.is-compact) .stashpad-note .stashpad-expand-toggle { grid-area: toggle; }
|
||||
.stashpad-view.is-mobile:not(.is-compact) .stashpad-note .stashpad-note-more { grid-area: more; }
|
||||
/* Timestamp reads horizontally (one line) on mobile so it "spreads out"
|
||||
across the top row instead of stacking date over time. */
|
||||
.stashpad-view.is-mobile:not(.is-compact) .stashpad-note-meta-top .stashpad-note-time {
|
||||
white-space: normal;
|
||||
}
|
||||
/* The top-row buttons sit flush right, snug together — and are sized down so
|
||||
they read at the same visual weight as the timestamp text (was 28px box /
|
||||
14px icon, which looked chunkier than the time). */
|
||||
.stashpad-view.is-mobile:not(.is-compact) .stashpad-note .stashpad-expand-toggle,
|
||||
.stashpad-view.is-mobile:not(.is-compact) .stashpad-note .stashpad-note-more {
|
||||
align-self: center;
|
||||
width: 22px;
|
||||
height: 22px;
|
||||
}
|
||||
.stashpad-view.is-mobile:not(.is-compact) .stashpad-note .stashpad-expand-toggle .svg-icon,
|
||||
.stashpad-view.is-mobile:not(.is-compact) .stashpad-note .stashpad-note-more .svg-icon {
|
||||
width: 15px;
|
||||
height: 15px;
|
||||
}
|
||||
/* Timestamp nudged up a step (smaller → small) so it matches the now-smaller
|
||||
top-row buttons instead of looking tiny beside them. */
|
||||
.stashpad-view.is-mobile:not(.is-compact) .stashpad-note-meta-top .stashpad-note-time {
|
||||
font-size: var(--font-ui-small);
|
||||
}
|
||||
|
||||
/* Suggest modal (pick/search) */
|
||||
.stashpad-suggest-item { padding: 6px 8px; }
|
||||
.stashpad-suggest-title { font-weight: 500; }
|
||||
|
|
@ -1993,13 +2066,30 @@
|
|||
transition-delay: 0s;
|
||||
}
|
||||
|
||||
/* 0.120.2 (item 3): kill list + composer transitions/animations on mobile to
|
||||
cut the repaint flicker on re-render / scroll / selection change. Scoped to
|
||||
the note rows (+ their children) and the COMPOSER'S DESCENDANTS only — the
|
||||
composer's own `margin-bottom` transition above is the keyboard-reflow timing
|
||||
and lives on `.stashpad-composer` itself, so the `*` descendant selector
|
||||
leaves it untouched. (Cheap; may only partly reduce the flicker since some of
|
||||
it comes from the deferred body-render reflow, not CSS animation.) */
|
||||
.stashpad-view.is-mobile .stashpad-note,
|
||||
.stashpad-view.is-mobile .stashpad-note *,
|
||||
.stashpad-view.is-mobile .stashpad-composer * {
|
||||
transition: none !important;
|
||||
animation: none !important;
|
||||
}
|
||||
|
||||
/* Hide the composer's helper text on mobile — it wraps and eats
|
||||
another row of vertical space we can't afford. */
|
||||
.stashpad-view.is-mobile .stashpad-composer-help { display: none; }
|
||||
|
||||
/* Tighten the top filter row: pills match the action-button height
|
||||
(28px) so the whole row reads as a single chip strip. */
|
||||
.stashpad-view.is-mobile .stashpad-time-filter-bar { padding: 1px 0 2px; gap: 4px; align-items: center; }
|
||||
/* 0.119.2 (mobile-ui-changes-2): balanced, compact vertical spacing — a little
|
||||
room above (between the divider and the button row) and below, instead of the
|
||||
buttons hugging the top border with a big gap beneath. */
|
||||
.stashpad-view.is-mobile .stashpad-time-filter-bar { padding: 5px 0; gap: 4px; align-items: center; }
|
||||
.stashpad-view.is-mobile .stashpad-folder-btn,
|
||||
.stashpad-view.is-mobile .stashpad-time-filter-btn {
|
||||
height: 28px;
|
||||
|
|
@ -2035,25 +2125,69 @@
|
|||
doesn't run under the rail. */
|
||||
.stashpad-view.is-mobile .stashpad-composer {
|
||||
position: relative;
|
||||
padding-top: 6px;
|
||||
/* 0.119.2: was 6px; trimmed so the toolbar sits closer above the composer
|
||||
(less dead space between the button row and the input). */
|
||||
padding-top: 2px;
|
||||
}
|
||||
.stashpad-view.is-mobile .stashpad-composer-input-wrap {
|
||||
width: 100%;
|
||||
flex: 1 1 100%;
|
||||
}
|
||||
.stashpad-view.is-mobile .stashpad-composer-input {
|
||||
padding-right: 96px;
|
||||
/* 0.119.3: the button rail is now a full-width strip along the bottom
|
||||
(folder/search left, send right), so reserve bottom space instead of a
|
||||
right gutter. */
|
||||
padding-right: 12px;
|
||||
padding-bottom: 40px;
|
||||
min-height: 72px;
|
||||
max-height: 220px;
|
||||
}
|
||||
.stashpad-view.is-mobile .stashpad-composer-btn-rail {
|
||||
position: absolute;
|
||||
right: 6px;
|
||||
bottom: 8px;
|
||||
/* 0.119.3: span the full width so the nav cluster can sit at the LEFT
|
||||
(easily reachable) while send stays at the right. */
|
||||
left: 8px;
|
||||
right: 8px;
|
||||
bottom: 6px;
|
||||
gap: 0;
|
||||
pointer-events: none;
|
||||
display: inline-flex;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
/* 0.119.8 (item 4): right-anchor the toggle+send cluster so the collapse
|
||||
chevron sits in a FIXED spot whether the secondary group is collapsed or
|
||||
expanded. nav's margin-right:auto already pushes the cluster right when
|
||||
present; flex-end keeps it anchored even if it isn't. */
|
||||
justify-content: flex-end;
|
||||
}
|
||||
/* 0.119.8 (item 4): order the collapsible group BEFORE the toggle so it
|
||||
expands to the LEFT of the chevron. With the cluster right-anchored, the
|
||||
toggle + send then stay put (toggle = rightEdge − send − toggle) regardless
|
||||
of whether the group is showing — no more chevron jump on expand/collapse. */
|
||||
.stashpad-view.is-mobile .stashpad-composer-btn-group { order: 1; }
|
||||
.stashpad-view.is-mobile .stashpad-composer-rail-toggle { order: 2; }
|
||||
.stashpad-view.is-mobile .stashpad-composer-send { order: 3; }
|
||||
/* nav cluster pushed to the far left; everything after it (collapse chevron,
|
||||
secondary group, send) stays on the right. */
|
||||
.stashpad-view.is-mobile .stashpad-composer-nav { margin-right: auto; gap: 6px; }
|
||||
/* 0.119.4 / 0.119.5: the composer folder button shows the folder NAME so you know
|
||||
which folder you're in. The button must GROW to fit the name — use the compound
|
||||
`.stashpad-composer-btn.stashpad-composer-nav-folder` (both classes, no space)
|
||||
so width:auto out-specifies the generic mobile `width: 32px` for composer-btn. */
|
||||
.stashpad-view.is-mobile .stashpad-composer-btn.stashpad-composer-nav-folder {
|
||||
width: auto;
|
||||
min-width: 0;
|
||||
padding: 0 8px;
|
||||
gap: 4px;
|
||||
}
|
||||
.stashpad-view.is-mobile .stashpad-composer-btn.stashpad-composer-nav-folder .stashpad-btn-text {
|
||||
display: inline-block;
|
||||
/* ≥15 chars of the folder name before ellipsis. */
|
||||
max-width: 16ch;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
vertical-align: middle;
|
||||
font-size: var(--font-ui-smaller);
|
||||
}
|
||||
.stashpad-view.is-mobile .stashpad-composer-btn-rail > *,
|
||||
.stashpad-view.is-mobile .stashpad-composer-btn-group > * { pointer-events: auto; }
|
||||
|
|
@ -2078,6 +2212,26 @@
|
|||
color: var(--text-on-accent);
|
||||
}
|
||||
.stashpad-view.is-mobile .stashpad-composer-send:hover { color: var(--text-on-accent); }
|
||||
|
||||
/* 0.119.0 (mobile-ui-changes-2): mobile layout — move the filter/view/time
|
||||
toolbar to the bottom (above the composer); the breadcrumb trail stays at top.
|
||||
The root is a flex column whose direct children are time-filter-bar,
|
||||
breadcrumb, list, composer — reorder them with flex `order`. */
|
||||
/* 0.119.2: tighter row spacing on mobile (the root column's flex gap was 8px,
|
||||
which read as too much dead space between the bottom toolbar and composer). */
|
||||
.stashpad-view.is-mobile { gap: 5px; }
|
||||
.stashpad-view.is-mobile .stashpad-breadcrumb { order: 1; }
|
||||
.stashpad-view.is-mobile .stashpad-list { order: 2; }
|
||||
.stashpad-view.is-mobile .stashpad-time-filter-bar {
|
||||
order: 3;
|
||||
/* It now sits above the composer, so border reads better on top. */
|
||||
border-bottom: none;
|
||||
border-top: 1px solid var(--background-modifier-border);
|
||||
}
|
||||
.stashpad-view.is-mobile .stashpad-composer { order: 4; }
|
||||
/* Bottom-left nav cluster inside the composer: folder · search · route. */
|
||||
.stashpad-composer-nav { display: inline-flex; align-items: center; gap: 4px; flex: 0 0 auto; }
|
||||
.stashpad-composer-nav .stashpad-composer-btn { flex: 0 0 auto; }
|
||||
/* Collapsible secondary-options group. Default state: width 0,
|
||||
contents clipped — only the toggle chevron + send are visible.
|
||||
Expanded: width grows to fit children, contents fade in. */
|
||||
|
|
@ -3386,6 +3540,12 @@
|
|||
min-height: 32px;
|
||||
align-items: center;
|
||||
overflow: hidden;
|
||||
/* 0.119.7: the base row keeps 8px top/bottom padding, which left only
|
||||
~16px of content area inside the 32px row — too short for the ~22px
|
||||
text line, so it clipped and sat low ("top gap + sunk text"). Drop the
|
||||
vertical padding so the single clamped line centers cleanly in 32px. */
|
||||
padding-top: 0;
|
||||
padding-bottom: 0;
|
||||
}
|
||||
/* Make sure the tiny-header buttons never get shrunk out of view. */
|
||||
.stashpad-tiny-header .stashpad-tiny-expand,
|
||||
|
|
|
|||
Loading…
Reference in a new issue