release 0.1.2 — Obsidian review hygiene

Cut the automated review-bot CSS lint without touching theme/iOS-defensive
overrides: dedupe height/max-height (100dvh only), replace
.modal-container:has(...) with an onOpen container class, and style toolbar
scrollbars via ::-webkit-scrollbar. Adds CONTRIBUTING.

Co-Authored-By: chodaict <5516582+chodaict@users.noreply.github.com>
This commit is contained in:
mixflavor 2026-06-20 17:57:10 +09:00
parent b9c6f17341
commit f6a4285049
5 changed files with 67 additions and 28 deletions

30
CONTRIBUTING.md Normal file
View file

@ -0,0 +1,30 @@
# Contributing to marktile
Thanks for your interest! marktile is an open-source Obsidian plugin by [CVER Inc.](https://cver.net)
## Reporting bugs & requesting features
Please open a [GitHub issue](https://github.com/CVERInc/marktile/issues). Helpful details:
- Obsidian version, and your OS / device (desktop, iOS, Android)
- Steps to reproduce, and what you expected instead
- A small sample of the Markdown when it's relevant
## Pull requests
This repository is the public **distribution mirror** — the release build (`main.js`,
`styles.css`, `manifest.json`) is generated from a private development monorepo, so code
PRs against these built files can't be merged directly. For a fix or an idea, please open
an issue first and we'll land it at the source.
Documentation wording and translation suggestions are very welcome.
## Translations
marktile ships English, 日本語, 한국어, and 繁體中文. To add or correct a locale, open an
issue with the strings and we'll wire it in.
## License
By contributing you agree your contributions are licensed under the repository's
[MIT License](LICENSE).

View file

@ -127,7 +127,8 @@ function renumberLists(v) {
for (let i = 0; i < lines.length; i++) {
const m = /^(\d+)([.)])(\s)/.exec(lines[i]);
if (m) { n++; const want = n + m[2] + m[3]; if (m[0] !== want) { lines[i] = want + lines[i].slice(m[0].length); changed = true; } }
else n = 0; // blank / non-ordered line breaks the block → next ordered run restarts at 1
else if (/^[ \t]+\S/.test(lines[i])) continue; // indented continuation / nested list → part of the current item, leave the top-level counter alone
else n = 0; // blank / non-indented non-ordered line breaks the block → next ordered run restarts at 1
}
return changed ? lines.join('\n') : v;
}
@ -571,6 +572,10 @@ class TileEditModal extends Modal {
this.host._editModalOpen = true;
this.host.freezeBoard();
this.modalEl.addClass('tugtile-edit-modal-full');
// Tag the modal CONTAINER so the backdrop/alignment rules can target it directly instead of via
// .modal-container:has(.tugtile-edit-modal-full) — same timing as the modalEl class above, but no :has()
// selector invalidation. Guarded: the ejecta web shim's Modal may not expose containerEl.
if (this.containerEl) this.containerEl.addClass('tugtile-edit-host');
// Obsidian vault hooks (source path = the board file): image save/resolution. Computed up here so mountEditor's
// toolbar image/video buttons and equipEditor's paste handler share the same seam.
const app = this.app, srcPath = (this.view && this.view.file) ? this.view.file.path : '';

View file

@ -1,7 +1,7 @@
{
"id": "marktile",
"name": "marktile",
"version": "0.1.1",
"version": "0.1.2",
"minAppVersion": "1.4.0",
"description": "A Markdown editor where the markers never hide — headings grow while ## stays put. Pairs with tugtile. CJK-friendly.",
"author": "CVER Inc.",

View file

@ -394,9 +394,7 @@ body.is-phone .workspace-leaf-content[data-type="tugtile-board"] .view-content {
.tugtile-edit-modal-full {
width: 100vw;
max-width: 100vw;
height: 100vh;
height: 100dvh; /* Dynamic viewport: shrinks with the on-screen keyboard so the editor stays inside the visible area and the textarea can scroll */
max-height: 100vh;
height: 100dvh; /* Dynamic viewport: shrinks with the on-screen keyboard so the editor stays inside the visible area and the textarea can scroll. dvh is supported on every Obsidian-supported renderer (Electron + iOS/Android webview), so no 100vh fallback is needed. */
max-height: 100dvh;
border-radius: 0; /* Full-screen focus editor — nothing of the board shows behind */
/* Inset content past the iPad status bar / notch / home indicator; env() = 0 on desktop. The solid background still bleeds into these strips. */
@ -406,11 +404,11 @@ body.is-phone .workspace-leaf-content[data-type="tugtile-board"] .view-content {
animation: tugtile-pop-in 0.4s cubic-bezier(.34, 1.56, .64, 1); /* Keep the bounce — full-screen ≠ unrefined */
}
/* Backdrop darkens to solid FAST (so the board vanishes quickly, not lingering behind the bounce); zero container padding */
.modal-container:has(.tugtile-edit-modal-full) { padding: 0; align-items: flex-start; } /* Top-pinned, so shrinking the editor to the visual viewport (keyboard) keeps it above the keyboard */
.modal-container.tugtile-edit-host { padding: 0; align-items: flex-start; } /* Top-pinned, so shrinking the editor to the visual viewport (keyboard) keeps it above the keyboard. Class set in TileEditModal.onOpen — a direct container target, no parent-match selector. */
/* The status-bar / home-indicator strips show --background-secondary (white in light, grey in dark) and sit
outside the modal's reach. So instead of fighting to cover them, dye the WHOLE editor --background-secondary
to match seamless in both modes (the old --background-primary only matched in light mode). */
.modal-container:has(.tugtile-edit-modal-full) .modal-bg { background: var(--background-secondary); opacity: 1; animation: tugtile-bg-in 0.12s ease both; }
.modal-container.tugtile-edit-host .modal-bg { background: var(--background-secondary); opacity: 1; animation: tugtile-bg-in 0.12s ease both; }
.tugtile-edit-modal-full,
.tugtile-edit-modal-full .tugtile-edit-modal { background: var(--background-secondary); }
/* macOS: push the editor down so its top bar (✕) isn't covered by the window traffic lights (class is .mod-macos) */
@ -430,7 +428,7 @@ body.is-phone .workspace-leaf-content[data-type="tugtile-board"] .view-content {
to { opacity: 0; transform: scale(0.92); }
}
/* Small input modal (search / rename / add lane): sit near the top like Spotlight, so the virtual keyboard doesn't cover it. */
.modal-container:has(.tugtile-prompt-modal) { align-items: flex-start; }
.modal-container.tugtile-prompt-host { align-items: flex-start; }
.tugtile-prompt-modal {
margin-top: 10vh;
transform-origin: center;
@ -452,7 +450,7 @@ body.is-phone .tugtile-prompt-modal { margin-top: calc(env(safe-area-inset-top)
.tugtile-prompt-modal .modal-content { padding: 8px; justify-content: center; }
/* Dim the board behind the search modal (Obsidian forces the prompt backdrop transparent opacity:0 !important
so without this the board shows at full brightness and crowds the field on a narrow phone). */
.modal-container:has(.tugtile-prompt-modal) .modal-bg {
.modal-container.tugtile-prompt-host .modal-bg {
opacity: 1 !important;
background-color: rgba(0, 0, 0, 0.4);
-webkit-backdrop-filter: blur(16px);
@ -472,22 +470,21 @@ body.is-phone .tugtile-prompt-modal { margin-top: calc(env(safe-area-inset-top)
.tugtile-prompt-field:focus-within { border-color: var(--interactive-accent); }
.tugtile-prompt-fieldicon { flex: 0 0 auto; display: inline-flex; align-items: center; color: var(--text-muted); }
.tugtile-prompt-fieldicon svg { width: 1.15em; height: 1.15em; }
/* The input is bare the .tugtile-prompt-field IS the visible box. High-specificity selector + appearance:none
+ !important on every state to fully strip Obsidian's input chrome (the focus border/box-shadow was bigger
than the field and broke the nesting). */
input.tugtile-prompt-input,
input.tugtile-prompt-input:hover,
input.tugtile-prompt-input:focus,
input.tugtile-prompt-input:focus-visible,
input.tugtile-prompt-input:active {
flex: 1; min-width: 0; width: auto;
-webkit-appearance: none; appearance: none;
background: transparent !important;
border: 0 !important; border-radius: 0 !important;
outline: 0 !important; box-shadow: none !important;
padding: 0 !important; margin: 0 !important;
height: 100%; min-height: 0;
font-size: 16px; color: var(--text-normal); /* 16px prevents iOS auto-zoom on focus */
/* The input is our OWN contenteditable element (not a native <input>), so Obsidian's input chrome never
applies no overrides, no !important. The .tugtile-prompt-field IS the visible box; this is the bare
single-line text surface inside it. plaintext-only keeps paste/IME clean (no rich markup, CJK-safe). */
.tugtile-prompt-input {
flex: 1; min-width: 0;
outline: none;
white-space: pre; overflow-x: auto; overflow-y: hidden; /* single line: scroll sideways, never wrap */
font-size: 16px; color: var(--text-normal); /* 16px prevents iOS auto-zoom on focus */
-webkit-user-modify: read-write-plaintext-only;
}
.tugtile-prompt-input::-webkit-scrollbar { display: none; } /* hide the horizontal scrollbar on the one-line field */
.tugtile-prompt-input:empty::before {
content: attr(data-placeholder);
color: var(--text-faint);
pointer-events: none; /* placeholder text isn't selectable / doesn't block the caret */
}
/* .tugtile-prompt-x / -ok inherit from .tugtile-iconbtn (the ✓ colour override lives up there) */
.tugtile-edit-modal-full .modal-close-button { display: none; } /* Hides the native top-right ✕ button in favor of our custom ❌ button with safety checks */
@ -543,7 +540,7 @@ input.tugtile-prompt-input:active {
flex: 1; min-width: 0;
display: flex; flex-wrap: nowrap; gap: 4px; align-items: center;
justify-content: safe center; /* Centered in the bar (not crammed next to ✕); falls back to start-aligned + scrollable when it overflows */
overflow-x: auto; scrollbar-width: thin;
overflow-x: auto;
}
/* .tugtile-ed-tool, .tugtile-ed-find-b, .tugtile-prompt-x/ok all inherit everything from .tugtile-iconbtn above */
/* Group divider in the editor toolbar */
@ -553,10 +550,17 @@ input.tugtile-prompt-input:active {
flex: 0 0 auto;
display: flex; flex-wrap: nowrap; gap: 2px; align-items: center;
justify-content: safe center;
overflow-x: auto; scrollbar-width: thin; -webkit-overflow-scrolling: touch;
overflow-x: auto; -webkit-overflow-scrolling: touch;
padding: 6px 10px;
border-bottom: 1px solid var(--background-modifier-border);
}
/* Thin horizontal scrollbar for the overflowing toolbar rows, styled via ::-webkit-scrollbar Obsidian's
renderer is Chromium/WebKit on every platform, so this is the portable way to thin the bar (the standard
thin-scrollbar property is only partially supported by Obsidian's older builds). */
.tugtile-ed-tools::-webkit-scrollbar,
.tugtile-ed-tools2::-webkit-scrollbar { height: 6px; }
.tugtile-ed-tools::-webkit-scrollbar-thumb,
.tugtile-ed-tools2::-webkit-scrollbar-thumb { background: var(--background-modifier-border); border-radius: 3px; }
body.is-phone .tugtile-ed-sep { display: none; } /* no group separators in the compact phone rows */
/* Phone: pull the editor near the top so the toolbar hugs the Dynamic Island (4px breathing room below it) */
body.is-phone .tugtile-edit-modal-full { padding-top: 4px; }

View file

@ -1,3 +1,3 @@
{
"0.1.1": "1.4.0"
"0.1.2": "1.4.0"
}