2026-04-25 09:51:22 +00:00
|
|
|
/* ===== Floating toolbar ===== */
|
|
|
|
|
.tm-floating-toolbar {
|
2026-04-27 06:42:20 +00:00
|
|
|
/* We mount on <body> and rely on `position: fixed` so transformed editor
|
|
|
|
|
* ancestors cannot anchor the toolbar to themselves. (Position lives in
|
|
|
|
|
* CSS rather than inline styles; only the dynamic `top`/`left` are set
|
|
|
|
|
* imperatively by the view plugin.) */
|
2026-04-25 09:51:22 +00:00
|
|
|
position: fixed;
|
2026-04-29 10:02:58 +00:00
|
|
|
/* Stay above ordinary editor widgets but below Obsidian's modal/popover
|
|
|
|
|
* layers so a settings dialog (or any other Modal) is never visually
|
|
|
|
|
* buried by the toolbar. `--layer-popover` is well below `--layer-modal`
|
|
|
|
|
* in Obsidian, which gives us the right stacking order out of the box. */
|
|
|
|
|
z-index: var(--layer-popover, 30);
|
2026-04-25 09:51:22 +00:00
|
|
|
pointer-events: auto;
|
2026-04-29 10:02:58 +00:00
|
|
|
/* Row layout: content groups on the left, collapse toggle on the right. */
|
2026-04-25 09:51:22 +00:00
|
|
|
display: flex;
|
2026-04-29 10:02:58 +00:00
|
|
|
flex-direction: row;
|
2026-04-25 09:51:22 +00:00
|
|
|
align-items: stretch;
|
2026-04-29 10:02:58 +00:00
|
|
|
gap: 0;
|
2026-04-25 09:51:22 +00:00
|
|
|
padding: 4px;
|
|
|
|
|
background: var(--background-primary);
|
|
|
|
|
border: 1px solid var(--background-modifier-border);
|
|
|
|
|
border-radius: 6px;
|
|
|
|
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
|
|
|
|
|
font-size: var(--font-ui-smaller);
|
|
|
|
|
user-select: none;
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-27 06:42:20 +00:00
|
|
|
.tm-floating-toolbar.is-hidden {
|
2026-05-18 03:37:14 +00:00
|
|
|
display: none;
|
2026-04-27 06:42:20 +00:00
|
|
|
}
|
|
|
|
|
|
2026-04-29 10:02:58 +00:00
|
|
|
/* Inner wrapper that stacks functional groups vertically.
|
|
|
|
|
* Has its own overflow:hidden so the collapse animation clips content
|
|
|
|
|
* without affecting the toggle button. */
|
|
|
|
|
.tm-floating-toolbar .tm-toolbar-content {
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
align-items: stretch;
|
|
|
|
|
gap: 2px;
|
|
|
|
|
flex: 0 0 auto;
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
transition: width 0.3s ease, opacity 0.25s ease;
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-30 03:05:05 +00:00
|
|
|
/* While the collapse animation is in flight, the JS toggle adds
|
|
|
|
|
* `is-size-locked` and writes the measured size into CSS custom properties
|
|
|
|
|
* (`--tm-locked-width` / `--tm-locked-height`) via setCssProps. The class is
|
|
|
|
|
* removed (and the variables cleared) once the transition settles, so the
|
|
|
|
|
* content can reflow naturally on subsequent layout changes. */
|
|
|
|
|
.tm-floating-toolbar .tm-toolbar-content.is-size-locked {
|
|
|
|
|
width: var(--tm-locked-width);
|
|
|
|
|
height: var(--tm-locked-height);
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-29 10:02:58 +00:00
|
|
|
.tm-floating-toolbar.is-collapsed .tm-toolbar-content {
|
2026-05-18 03:37:14 +00:00
|
|
|
width: 0;
|
2026-04-29 10:02:58 +00:00
|
|
|
opacity: 0;
|
|
|
|
|
padding: 0;
|
|
|
|
|
gap: 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Collapse toggle: right edge, vertically centered, same size as toolbar buttons. */
|
|
|
|
|
.tm-floating-toolbar .tm-collapse-toggle {
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
justify-content: center;
|
|
|
|
|
min-width: 26px;
|
|
|
|
|
min-height: 26px;
|
|
|
|
|
padding: 4px;
|
|
|
|
|
margin-left: 2px;
|
|
|
|
|
border-left: 1px solid var(--background-modifier-border);
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
flex-shrink: 0;
|
|
|
|
|
background: transparent;
|
|
|
|
|
border-top: none;
|
|
|
|
|
border-right: none;
|
|
|
|
|
border-bottom: none;
|
|
|
|
|
border-radius: 4px;
|
|
|
|
|
color: var(--text-muted);
|
|
|
|
|
transition: margin 0.25s ease;
|
|
|
|
|
}
|
|
|
|
|
.tm-floating-toolbar .tm-collapse-toggle:hover {
|
|
|
|
|
background: var(--background-modifier-hover);
|
|
|
|
|
color: var(--text-normal);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* When collapsed the toolbar slides left past the viewport; content stays in
|
|
|
|
|
* the DOM to preserve its measured width for the slide calculation. The
|
|
|
|
|
* viewport edge naturally clips it. */
|
|
|
|
|
|
|
|
|
|
/* Toggle icon: same size as toolbar button icons + smooth rotation. */
|
|
|
|
|
.tm-floating-toolbar .tm-collapse-toggle .tm-icon {
|
|
|
|
|
width: 16px;
|
|
|
|
|
height: 16px;
|
|
|
|
|
transition: transform 0.3s ease;
|
|
|
|
|
}
|
|
|
|
|
.tm-floating-toolbar.is-collapsed .tm-collapse-toggle .tm-icon {
|
|
|
|
|
transform: rotate(180deg);
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-28 01:18:45 +00:00
|
|
|
/* Live Preview merged-cell placeholder: anchors keep their colspan/rowspan
|
|
|
|
|
* attributes, while the cells they cover are hidden via this class so the
|
|
|
|
|
* row/column geometry collapses around the anchor. Toggled in JS via
|
|
|
|
|
* classList instead of writing element.style.display directly. */
|
|
|
|
|
table td.tm-merge-placeholder,
|
|
|
|
|
table th.tm-merge-placeholder {
|
2026-05-18 03:37:14 +00:00
|
|
|
display: none;
|
2026-04-28 01:18:45 +00:00
|
|
|
}
|
|
|
|
|
|
2026-04-25 09:51:22 +00:00
|
|
|
.tm-floating-toolbar .tm-group {
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
flex-wrap: wrap;
|
|
|
|
|
gap: 1px;
|
|
|
|
|
padding: 2px 3px;
|
|
|
|
|
/* Visual separator between vertically-stacked groups. */
|
|
|
|
|
border-bottom: 1px solid var(--background-modifier-border);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.tm-floating-toolbar .tm-group:last-child {
|
|
|
|
|
border-bottom: none;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.tm-floating-toolbar button.tm-btn {
|
|
|
|
|
display: inline-flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
justify-content: center;
|
|
|
|
|
min-width: 26px;
|
|
|
|
|
height: 26px;
|
|
|
|
|
padding: 0 6px;
|
|
|
|
|
background: transparent;
|
|
|
|
|
border: none;
|
|
|
|
|
border-radius: 4px;
|
|
|
|
|
color: var(--text-muted);
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.tm-floating-toolbar button.tm-btn:hover {
|
|
|
|
|
background: var(--background-modifier-hover);
|
|
|
|
|
color: var(--text-normal);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.tm-floating-toolbar button.tm-btn.is-active {
|
|
|
|
|
background: var(--background-modifier-active-hover);
|
|
|
|
|
color: var(--text-accent);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.tm-floating-toolbar button.tm-btn .tm-icon {
|
|
|
|
|
width: 16px;
|
|
|
|
|
height: 16px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* ===== Modal grid editor ===== */
|
2026-04-29 10:02:58 +00:00
|
|
|
.tm-modal {
|
|
|
|
|
/* Ensure the modal is wide enough that multi-column tables
|
|
|
|
|
* are never squeezed down to a single visible column. */
|
|
|
|
|
min-width: min(90vw, 600px);
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-25 09:51:22 +00:00
|
|
|
.tm-modal .modal-content {
|
|
|
|
|
padding: 0;
|
2026-04-29 10:02:58 +00:00
|
|
|
overflow: visible;
|
2026-04-25 09:51:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.tm-grid-toolbar {
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-wrap: wrap;
|
|
|
|
|
gap: 4px;
|
|
|
|
|
padding: 8px 12px;
|
|
|
|
|
border-bottom: 1px solid var(--background-modifier-border);
|
|
|
|
|
background: var(--background-secondary);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.tm-grid-toolbar button {
|
|
|
|
|
height: 28px;
|
|
|
|
|
padding: 0 10px;
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-30 04:39:42 +00:00
|
|
|
.tm-grid-toolbar button.tm-grid-icon-btn {
|
|
|
|
|
padding: 0 6px;
|
|
|
|
|
display: inline-flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
justify-content: center;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.tm-grid-toolbar button.tm-grid-icon-btn .svg-icon {
|
|
|
|
|
width: 16px;
|
|
|
|
|
height: 16px;
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-25 09:51:22 +00:00
|
|
|
.tm-grid-wrap {
|
|
|
|
|
max-height: 60vh;
|
|
|
|
|
overflow: auto;
|
|
|
|
|
padding: 8px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
table.tm-grid {
|
|
|
|
|
border-collapse: collapse;
|
2026-04-29 10:02:58 +00:00
|
|
|
/* `table-layout: auto` lets the browser size columns to their content.
|
|
|
|
|
* `width: 100%` fills the wrapper; wider tables cause `.tm-grid-wrap`
|
|
|
|
|
* to scroll horizontally. */
|
|
|
|
|
table-layout: auto;
|
2026-04-25 09:51:22 +00:00
|
|
|
width: 100%;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
table.tm-grid th,
|
|
|
|
|
table.tm-grid td {
|
|
|
|
|
border: 1px solid var(--background-modifier-border);
|
|
|
|
|
padding: 4px 8px;
|
|
|
|
|
min-width: 80px;
|
|
|
|
|
vertical-align: top;
|
|
|
|
|
position: relative;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
table.tm-grid th {
|
|
|
|
|
background: var(--background-secondary);
|
|
|
|
|
font-weight: 600;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
table.tm-grid td.tm-selected,
|
|
|
|
|
table.tm-grid th.tm-selected {
|
|
|
|
|
outline: 2px solid var(--interactive-accent);
|
|
|
|
|
outline-offset: -2px;
|
|
|
|
|
background: var(--background-modifier-hover);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
table.tm-grid .tm-cell-edit {
|
|
|
|
|
display: block;
|
|
|
|
|
width: 100%;
|
|
|
|
|
min-height: 1.4em;
|
|
|
|
|
outline: none;
|
|
|
|
|
white-space: pre-wrap;
|
|
|
|
|
word-break: break-word;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.tm-grid-actions {
|
|
|
|
|
display: flex;
|
|
|
|
|
justify-content: flex-end;
|
|
|
|
|
gap: 8px;
|
|
|
|
|
padding: 10px 12px;
|
|
|
|
|
border-top: 1px solid var(--background-modifier-border);
|
|
|
|
|
background: var(--background-secondary);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* ===== Reading view & Live Preview rebuilt tables ===== */
|
|
|
|
|
/* When the post-processor / live-preview view plugin rewrites a table,
|
|
|
|
|
* Obsidian's widget-scoped CSS (e.g. `.cm-table-widget td`) no longer matches
|
|
|
|
|
* because we replace `<table>`'s children. Re-establish the cell metrics here
|
|
|
|
|
* so the rebuilt table never looks more cramped than the original GFM render.
|
|
|
|
|
*/
|
|
|
|
|
table.tm-rendered {
|
|
|
|
|
border-collapse: collapse;
|
|
|
|
|
margin: 0.5em 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
table.tm-rendered caption {
|
|
|
|
|
caption-side: top;
|
|
|
|
|
font-style: italic;
|
|
|
|
|
color: var(--text-muted);
|
|
|
|
|
padding: 4px 0;
|
|
|
|
|
text-align: center;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
table.tm-rendered th,
|
|
|
|
|
table.tm-rendered td {
|
|
|
|
|
padding: var(--table-cell-padding, 6px 12px);
|
|
|
|
|
border: 1px solid var(--background-modifier-border);
|
|
|
|
|
vertical-align: top;
|
|
|
|
|
line-height: var(--line-height-normal, 1.5);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
table.tm-rendered th {
|
|
|
|
|
background: var(--background-secondary);
|
|
|
|
|
font-weight: var(--font-semibold, 600);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
table.tm-rendered tbody + tbody {
|
|
|
|
|
border-top: 2px solid var(--background-modifier-border);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Highlight merged anchors so the user can spot the merged region at a
|
|
|
|
|
* glance. The legacy `.tm-merged-anchor` class is kept for backward compat,
|
|
|
|
|
* but the actual marker on rendered tables is `data-tm-merge="anchor"`
|
|
|
|
|
* (set by Live Preview) and the `[rowspan]` / `[colspan]` attributes
|
|
|
|
|
* (set by Reading-view rebuild + grid editor). */
|
|
|
|
|
table.tm-rendered td.tm-merged-anchor,
|
|
|
|
|
table.tm-rendered th.tm-merged-anchor,
|
|
|
|
|
table.tm-rendered td[rowspan]:not([rowspan="1"]),
|
|
|
|
|
table.tm-rendered th[rowspan]:not([rowspan="1"]),
|
|
|
|
|
table.tm-rendered td[colspan]:not([colspan="1"]),
|
|
|
|
|
table.tm-rendered th[colspan]:not([colspan="1"]),
|
|
|
|
|
table td[data-tm-merge="anchor"],
|
|
|
|
|
table th[data-tm-merge="anchor"] {
|
|
|
|
|
background-color: var(--background-secondary-alt);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Vertically center the text inside any cell that spans more than one row.
|
|
|
|
|
* Covers all three renderers: Reading view (`table.tm-rendered td[rowspan]`),
|
|
|
|
|
* Live Preview (anchor cells flagged with `data-tm-merge="anchor"`), and the
|
|
|
|
|
* modal grid editor (`table.tm-grid`). Merging up / down should feel
|
|
|
|
|
* symmetric — the merged text sits between the rows it now spans. */
|
|
|
|
|
table.tm-rendered td[rowspan]:not([rowspan="1"]),
|
|
|
|
|
table.tm-rendered th[rowspan]:not([rowspan="1"]),
|
|
|
|
|
table td[data-tm-merge="anchor"][rowspan]:not([rowspan="1"]),
|
|
|
|
|
table th[data-tm-merge="anchor"][rowspan]:not([rowspan="1"]),
|
|
|
|
|
table.tm-grid td[rowspan]:not([rowspan="1"]),
|
|
|
|
|
table.tm-grid th[rowspan]:not([rowspan="1"]) {
|
|
|
|
|
vertical-align: middle;
|
|
|
|
|
}
|