mirror of
https://github.com/moranrs/table-master.git
synced 2026-07-22 06:53:11 +00:00
fix: address ObsidianReviewBot required findings
- src/editor/actions.ts: drop two unnecessary type assertions on navigator.clipboard
- src/i18n/en.ts: rewrite 9 strings into sentence case (drop button-name parentheticals like ' (Tab)' and 'Excel/web')
- src/i18n/zh.ts: drop now-unused plugin.name key
- src/main.ts: split async init out of onload into a private bootstrap()
so Plugin.onload's declared void return type is preserved
- src/main.ts, src/i18n/index.ts, src/editor/actions.ts: remove unused
'_' catch parameters (use bare 'catch')
- src/render/tableRenderer.ts: replace 'Function' with an explicit
MarkdownRenderFn signature for the renderer.render fallback
- src/settings.ts: drop the manual createEl('h2', ...) heading;
Obsidian renders the plugin name as the settings-tab heading
- src/ui/floatingToolbar.ts: replace inline element.style.display
writes with an 'is-hidden' CSS class toggle, drop redundant
inline 'position: fixed' (now CSS-only), and parse SVG icons via
DOMParser instead of innerHTML
- styles.css: add .tm-floating-toolbar.is-hidden rule
- add eslint.config.mjs + dev dependencies so contributors can
reproduce ObsidianReviewBot's checks locally
This commit is contained in:
parent
e686290346
commit
8af1ff7f57
13 changed files with 4931 additions and 36 deletions
3
.gitignore
vendored
3
.gitignore
vendored
|
|
@ -42,6 +42,9 @@ coverage/
|
||||||
.nyc_output/
|
.nyc_output/
|
||||||
*.lcov
|
*.lcov
|
||||||
|
|
||||||
|
# ---------- Lint output ----------
|
||||||
|
eslint-report.txt
|
||||||
|
|
||||||
# ---------- Misc ----------
|
# ---------- Misc ----------
|
||||||
.env
|
.env
|
||||||
.env.local
|
.env.local
|
||||||
|
|
|
||||||
44
eslint.config.mjs
Normal file
44
eslint.config.mjs
Normal file
|
|
@ -0,0 +1,44 @@
|
||||||
|
// ESLint flat config that mirrors `ObsidianReviewBot` so violations can be
|
||||||
|
// reproduced locally before re-pushing.
|
||||||
|
import tsParser from "@typescript-eslint/parser";
|
||||||
|
import tsPlugin from "@typescript-eslint/eslint-plugin";
|
||||||
|
import obsidianmdPkg from "eslint-plugin-obsidianmd";
|
||||||
|
|
||||||
|
const obsidianmd = obsidianmdPkg.default ?? obsidianmdPkg;
|
||||||
|
|
||||||
|
// Hand-pick rules so we don't have to rely on the plugin's preset format.
|
||||||
|
const obsidianRules = {};
|
||||||
|
for (const ruleName of Object.keys(obsidianmd.rules ?? {})) {
|
||||||
|
obsidianRules[`obsidianmd/${ruleName}`] = "error";
|
||||||
|
}
|
||||||
|
|
||||||
|
export default [
|
||||||
|
{
|
||||||
|
files: ["src/**/*.ts"],
|
||||||
|
languageOptions: {
|
||||||
|
parser: tsParser,
|
||||||
|
parserOptions: {
|
||||||
|
ecmaVersion: 2022,
|
||||||
|
sourceType: "module",
|
||||||
|
project: "./tsconfig.json",
|
||||||
|
tsconfigRootDir: import.meta.dirname,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
plugins: {
|
||||||
|
"@typescript-eslint": tsPlugin,
|
||||||
|
obsidianmd,
|
||||||
|
},
|
||||||
|
rules: {
|
||||||
|
...(tsPlugin.configs?.recommended?.rules ?? {}),
|
||||||
|
...obsidianRules,
|
||||||
|
// Disable rules that need full `parserOptions.project` type info we
|
||||||
|
// don't wire up here.
|
||||||
|
"@typescript-eslint/no-unsafe-function-type": "error",
|
||||||
|
"@typescript-eslint/no-misused-promises": "error",
|
||||||
|
"@typescript-eslint/no-unused-vars": [
|
||||||
|
"error",
|
||||||
|
{ argsIgnorePattern: "^_", varsIgnorePattern: "^_" },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
4811
package-lock.json
generated
4811
package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
|
@ -21,8 +21,12 @@
|
||||||
"@codemirror/state": "6.5.0",
|
"@codemirror/state": "6.5.0",
|
||||||
"@codemirror/view": "6.23.0",
|
"@codemirror/view": "6.23.0",
|
||||||
"@types/node": "^20.10.0",
|
"@types/node": "^20.10.0",
|
||||||
|
"@typescript-eslint/eslint-plugin": "^8.59.0",
|
||||||
|
"@typescript-eslint/parser": "^8.59.0",
|
||||||
"builtin-modules": "^3.3.0",
|
"builtin-modules": "^3.3.0",
|
||||||
"esbuild": "^0.20.0",
|
"esbuild": "^0.20.0",
|
||||||
|
"eslint": "^9.39.4",
|
||||||
|
"eslint-plugin-obsidianmd": "^0.2.4",
|
||||||
"happy-dom": "^20.9.0",
|
"happy-dom": "^20.9.0",
|
||||||
"obsidian": "1.5.7",
|
"obsidian": "1.5.7",
|
||||||
"tslib": "^2.6.2",
|
"tslib": "^2.6.2",
|
||||||
|
|
|
||||||
|
|
@ -30,7 +30,7 @@ function resolve(editor: Editor): ResolvedLocation | null {
|
||||||
let model: TableModel;
|
let model: TableModel;
|
||||||
try {
|
try {
|
||||||
model = parseTable(loc.text).model;
|
model = parseTable(loc.text).model;
|
||||||
} catch (_) {
|
} catch {
|
||||||
new Notice(t("notice.notInTable"));
|
new Notice(t("notice.notInTable"));
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
@ -341,10 +341,10 @@ interface ClipboardPayload {
|
||||||
|
|
||||||
async function readClipboardPayload(): Promise<ClipboardPayload | null> {
|
async function readClipboardPayload(): Promise<ClipboardPayload | null> {
|
||||||
// Modern path: navigator.clipboard.read() exposes both text/html and text/plain.
|
// Modern path: navigator.clipboard.read() exposes both text/html and text/plain.
|
||||||
const nav = (typeof navigator !== "undefined" ? navigator : null) as Navigator | null;
|
const nav = typeof navigator !== "undefined" ? navigator : null;
|
||||||
if (nav && "clipboard" in nav && typeof (nav.clipboard as Clipboard & { read?: () => Promise<ClipboardItem[]> }).read === "function") {
|
if (nav && "clipboard" in nav && typeof (nav.clipboard as Clipboard & { read?: () => Promise<ClipboardItem[]> }).read === "function") {
|
||||||
try {
|
try {
|
||||||
const items = await (nav.clipboard as Clipboard).read();
|
const items = await nav.clipboard.read();
|
||||||
const out: ClipboardPayload = {};
|
const out: ClipboardPayload = {};
|
||||||
for (const item of items) {
|
for (const item of items) {
|
||||||
if (item.types.includes("text/html") && !out.html) {
|
if (item.types.includes("text/html") && !out.html) {
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,4 @@
|
||||||
export const en = {
|
export const en = {
|
||||||
// Plugin
|
|
||||||
"plugin.name": "Table Master",
|
|
||||||
|
|
||||||
// Commands
|
// Commands
|
||||||
"cmd.insertRowAbove": "Insert row above",
|
"cmd.insertRowAbove": "Insert row above",
|
||||||
"cmd.insertRowBelow": "Insert row below",
|
"cmd.insertRowBelow": "Insert row below",
|
||||||
|
|
@ -26,15 +23,15 @@ export const en = {
|
||||||
"cmd.sortDesc": "Sort column descending",
|
"cmd.sortDesc": "Sort column descending",
|
||||||
"cmd.openGridEditor": "Open grid editor",
|
"cmd.openGridEditor": "Open grid editor",
|
||||||
"cmd.toggleFloating": "Toggle floating toolbar",
|
"cmd.toggleFloating": "Toggle floating toolbar",
|
||||||
"cmd.nextCell": "Next cell (Tab)",
|
"cmd.nextCell": "Next cell",
|
||||||
"cmd.prevCell": "Previous cell (Shift-Tab)",
|
"cmd.prevCell": "Previous cell",
|
||||||
"cmd.newRowEnter": "New row (Enter)",
|
"cmd.newRowEnter": "New row on enter",
|
||||||
"cmd.newTable": "Insert new table…",
|
"cmd.newTable": "Insert new table…",
|
||||||
"cmd.designNewTable": "Design new table in grid editor…",
|
"cmd.designNewTable": "Design new table in grid editor…",
|
||||||
"cmd.insertCaption": "Set table caption…",
|
"cmd.insertCaption": "Set table caption…",
|
||||||
"cmd.toggleHeader": "Toggle table header",
|
"cmd.toggleHeader": "Toggle table header",
|
||||||
"cmd.toggleTbodyBreak": "Toggle tbody break before this row",
|
"cmd.toggleTbodyBreak": "Toggle tbody break before this row",
|
||||||
"cmd.importTable": "Paste table from clipboard (Excel / web)",
|
"cmd.importTable": "Paste table from clipboard",
|
||||||
|
|
||||||
// Toolbar tooltips
|
// Toolbar tooltips
|
||||||
"tip.insertRowAbove": "Insert row above",
|
"tip.insertRowAbove": "Insert row above",
|
||||||
|
|
@ -56,7 +53,7 @@ export const en = {
|
||||||
"tip.split": "Split cell",
|
"tip.split": "Split cell",
|
||||||
"tip.gridEditor": "Open grid editor",
|
"tip.gridEditor": "Open grid editor",
|
||||||
"tip.format": "Format",
|
"tip.format": "Format",
|
||||||
"tip.importTable": "Paste table from clipboard (Excel / web)",
|
"tip.importTable": "Paste table from clipboard",
|
||||||
|
|
||||||
// Modal
|
// Modal
|
||||||
"modal.title": "Table grid editor",
|
"modal.title": "Table grid editor",
|
||||||
|
|
@ -71,7 +68,7 @@ export const en = {
|
||||||
"modal.alignRight": "Right",
|
"modal.alignRight": "Right",
|
||||||
"modal.ok": "Apply",
|
"modal.ok": "Apply",
|
||||||
"modal.cancel": "Cancel",
|
"modal.cancel": "Cancel",
|
||||||
"modal.hint": "Click to select a cell. Drag or Shift-click to select a range.",
|
"modal.hint": "Click to select a cell. Drag or shift-click to select a range.",
|
||||||
|
|
||||||
// New table prompt
|
// New table prompt
|
||||||
"newTable.title": "Insert new table",
|
"newTable.title": "Insert new table",
|
||||||
|
|
@ -109,8 +106,8 @@ export const en = {
|
||||||
"set.toolbarPosition.onClick": "Pop up at click position when clicking a table (default)",
|
"set.toolbarPosition.onClick": "Pop up at click position when clicking a table (default)",
|
||||||
"set.toolbarPosition.followMouse": "Follow mouse inside table; top-left otherwise",
|
"set.toolbarPosition.followMouse": "Follow mouse inside table; top-left otherwise",
|
||||||
"set.toolbarPosition.topLeft": "Always at the editor's top-left",
|
"set.toolbarPosition.topLeft": "Always at the editor's top-left",
|
||||||
"set.tabNavigation": "Enable Tab navigation",
|
"set.tabNavigation": "Enable tab navigation",
|
||||||
"set.tabNavigation.desc": "Use Tab / Shift-Tab / Enter to move between cells.",
|
"set.tabNavigation.desc": "Use tab, shift-tab or enter to move between cells.",
|
||||||
"set.defaultAlign": "Default column alignment",
|
"set.defaultAlign": "Default column alignment",
|
||||||
"set.language": "Interface language",
|
"set.language": "Interface language",
|
||||||
"set.language.auto": "Auto",
|
"set.language.auto": "Auto",
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,7 @@ function detectObsidianLocale(): string {
|
||||||
if (w?.moment?.locale) {
|
if (w?.moment?.locale) {
|
||||||
try {
|
try {
|
||||||
return w.moment.locale() ?? "en";
|
return w.moment.locale() ?? "en";
|
||||||
} catch (_) {
|
} catch {
|
||||||
// ignore
|
// ignore
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,6 @@
|
||||||
import type { Dict } from "./en";
|
import type { Dict } from "./en";
|
||||||
|
|
||||||
export const zh: Dict = {
|
export const zh: Dict = {
|
||||||
"plugin.name": "Table Master 表格大师",
|
|
||||||
|
|
||||||
"cmd.insertRowAbove": "在上方插入行",
|
"cmd.insertRowAbove": "在上方插入行",
|
||||||
"cmd.insertRowBelow": "在下方插入行",
|
"cmd.insertRowBelow": "在下方插入行",
|
||||||
"cmd.insertColLeft": "在左侧插入列",
|
"cmd.insertColLeft": "在左侧插入列",
|
||||||
|
|
|
||||||
18
src/main.ts
18
src/main.ts
|
|
@ -25,7 +25,19 @@ const CONFLICT_PLUGIN_IDS = [
|
||||||
export default class TableMasterPlugin extends Plugin {
|
export default class TableMasterPlugin extends Plugin {
|
||||||
settings: TableMasterSettings = DEFAULT_SETTINGS;
|
settings: TableMasterSettings = DEFAULT_SETTINGS;
|
||||||
|
|
||||||
async onload(): Promise<void> {
|
onload(): void {
|
||||||
|
// Obsidian's `Plugin.onload` signature is declared as `void` even though
|
||||||
|
// it accepts a Promise at runtime; declaring this method `async` makes
|
||||||
|
// typescript-eslint's `no-misused-promises` flag the override mismatch.
|
||||||
|
// Keep the wrapper synchronous and run the actual init in a fire-and-
|
||||||
|
// forget helper. We purposely register every editor extension and
|
||||||
|
// command immediately (without waiting for `loadSettings`) so plugin
|
||||||
|
// lifecycle is consistent; settings have a sane default value (see
|
||||||
|
// `DEFAULT_SETTINGS`) until the on-disk copy finishes loading.
|
||||||
|
void this.bootstrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
private async bootstrap(): Promise<void> {
|
||||||
await this.loadSettings();
|
await this.loadSettings();
|
||||||
setLanguage(this.settings.language);
|
setLanguage(this.settings.language);
|
||||||
|
|
||||||
|
|
@ -240,7 +252,7 @@ export default class TableMasterPlugin extends Plugin {
|
||||||
let result: Awaited<ReturnType<typeof actions.importTableFromClipboard>>;
|
let result: Awaited<ReturnType<typeof actions.importTableFromClipboard>>;
|
||||||
try {
|
try {
|
||||||
result = await actions.importTableFromClipboard(ctx);
|
result = await actions.importTableFromClipboard(ctx);
|
||||||
} catch (_) {
|
} catch {
|
||||||
new Notice(t("notice.importFailed"));
|
new Notice(t("notice.importFailed"));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -270,7 +282,7 @@ export default class TableMasterPlugin extends Plugin {
|
||||||
let model: TableModel;
|
let model: TableModel;
|
||||||
try {
|
try {
|
||||||
model = parseTable(loc.text).model;
|
model = parseTable(loc.text).model;
|
||||||
} catch (_) {
|
} catch {
|
||||||
new Notice(t("notice.notInTable"));
|
new Notice(t("notice.notInTable"));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -117,8 +117,14 @@ async function renderCellsInline(
|
||||||
try {
|
try {
|
||||||
// Newer Obsidian versions expose `render`; older ones `renderMarkdown`.
|
// Newer Obsidian versions expose `render`; older ones `renderMarkdown`.
|
||||||
// Both have signature (markdown, el, sourcePath, component).
|
// Both have signature (markdown, el, sourcePath, component).
|
||||||
const fn = (renderer as unknown as { render?: Function }).render
|
type MarkdownRenderFn = (
|
||||||
?? (renderer as unknown as { renderMarkdown?: Function }).renderMarkdown;
|
markdown: string,
|
||||||
|
el: HTMLElement,
|
||||||
|
sourcePath: string,
|
||||||
|
component: Component,
|
||||||
|
) => Promise<unknown>;
|
||||||
|
const fn = (renderer as unknown as { render?: MarkdownRenderFn }).render
|
||||||
|
?? (renderer as unknown as { renderMarkdown?: MarkdownRenderFn }).renderMarkdown;
|
||||||
if (typeof fn === "function") {
|
if (typeof fn === "function") {
|
||||||
await fn.call(renderer, text, td, opts.sourcePath, opts.component);
|
await fn.call(renderer, text, td, opts.sourcePath, opts.component);
|
||||||
unwrapTrailingParagraph(td);
|
unwrapTrailingParagraph(td);
|
||||||
|
|
|
||||||
|
|
@ -60,7 +60,9 @@ export class TableMasterSettingTab extends PluginSettingTab {
|
||||||
display(): void {
|
display(): void {
|
||||||
const { containerEl } = this;
|
const { containerEl } = this;
|
||||||
containerEl.empty();
|
containerEl.empty();
|
||||||
containerEl.createEl("h2", { text: t("plugin.name") });
|
// Obsidian renders the plugin name as the settings-tab heading
|
||||||
|
// automatically, so we don't add a manual <h2> here (which would also
|
||||||
|
// trip `obsidianmd/settings-tab/no-manual-html-headings`).
|
||||||
|
|
||||||
new Setting(containerEl)
|
new Setting(containerEl)
|
||||||
.setName(t("set.language"))
|
.setName(t("set.language"))
|
||||||
|
|
|
||||||
|
|
@ -46,7 +46,11 @@ export function buildFloatingToolbarExt(host: ToolbarHost) {
|
||||||
// whose CM6 destroy() hook didn't fire (Obsidian doesn't always
|
// whose CM6 destroy() hook didn't fire (Obsidian doesn't always
|
||||||
// reconfigure already-open editor views on plugin unload).
|
// reconfigure already-open editor views on plugin unload).
|
||||||
this.dom.dataset.tmFloatingToolbar = "1";
|
this.dom.dataset.tmFloatingToolbar = "1";
|
||||||
this.dom.style.display = "none";
|
// Hidden until refresh() decides to show it. Toggled via the
|
||||||
|
// `is-hidden` CSS class (defined in styles.css) rather than an inline
|
||||||
|
// `display` style — obsidianmd lint forbids static style.display
|
||||||
|
// assignments because they bypass theme overrides.
|
||||||
|
this.dom.classList.add("is-hidden");
|
||||||
// Stop bubbling to avoid Obsidian re-focusing editor and dropping cursor
|
// Stop bubbling to avoid Obsidian re-focusing editor and dropping cursor
|
||||||
this.dom.addEventListener("mousedown", (e) => e.preventDefault());
|
this.dom.addEventListener("mousedown", (e) => e.preventDefault());
|
||||||
// IMPORTANT: mount on <body>, not view.dom.parentElement. Some
|
// IMPORTANT: mount on <body>, not view.dom.parentElement. Some
|
||||||
|
|
@ -232,8 +236,8 @@ export function buildFloatingToolbarExt(host: ToolbarHost) {
|
||||||
|
|
||||||
/** Make the toolbar measurable & visible without leaving it at a stale position. */
|
/** Make the toolbar measurable & visible without leaving it at a stale position. */
|
||||||
private ensureVisible() {
|
private ensureVisible() {
|
||||||
if (this.dom.style.display === "none") {
|
if (this.dom.classList.contains("is-hidden")) {
|
||||||
this.dom.style.display = "inline-flex";
|
this.dom.classList.remove("is-hidden");
|
||||||
this.visible = true;
|
this.visible = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -327,7 +331,7 @@ export function buildFloatingToolbarExt(host: ToolbarHost) {
|
||||||
|
|
||||||
hide() {
|
hide() {
|
||||||
if (!this.visible) return;
|
if (!this.visible) return;
|
||||||
this.dom.style.display = "none";
|
this.dom.classList.add("is-hidden");
|
||||||
this.visible = false;
|
this.visible = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -339,7 +343,9 @@ export function buildFloatingToolbarExt(host: ToolbarHost) {
|
||||||
*/
|
*/
|
||||||
private placeTopLeft(view: EditorView) {
|
private placeTopLeft(view: EditorView) {
|
||||||
const rect = view.dom.getBoundingClientRect();
|
const rect = view.dom.getBoundingClientRect();
|
||||||
this.dom.style.position = "fixed";
|
// `position: fixed` is declared in styles.css; we only set the dynamic
|
||||||
|
// top / left here. Inline display/position assignments would trip
|
||||||
|
// obsidianmd's no-static-styles-assignment rule.
|
||||||
this.ensureVisible();
|
this.ensureVisible();
|
||||||
this.dom.style.top = `${Math.max(rect.top, 0)}px`;
|
this.dom.style.top = `${Math.max(rect.top, 0)}px`;
|
||||||
this.dom.style.left = `${Math.max(rect.left, 0)}px`;
|
this.dom.style.left = `${Math.max(rect.left, 0)}px`;
|
||||||
|
|
@ -352,7 +358,8 @@ export function buildFloatingToolbarExt(host: ToolbarHost) {
|
||||||
* toolbar is never partially off-screen.
|
* toolbar is never partially off-screen.
|
||||||
*/
|
*/
|
||||||
private placeAtMouse() {
|
private placeAtMouse() {
|
||||||
this.dom.style.position = "fixed";
|
// `position: fixed` lives in styles.css; only the dynamic top/left
|
||||||
|
// are set inline (see comment in placeTopLeft).
|
||||||
this.ensureVisible();
|
this.ensureVisible();
|
||||||
const myWidth = this.dom.offsetWidth || 200;
|
const myWidth = this.dom.offsetWidth || 200;
|
||||||
const myHeight = this.dom.offsetHeight || 80;
|
const myHeight = this.dom.offsetHeight || 80;
|
||||||
|
|
@ -413,7 +420,15 @@ export function buildFloatingToolbarExt(host: ToolbarHost) {
|
||||||
const btn = g.createEl("button", { cls: "tm-btn" });
|
const btn = g.createEl("button", { cls: "tm-btn" });
|
||||||
btn.setAttribute("aria-label", item.tip);
|
btn.setAttribute("aria-label", item.tip);
|
||||||
btn.title = item.tip;
|
btn.title = item.tip;
|
||||||
btn.innerHTML = item.icon;
|
// Parse the bundled SVG via DOMParser instead of `innerHTML =` so
|
||||||
|
// the obsidianmd lint rule banning innerHTML/outerHTML writes is
|
||||||
|
// satisfied. The strings live in `Icons` and are entirely
|
||||||
|
// controlled by us, so the parser sees only trusted markup.
|
||||||
|
const parsedIcon = new DOMParser().parseFromString(item.icon, "image/svg+xml");
|
||||||
|
const iconEl = parsedIcon.documentElement;
|
||||||
|
if (iconEl && iconEl.nodeName.toLowerCase() === "svg") {
|
||||||
|
btn.appendChild(iconEl);
|
||||||
|
}
|
||||||
btn.addEventListener("click", (e) => {
|
btn.addEventListener("click", (e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
|
|
|
||||||
15
styles.css
15
styles.css
|
|
@ -1,8 +1,9 @@
|
||||||
/* ===== Floating toolbar ===== */
|
/* ===== Floating toolbar ===== */
|
||||||
.tm-floating-toolbar {
|
.tm-floating-toolbar {
|
||||||
/* Position is set imperatively by the view plugin; we mount on <body> and
|
/* We mount on <body> and rely on `position: fixed` so transformed editor
|
||||||
* always switch to `position: fixed` so transformed editor ancestors
|
* ancestors cannot anchor the toolbar to themselves. (Position lives in
|
||||||
* cannot anchor the toolbar to themselves. */
|
* CSS rather than inline styles; only the dynamic `top`/`left` are set
|
||||||
|
* imperatively by the view plugin.) */
|
||||||
position: fixed;
|
position: fixed;
|
||||||
/* Some Obsidian themes bump table-widget z-index up into the 30-40s; keep
|
/* Some Obsidian themes bump table-widget z-index up into the 30-40s; keep
|
||||||
* the toolbar above any of them so it's never visually buried. */
|
* the toolbar above any of them so it's never visually buried. */
|
||||||
|
|
@ -24,6 +25,14 @@
|
||||||
user-select: none;
|
user-select: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.tm-floating-toolbar.is-hidden {
|
||||||
|
/* Toggled by the view plugin via classList instead of inline display
|
||||||
|
* styles. `!important` is used so a theme that bumps `display` on
|
||||||
|
* `.tm-floating-toolbar` cannot accidentally re-show the toolbar while
|
||||||
|
* the plugin considers it hidden. */
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
.tm-floating-toolbar .tm-group {
|
.tm-floating-toolbar .tm-group {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue