fix(mdedit): two-line ZDDC tree display + dark-mode editor contrast
Two issues from one session: * File tree: ZDDC-conforming filenames render as a single line even though the JS already produced two-div markup (filename-main + filename-secondary). Cause: .tree-row__label was display:flex (row-direction), so the two divs laid out side-by-side. Fix: wrap each label's text in a new .tree-row__name span styled flex-direction:column. Both file and folder code paths use the same wrapper now; non-ZDDC entries collapse to a single .filename-main line so typography stays consistent across the tree. Tested by injecting a ZDDC filename into a mock directory and asserting filename-secondary's bounding-box top is below filename-main's bottom. * Toast UI Editor was unreadable in dark mode. Toast UI ships with light-only chrome; its .toastui-editor-md-container has color #222 on a transparent bg, so when mdedit's dark theme rendered the surrounding pane in #1e1e1e the editor text fell on near-black background → effectively invisible. Fix: add CSS overrides in mdedit/css/editor.css that target the editor's load-bearing surfaces (md-container, md-preview, ww-container, ProseMirror, toolbar, mode-switch tabs, popups) and apply var(--bg) / var(--text). Toolbar icons get a filter:invert(0.85) hue-rotate to flip the sprite-baked dark glyphs. Both manual override (data-theme="dark") and OS-pref auto fallback (prefers-color-scheme) are covered. Tested by computing contrast ratios on every editor surface in dark mode — all came in at 10:1+ (well above WCAG AA's 4.5:1). Embedded snapshots refreshed to current main HEAD's dev build label. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
6cc0d2ae27
commit
8c2e65e4a2
10 changed files with 6463 additions and 28 deletions
|
|
@ -337,13 +337,25 @@
|
||||||
flex: 1;
|
flex: 1;
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
white-space: nowrap;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 0.25rem;
|
gap: 0.25rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* The text wrapper inside a tree-row label. For ZDDC-conforming files and
|
||||||
|
folders, this wraps two stacked <div>s (filename-main + filename-secondary)
|
||||||
|
so the row reads top-to-bottom as title + metadata — same shape the archive
|
||||||
|
tool uses for its transmittal-folder list. For non-ZDDC entries it just
|
||||||
|
contains a single line. flex column makes the two-line case work; min-width:0
|
||||||
|
lets each line truncate independently. */
|
||||||
|
.tree-row__name {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
min-width: 0;
|
||||||
|
flex: 1;
|
||||||
|
line-height: 1.25;
|
||||||
|
}
|
||||||
|
|
||||||
/* ── New-file modal ─────────────────────────────────────────────────────────── */
|
/* ── New-file modal ─────────────────────────────────────────────────────────── */
|
||||||
.modal-overlay {
|
.modal-overlay {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
|
|
|
||||||
|
|
@ -23,3 +23,97 @@
|
||||||
.toastui-editor-main .toastui-editor-md-preview {
|
.toastui-editor-main .toastui-editor-md-preview {
|
||||||
height: 100% !important;
|
height: 100% !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ── Toast UI Editor — dark-theme overrides ───────────────────────────────
|
||||||
|
Toast UI ships with light-mode chrome and edit surfaces by default. In
|
||||||
|
mdedit's dark mode the editor's text (#222) falls onto the transparent
|
||||||
|
md-container, which inherits var(--bg) dark = #1e1e1e → effectively
|
||||||
|
black-on-black. Override the load-bearing surfaces with mdedit's tokens
|
||||||
|
so the editor harmonises with the rest of the chrome.
|
||||||
|
The selectors target both manual override (data-theme="dark") and the
|
||||||
|
OS-pref auto fallback (prefers-color-scheme + no data-theme="light"). */
|
||||||
|
|
||||||
|
/* Manual dark override */
|
||||||
|
[data-theme="dark"] .toastui-editor-defaultUI,
|
||||||
|
[data-theme="dark"] .toastui-editor-md-container,
|
||||||
|
[data-theme="dark"] .toastui-editor-md-preview,
|
||||||
|
[data-theme="dark"] .toastui-editor-ww-container,
|
||||||
|
[data-theme="dark"] .toastui-editor-mode-switch,
|
||||||
|
[data-theme="dark"] .toastui-editor-main,
|
||||||
|
[data-theme="dark"] .ProseMirror {
|
||||||
|
background-color: var(--bg);
|
||||||
|
color: var(--text);
|
||||||
|
}
|
||||||
|
[data-theme="dark"] .toastui-editor-defaultUI-toolbar {
|
||||||
|
background-color: var(--bg-secondary);
|
||||||
|
border-bottom-color: var(--border);
|
||||||
|
}
|
||||||
|
[data-theme="dark"] .toastui-editor-md-splitter {
|
||||||
|
background-color: var(--border);
|
||||||
|
}
|
||||||
|
[data-theme="dark"] .toastui-editor-toolbar-icons {
|
||||||
|
/* Toast UI's icons are sprite-baked dark; invert flips them to light. */
|
||||||
|
filter: invert(0.85) hue-rotate(180deg);
|
||||||
|
}
|
||||||
|
[data-theme="dark"] .toastui-editor-toolbar-divider {
|
||||||
|
background-color: var(--border);
|
||||||
|
}
|
||||||
|
[data-theme="dark"] .toastui-editor-mode-switch {
|
||||||
|
border-top-color: var(--border);
|
||||||
|
}
|
||||||
|
[data-theme="dark"] .toastui-editor-mode-switch .tab-item {
|
||||||
|
color: var(--text-muted);
|
||||||
|
}
|
||||||
|
[data-theme="dark"] .toastui-editor-mode-switch .tab-item.active {
|
||||||
|
color: var(--text);
|
||||||
|
background-color: var(--bg);
|
||||||
|
}
|
||||||
|
[data-theme="dark"] .toastui-editor-popup,
|
||||||
|
[data-theme="dark"] .toastui-editor-context-menu {
|
||||||
|
background-color: var(--bg-secondary);
|
||||||
|
color: var(--text);
|
||||||
|
border-color: var(--border);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* OS-pref auto fallback (matches every selector above) */
|
||||||
|
@media (prefers-color-scheme: dark) {
|
||||||
|
:root:not([data-theme="light"]) .toastui-editor-defaultUI,
|
||||||
|
:root:not([data-theme="light"]) .toastui-editor-md-container,
|
||||||
|
:root:not([data-theme="light"]) .toastui-editor-md-preview,
|
||||||
|
:root:not([data-theme="light"]) .toastui-editor-ww-container,
|
||||||
|
:root:not([data-theme="light"]) .toastui-editor-mode-switch,
|
||||||
|
:root:not([data-theme="light"]) .toastui-editor-main,
|
||||||
|
:root:not([data-theme="light"]) .ProseMirror {
|
||||||
|
background-color: var(--bg);
|
||||||
|
color: var(--text);
|
||||||
|
}
|
||||||
|
:root:not([data-theme="light"]) .toastui-editor-defaultUI-toolbar {
|
||||||
|
background-color: var(--bg-secondary);
|
||||||
|
border-bottom-color: var(--border);
|
||||||
|
}
|
||||||
|
:root:not([data-theme="light"]) .toastui-editor-md-splitter {
|
||||||
|
background-color: var(--border);
|
||||||
|
}
|
||||||
|
:root:not([data-theme="light"]) .toastui-editor-toolbar-icons {
|
||||||
|
filter: invert(0.85) hue-rotate(180deg);
|
||||||
|
}
|
||||||
|
:root:not([data-theme="light"]) .toastui-editor-toolbar-divider {
|
||||||
|
background-color: var(--border);
|
||||||
|
}
|
||||||
|
:root:not([data-theme="light"]) .toastui-editor-mode-switch {
|
||||||
|
border-top-color: var(--border);
|
||||||
|
}
|
||||||
|
:root:not([data-theme="light"]) .toastui-editor-mode-switch .tab-item {
|
||||||
|
color: var(--text-muted);
|
||||||
|
}
|
||||||
|
:root:not([data-theme="light"]) .toastui-editor-mode-switch .tab-item.active {
|
||||||
|
color: var(--text);
|
||||||
|
background-color: var(--bg);
|
||||||
|
}
|
||||||
|
:root:not([data-theme="light"]) .toastui-editor-popup,
|
||||||
|
:root:not([data-theme="light"]) .toastui-editor-context-menu {
|
||||||
|
background-color: var(--bg-secondary);
|
||||||
|
color: var(--text);
|
||||||
|
border-color: var(--border);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
6205
mdedit/dist/mdedit.html
vendored
Normal file
6205
mdedit/dist/mdedit.html
vendored
Normal file
File diff suppressed because one or more lines are too long
|
|
@ -101,7 +101,7 @@ function renderFileTree() {
|
||||||
|
|
||||||
const scratchLabel = document.createElement('span');
|
const scratchLabel = document.createElement('span');
|
||||||
scratchLabel.className = 'tree-row__label';
|
scratchLabel.className = 'tree-row__label';
|
||||||
scratchLabel.innerHTML = '<div class="filename-main">📝 Scratchpad</div><div class="filename-secondary">Quick notes — no directory needed</div>';
|
scratchLabel.innerHTML = '<span class="tree-row__name"><div class="filename-main">📝 Scratchpad</div><div class="filename-secondary">Quick notes — no directory needed</div></span>';
|
||||||
scratchpadElement.appendChild(scratchLabel);
|
scratchpadElement.appendChild(scratchLabel);
|
||||||
|
|
||||||
const scratchActions = document.createElement('div');
|
const scratchActions = document.createElement('div');
|
||||||
|
|
@ -169,7 +169,10 @@ function renderFileTree() {
|
||||||
const meta = `${parsedFolder.trackingNumber} (${parsedFolder.status}) — ${parsedFolder.date}`;
|
const meta = `${parsedFolder.trackingNumber} (${parsedFolder.status}) — ${parsedFolder.date}`;
|
||||||
dirName.innerHTML = `<div class="filename-main">📁 ${escapeHtml(parsedFolder.title)}</div><div class="filename-secondary">${escapeHtml(meta)}</div>`;
|
dirName.innerHTML = `<div class="filename-main">📁 ${escapeHtml(parsedFolder.title)}</div><div class="filename-secondary">${escapeHtml(meta)}</div>`;
|
||||||
} else {
|
} else {
|
||||||
dirName.textContent = `📁 ${name}`;
|
// Non-ZDDC folder: still wrap in filename-main so
|
||||||
|
// typography matches the two-line entries (same font
|
||||||
|
// size + weight; just no secondary line).
|
||||||
|
dirName.innerHTML = `<div class="filename-main">📁 ${escapeHtml(name)}</div>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
const dirLabel = document.createElement('span');
|
const dirLabel = document.createElement('span');
|
||||||
|
|
@ -209,24 +212,30 @@ function renderFileTree() {
|
||||||
|
|
||||||
const fileIcon = getFileTypeIcon(name);
|
const fileIcon = getFileTypeIcon(name);
|
||||||
|
|
||||||
let fileNameDisplay;
|
// Build the inner two-line text inside a tree-row__name
|
||||||
|
// wrapper (column-flex). ZDDC-conforming filenames split
|
||||||
|
// into title + meta; "Title - filename.ext" pattern uses
|
||||||
|
// the dash as the same split. Plain names get a single
|
||||||
|
// line via filename-main only — same wrapper, just no
|
||||||
|
// secondary div, so the layout stays consistent.
|
||||||
|
let fileNameInner;
|
||||||
const parsed = zddc.parseFilename(name);
|
const parsed = zddc.parseFilename(name);
|
||||||
if (parsed && parsed.valid) {
|
if (parsed && parsed.valid) {
|
||||||
const titleDisplay = escapeHtml(parsed.title);
|
const titleDisplay = escapeHtml(parsed.title);
|
||||||
const metaDisplay = escapeHtml(`${parsed.trackingNumber}_${parsed.revision} (${parsed.status})`);
|
const metaDisplay = escapeHtml(`${parsed.trackingNumber}_${parsed.revision} (${parsed.status})`);
|
||||||
fileNameDisplay = `<div class="filename-main">${fileIcon} ${titleDisplay}</div><div class="filename-secondary">${metaDisplay}</div>`;
|
fileNameInner = `<div class="filename-main">${fileIcon} ${titleDisplay}</div><div class="filename-secondary">${metaDisplay}</div>`;
|
||||||
} else if (name.includes(' - ')) {
|
} else if (name.includes(' - ')) {
|
||||||
const dashIdx = name.lastIndexOf(' - ');
|
const dashIdx = name.lastIndexOf(' - ');
|
||||||
const secondary = escapeHtml(name.substring(0, dashIdx));
|
const secondary = escapeHtml(name.substring(0, dashIdx));
|
||||||
const primary = escapeHtml(name.substring(dashIdx + 3).replace(/\.[^.]+$/, ''));
|
const primary = escapeHtml(name.substring(dashIdx + 3).replace(/\.[^.]+$/, ''));
|
||||||
fileNameDisplay = `<div class="filename-main">${fileIcon} ${primary}</div><div class="filename-secondary">${secondary}</div>`;
|
fileNameInner = `<div class="filename-main">${fileIcon} ${primary}</div><div class="filename-secondary">${secondary}</div>`;
|
||||||
} else {
|
} else {
|
||||||
fileNameDisplay = `<span>${fileIcon} ${escapeHtml(name)}</span>`;
|
fileNameInner = `<div class="filename-main">${fileIcon} ${escapeHtml(name)}</div>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
const fileLabel = document.createElement('span');
|
const fileLabel = document.createElement('span');
|
||||||
fileLabel.className = 'tree-row__label';
|
fileLabel.className = 'tree-row__label';
|
||||||
fileLabel.innerHTML = fileNameDisplay;
|
fileLabel.innerHTML = `<span class="tree-row__name">${fileNameInner}</span>`;
|
||||||
|
|
||||||
const fileActions = createActionButtons(filePath, 'file');
|
const fileActions = createActionButtons(filePath, 'file');
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2113,7 +2113,7 @@ td[data-field="trackingNumber"] {
|
||||||
</svg>
|
</svg>
|
||||||
<div class="header-title-group">
|
<div class="header-title-group">
|
||||||
<span class="app-header__title">ZDDC Archive</span>
|
<span class="app-header__title">ZDDC Archive</span>
|
||||||
<span class="build-timestamp">v0.0.8</span>
|
<span class="build-timestamp"><span style="color:red;font-weight:bold">v0.0.9-alpha · 2026-05-02 02:07:03 · 17b0a4d-dirty</span></span>
|
||||||
</div>
|
</div>
|
||||||
<button id="addDirectoryBtn" class="btn btn-primary">Add Local Directory</button>
|
<button id="addDirectoryBtn" class="btn btn-primary">Add Local Directory</button>
|
||||||
<button id="refreshHeaderBtn" class="btn btn-secondary hidden" title="Refresh Data" style="font-size:1.1rem;">⟳</button>
|
<button id="refreshHeaderBtn" class="btn btn-secondary hidden" title="Refresh Data" style="font-size:1.1rem;">⟳</button>
|
||||||
|
|
|
||||||
|
|
@ -1376,7 +1376,7 @@ body.help-open .app-header {
|
||||||
</svg>
|
</svg>
|
||||||
<div class="header-title-group">
|
<div class="header-title-group">
|
||||||
<span class="app-header__title">ZDDC Classifier</span>
|
<span class="app-header__title">ZDDC Classifier</span>
|
||||||
<span class="build-timestamp">v0.0.8</span>
|
<span class="build-timestamp"><span style="color:red;font-weight:bold">v0.0.9-alpha · 2026-05-02 02:07:03 · 17b0a4d-dirty</span></span>
|
||||||
</div>
|
</div>
|
||||||
<button id="selectDirectoryBtn" class="btn btn-primary">Select Directory</button>
|
<button id="selectDirectoryBtn" class="btn btn-primary">Select Directory</button>
|
||||||
<button id="refreshBtn" class="btn btn-secondary hidden" title="Refresh and rescan directory">Refresh</button>
|
<button id="refreshBtn" class="btn btn-secondary hidden" title="Refresh and rescan directory">Refresh</button>
|
||||||
|
|
|
||||||
|
|
@ -866,7 +866,7 @@ body {
|
||||||
</g>
|
</g>
|
||||||
</svg>
|
</svg>
|
||||||
<span class="app-header__title">ZDDC Archive</span>
|
<span class="app-header__title">ZDDC Archive</span>
|
||||||
<span class="build-timestamp">v0.0.8</span>
|
<span class="build-timestamp"><span style="color:red;font-weight:bold">v0.0.9-alpha · 2026-05-02 02:07:03 · 17b0a4d-dirty</span></span>
|
||||||
</div>
|
</div>
|
||||||
<div class="header-right">
|
<div class="header-right">
|
||||||
<button id="theme-btn" class="btn btn-secondary" title="Theme: auto (follows OS)" aria-label="Theme: auto (follows OS)">◐</button>
|
<button id="theme-btn" class="btn btn-secondary" title="Theme: auto (follows OS)" aria-label="Theme: auto (follows OS)">◐</button>
|
||||||
|
|
|
||||||
|
|
@ -1066,13 +1066,25 @@ body.help-open .app-header {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
white-space: nowrap;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 0.25rem;
|
gap: 0.25rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* The text wrapper inside a tree-row label. For ZDDC-conforming files and
|
||||||
|
folders, this wraps two stacked <div>s (filename-main + filename-secondary)
|
||||||
|
so the row reads top-to-bottom as title + metadata — same shape the archive
|
||||||
|
tool uses for its transmittal-folder list. For non-ZDDC entries it just
|
||||||
|
contains a single line. flex column makes the two-line case work; min-width:0
|
||||||
|
lets each line truncate independently. */
|
||||||
|
.tree-row__name {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
min-width: 0;
|
||||||
|
flex: 1;
|
||||||
|
line-height: 1.25;
|
||||||
|
}
|
||||||
|
|
||||||
/* ── New-file modal ─────────────────────────────────────────────────────────── */
|
/* ── New-file modal ─────────────────────────────────────────────────────────── */
|
||||||
.modal-overlay {
|
.modal-overlay {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
|
|
@ -1147,6 +1159,100 @@ body.help-open .app-header {
|
||||||
height: 100% !important;
|
height: 100% !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ── Toast UI Editor — dark-theme overrides ───────────────────────────────
|
||||||
|
Toast UI ships with light-mode chrome and edit surfaces by default. In
|
||||||
|
mdedit's dark mode the editor's text (#222) falls onto the transparent
|
||||||
|
md-container, which inherits var(--bg) dark = #1e1e1e → effectively
|
||||||
|
black-on-black. Override the load-bearing surfaces with mdedit's tokens
|
||||||
|
so the editor harmonises with the rest of the chrome.
|
||||||
|
The selectors target both manual override (data-theme="dark") and the
|
||||||
|
OS-pref auto fallback (prefers-color-scheme + no data-theme="light"). */
|
||||||
|
|
||||||
|
/* Manual dark override */
|
||||||
|
[data-theme="dark"] .toastui-editor-defaultUI,
|
||||||
|
[data-theme="dark"] .toastui-editor-md-container,
|
||||||
|
[data-theme="dark"] .toastui-editor-md-preview,
|
||||||
|
[data-theme="dark"] .toastui-editor-ww-container,
|
||||||
|
[data-theme="dark"] .toastui-editor-mode-switch,
|
||||||
|
[data-theme="dark"] .toastui-editor-main,
|
||||||
|
[data-theme="dark"] .ProseMirror {
|
||||||
|
background-color: var(--bg);
|
||||||
|
color: var(--text);
|
||||||
|
}
|
||||||
|
[data-theme="dark"] .toastui-editor-defaultUI-toolbar {
|
||||||
|
background-color: var(--bg-secondary);
|
||||||
|
border-bottom-color: var(--border);
|
||||||
|
}
|
||||||
|
[data-theme="dark"] .toastui-editor-md-splitter {
|
||||||
|
background-color: var(--border);
|
||||||
|
}
|
||||||
|
[data-theme="dark"] .toastui-editor-toolbar-icons {
|
||||||
|
/* Toast UI's icons are sprite-baked dark; invert flips them to light. */
|
||||||
|
filter: invert(0.85) hue-rotate(180deg);
|
||||||
|
}
|
||||||
|
[data-theme="dark"] .toastui-editor-toolbar-divider {
|
||||||
|
background-color: var(--border);
|
||||||
|
}
|
||||||
|
[data-theme="dark"] .toastui-editor-mode-switch {
|
||||||
|
border-top-color: var(--border);
|
||||||
|
}
|
||||||
|
[data-theme="dark"] .toastui-editor-mode-switch .tab-item {
|
||||||
|
color: var(--text-muted);
|
||||||
|
}
|
||||||
|
[data-theme="dark"] .toastui-editor-mode-switch .tab-item.active {
|
||||||
|
color: var(--text);
|
||||||
|
background-color: var(--bg);
|
||||||
|
}
|
||||||
|
[data-theme="dark"] .toastui-editor-popup,
|
||||||
|
[data-theme="dark"] .toastui-editor-context-menu {
|
||||||
|
background-color: var(--bg-secondary);
|
||||||
|
color: var(--text);
|
||||||
|
border-color: var(--border);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* OS-pref auto fallback (matches every selector above) */
|
||||||
|
@media (prefers-color-scheme: dark) {
|
||||||
|
:root:not([data-theme="light"]) .toastui-editor-defaultUI,
|
||||||
|
:root:not([data-theme="light"]) .toastui-editor-md-container,
|
||||||
|
:root:not([data-theme="light"]) .toastui-editor-md-preview,
|
||||||
|
:root:not([data-theme="light"]) .toastui-editor-ww-container,
|
||||||
|
:root:not([data-theme="light"]) .toastui-editor-mode-switch,
|
||||||
|
:root:not([data-theme="light"]) .toastui-editor-main,
|
||||||
|
:root:not([data-theme="light"]) .ProseMirror {
|
||||||
|
background-color: var(--bg);
|
||||||
|
color: var(--text);
|
||||||
|
}
|
||||||
|
:root:not([data-theme="light"]) .toastui-editor-defaultUI-toolbar {
|
||||||
|
background-color: var(--bg-secondary);
|
||||||
|
border-bottom-color: var(--border);
|
||||||
|
}
|
||||||
|
:root:not([data-theme="light"]) .toastui-editor-md-splitter {
|
||||||
|
background-color: var(--border);
|
||||||
|
}
|
||||||
|
:root:not([data-theme="light"]) .toastui-editor-toolbar-icons {
|
||||||
|
filter: invert(0.85) hue-rotate(180deg);
|
||||||
|
}
|
||||||
|
:root:not([data-theme="light"]) .toastui-editor-toolbar-divider {
|
||||||
|
background-color: var(--border);
|
||||||
|
}
|
||||||
|
:root:not([data-theme="light"]) .toastui-editor-mode-switch {
|
||||||
|
border-top-color: var(--border);
|
||||||
|
}
|
||||||
|
:root:not([data-theme="light"]) .toastui-editor-mode-switch .tab-item {
|
||||||
|
color: var(--text-muted);
|
||||||
|
}
|
||||||
|
:root:not([data-theme="light"]) .toastui-editor-mode-switch .tab-item.active {
|
||||||
|
color: var(--text);
|
||||||
|
background-color: var(--bg);
|
||||||
|
}
|
||||||
|
:root:not([data-theme="light"]) .toastui-editor-popup,
|
||||||
|
:root:not([data-theme="light"]) .toastui-editor-context-menu {
|
||||||
|
background-color: var(--bg-secondary);
|
||||||
|
color: var(--text);
|
||||||
|
border-color: var(--border);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* Table of Contents styles */
|
/* Table of Contents styles */
|
||||||
.toc-pane {
|
.toc-pane {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
|
@ -1668,7 +1774,7 @@ body.help-open .app-header {
|
||||||
</svg>
|
</svg>
|
||||||
<div class="header-title-group">
|
<div class="header-title-group">
|
||||||
<span class="app-header__title">ZDDC Markdown</span>
|
<span class="app-header__title">ZDDC Markdown</span>
|
||||||
<span class="build-timestamp">v0.0.8</span>
|
<span class="build-timestamp"><span style="color:red;font-weight:bold">v0.0.9-alpha · 2026-05-02 02:07:03 · 17b0a4d-dirty</span></span>
|
||||||
</div>
|
</div>
|
||||||
<button id="select-directory" class="btn btn-primary" title="Select a Directory">Select Directory</button>
|
<button id="select-directory" class="btn btn-primary" title="Select a Directory">Select Directory</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -4332,7 +4438,7 @@ function renderFileTree() {
|
||||||
|
|
||||||
const scratchLabel = document.createElement('span');
|
const scratchLabel = document.createElement('span');
|
||||||
scratchLabel.className = 'tree-row__label';
|
scratchLabel.className = 'tree-row__label';
|
||||||
scratchLabel.innerHTML = '<div class="filename-main">📝 Scratchpad</div><div class="filename-secondary">Quick notes — no directory needed</div>';
|
scratchLabel.innerHTML = '<span class="tree-row__name"><div class="filename-main">📝 Scratchpad</div><div class="filename-secondary">Quick notes — no directory needed</div></span>';
|
||||||
scratchpadElement.appendChild(scratchLabel);
|
scratchpadElement.appendChild(scratchLabel);
|
||||||
|
|
||||||
const scratchActions = document.createElement('div');
|
const scratchActions = document.createElement('div');
|
||||||
|
|
@ -4400,7 +4506,10 @@ function renderFileTree() {
|
||||||
const meta = `${parsedFolder.trackingNumber} (${parsedFolder.status}) — ${parsedFolder.date}`;
|
const meta = `${parsedFolder.trackingNumber} (${parsedFolder.status}) — ${parsedFolder.date}`;
|
||||||
dirName.innerHTML = `<div class="filename-main">📁 ${escapeHtml(parsedFolder.title)}</div><div class="filename-secondary">${escapeHtml(meta)}</div>`;
|
dirName.innerHTML = `<div class="filename-main">📁 ${escapeHtml(parsedFolder.title)}</div><div class="filename-secondary">${escapeHtml(meta)}</div>`;
|
||||||
} else {
|
} else {
|
||||||
dirName.textContent = `📁 ${name}`;
|
// Non-ZDDC folder: still wrap in filename-main so
|
||||||
|
// typography matches the two-line entries (same font
|
||||||
|
// size + weight; just no secondary line).
|
||||||
|
dirName.innerHTML = `<div class="filename-main">📁 ${escapeHtml(name)}</div>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
const dirLabel = document.createElement('span');
|
const dirLabel = document.createElement('span');
|
||||||
|
|
@ -4440,24 +4549,30 @@ function renderFileTree() {
|
||||||
|
|
||||||
const fileIcon = getFileTypeIcon(name);
|
const fileIcon = getFileTypeIcon(name);
|
||||||
|
|
||||||
let fileNameDisplay;
|
// Build the inner two-line text inside a tree-row__name
|
||||||
|
// wrapper (column-flex). ZDDC-conforming filenames split
|
||||||
|
// into title + meta; "Title - filename.ext" pattern uses
|
||||||
|
// the dash as the same split. Plain names get a single
|
||||||
|
// line via filename-main only — same wrapper, just no
|
||||||
|
// secondary div, so the layout stays consistent.
|
||||||
|
let fileNameInner;
|
||||||
const parsed = zddc.parseFilename(name);
|
const parsed = zddc.parseFilename(name);
|
||||||
if (parsed && parsed.valid) {
|
if (parsed && parsed.valid) {
|
||||||
const titleDisplay = escapeHtml(parsed.title);
|
const titleDisplay = escapeHtml(parsed.title);
|
||||||
const metaDisplay = escapeHtml(`${parsed.trackingNumber}_${parsed.revision} (${parsed.status})`);
|
const metaDisplay = escapeHtml(`${parsed.trackingNumber}_${parsed.revision} (${parsed.status})`);
|
||||||
fileNameDisplay = `<div class="filename-main">${fileIcon} ${titleDisplay}</div><div class="filename-secondary">${metaDisplay}</div>`;
|
fileNameInner = `<div class="filename-main">${fileIcon} ${titleDisplay}</div><div class="filename-secondary">${metaDisplay}</div>`;
|
||||||
} else if (name.includes(' - ')) {
|
} else if (name.includes(' - ')) {
|
||||||
const dashIdx = name.lastIndexOf(' - ');
|
const dashIdx = name.lastIndexOf(' - ');
|
||||||
const secondary = escapeHtml(name.substring(0, dashIdx));
|
const secondary = escapeHtml(name.substring(0, dashIdx));
|
||||||
const primary = escapeHtml(name.substring(dashIdx + 3).replace(/\.[^.]+$/, ''));
|
const primary = escapeHtml(name.substring(dashIdx + 3).replace(/\.[^.]+$/, ''));
|
||||||
fileNameDisplay = `<div class="filename-main">${fileIcon} ${primary}</div><div class="filename-secondary">${secondary}</div>`;
|
fileNameInner = `<div class="filename-main">${fileIcon} ${primary}</div><div class="filename-secondary">${secondary}</div>`;
|
||||||
} else {
|
} else {
|
||||||
fileNameDisplay = `<span>${fileIcon} ${escapeHtml(name)}</span>`;
|
fileNameInner = `<div class="filename-main">${fileIcon} ${escapeHtml(name)}</div>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
const fileLabel = document.createElement('span');
|
const fileLabel = document.createElement('span');
|
||||||
fileLabel.className = 'tree-row__label';
|
fileLabel.className = 'tree-row__label';
|
||||||
fileLabel.innerHTML = fileNameDisplay;
|
fileLabel.innerHTML = `<span class="tree-row__name">${fileNameInner}</span>`;
|
||||||
|
|
||||||
const fileActions = createActionButtons(filePath, 'file');
|
const fileActions = createActionButtons(filePath, 'file');
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2210,7 +2210,7 @@ dialog.modal--narrow {
|
||||||
</svg>
|
</svg>
|
||||||
<div class="header-title-group">
|
<div class="header-title-group">
|
||||||
<span class="app-header__title">ZDDC Transmittal</span>
|
<span class="app-header__title">ZDDC Transmittal</span>
|
||||||
<span class="build-timestamp">v0.0.8</span>
|
<span class="build-timestamp"><span style="color:red;font-weight:bold">v0.0.9-alpha · 2026-05-02 02:07:03 · 17b0a4d-dirty</span></span>
|
||||||
</div>
|
</div>
|
||||||
<div class="app-header__spacer"></div>
|
<div class="app-header__spacer"></div>
|
||||||
<div class="app-header__icons">
|
<div class="app-header__icons">
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
# Generated by build.sh — do not edit. One <app>=<build label> per line.
|
# Generated by build.sh — do not edit. One <app>=<build label> per line.
|
||||||
archive=v0.0.8
|
archive=v0.0.9-alpha · 2026-05-02 02:07:03 · 17b0a4d-dirty
|
||||||
transmittal=v0.0.8
|
transmittal=v0.0.9-alpha · 2026-05-02 02:07:03 · 17b0a4d-dirty
|
||||||
classifier=v0.0.8
|
classifier=v0.0.9-alpha · 2026-05-02 02:07:03 · 17b0a4d-dirty
|
||||||
mdedit=v0.0.8
|
mdedit=v0.0.9-alpha · 2026-05-02 02:07:03 · 17b0a4d-dirty
|
||||||
landing=v0.0.8
|
landing=v0.0.9-alpha · 2026-05-02 02:07:03 · 17b0a4d-dirty
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue