diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..61e31a5 --- /dev/null +++ b/CONTRIBUTING.md @@ -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). diff --git a/main.js b/main.js index 5ad3ba7..e697f08 100644 --- a/main.js +++ b/main.js @@ -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 : ''; diff --git a/manifest.json b/manifest.json index eba6522..f4247f4 100644 --- a/manifest.json +++ b/manifest.json @@ -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.", diff --git a/styles.css b/styles.css index df1f1e3..6624c64 100644 --- a/styles.css +++ b/styles.css @@ -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 ), 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; } diff --git a/versions.json b/versions.json index 739b7bf..b76f1b2 100644 --- a/versions.json +++ b/versions.json @@ -1,3 +1,3 @@ { - "0.1.1": "1.4.0" + "0.1.2": "1.4.0" }