2026-04-04 15:51:53 +00:00
|
|
|
/* Duckmage plugin styles */
|
|
|
|
|
|
|
|
|
|
/* ── Hex map view ─────────────────────────────────────────────────────────── */
|
|
|
|
|
|
|
|
|
|
.duckmage-hex-map-container {
|
|
|
|
|
position: relative;
|
|
|
|
|
width: 100%;
|
|
|
|
|
height: 100%;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Clips the panning/zooming viewport without affecting control buttons */
|
|
|
|
|
.duckmage-hex-map-clip {
|
|
|
|
|
position: absolute;
|
|
|
|
|
inset: 0;
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Transparent overlay for expand & toolbar buttons — no overflow clipping */
|
|
|
|
|
.duckmage-hex-map-controls {
|
|
|
|
|
position: absolute;
|
|
|
|
|
inset: 0;
|
|
|
|
|
pointer-events: none;
|
|
|
|
|
z-index: 20;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-hex-map-controls > * {
|
|
|
|
|
pointer-events: auto;
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-11 16:07:34 +00:00
|
|
|
/* ── Side panel toggle buttons ────────────────────────────────────────────── */
|
2026-04-04 15:51:53 +00:00
|
|
|
|
2026-04-11 16:07:34 +00:00
|
|
|
.duckmage-panel-toggle-btn {
|
2026-04-04 15:51:53 +00:00
|
|
|
position: absolute;
|
|
|
|
|
top: 8px;
|
|
|
|
|
width: 28px;
|
|
|
|
|
height: 28px;
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
justify-content: center;
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
border-radius: 4px;
|
|
|
|
|
border: 1px solid var(--background-modifier-border);
|
|
|
|
|
background: var(--background-secondary);
|
|
|
|
|
color: var(--text-muted);
|
|
|
|
|
opacity: 0.7;
|
|
|
|
|
pointer-events: auto;
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-11 16:07:34 +00:00
|
|
|
.duckmage-panel-toggle-btn svg {
|
|
|
|
|
width: 16px;
|
|
|
|
|
height: 16px;
|
|
|
|
|
flex-shrink: 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-panel-toggle-btn:hover {
|
2026-04-04 15:51:53 +00:00
|
|
|
opacity: 1;
|
|
|
|
|
color: var(--text-normal);
|
|
|
|
|
border-color: var(--interactive-accent);
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-11 16:07:34 +00:00
|
|
|
.duckmage-panel-toggle-btn.is-active {
|
|
|
|
|
opacity: 1;
|
|
|
|
|
border-color: var(--interactive-accent);
|
|
|
|
|
color: var(--interactive-accent);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* ── Side panel container ─────────────────────────────────────────────────── */
|
|
|
|
|
|
|
|
|
|
.duckmage-side-panel {
|
|
|
|
|
position: absolute;
|
|
|
|
|
top: 44px;
|
|
|
|
|
background: var(--background-secondary);
|
|
|
|
|
border: 1px solid var(--background-modifier-border);
|
|
|
|
|
border-radius: 6px;
|
|
|
|
|
padding: 8px;
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
gap: 6px;
|
|
|
|
|
z-index: 10;
|
|
|
|
|
min-width: 160px;
|
|
|
|
|
pointer-events: auto;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* ── Overlay panel rows ───────────────────────────────────────────────────── */
|
|
|
|
|
|
|
|
|
|
.duckmage-overlay-row {
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
gap: 6px;
|
|
|
|
|
font-size: 0.9em;
|
|
|
|
|
white-space: nowrap;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-overlay-row input[type="checkbox"] {
|
|
|
|
|
flex-shrink: 0;
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-overlay-label {
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
color: var(--text-normal);
|
|
|
|
|
user-select: none;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* ── Overlay hide rules ───────────────────────────────────────────────────── */
|
|
|
|
|
|
|
|
|
|
/* Terrain fallback icon — hidden by default; shown by CSS when override icons are off */
|
|
|
|
|
.duckmage-hex-terrain-icon { display: none; }
|
|
|
|
|
|
|
|
|
|
/* Coordinates */
|
|
|
|
|
.duckmage-hide-coords .duckmage-hex-label { visibility: hidden; }
|
|
|
|
|
.duckmage-hide-coords .duckmage-svg-coord-label { display: none; }
|
|
|
|
|
|
|
|
|
|
/* Terrain icons off: hide plain terrain icons and suppress the fallback */
|
|
|
|
|
.duckmage-hide-terrain-icons .duckmage-hex:not([data-icon-override]) .duckmage-hex-icon { display: none; }
|
2026-05-15 19:06:16 +00:00
|
|
|
.duckmage-hide-terrain-icons .duckmage-hex-terrain-icon { display: none; }
|
2026-04-11 16:07:34 +00:00
|
|
|
|
2026-05-15 19:06:16 +00:00
|
|
|
/* Icon overrides off: hide override icon, reveal terrain fallback (only when terrain icons are not also hidden) */
|
2026-04-11 16:07:34 +00:00
|
|
|
.duckmage-hide-icon-overrides .duckmage-hex-override-icon { display: none; }
|
2026-05-15 19:06:16 +00:00
|
|
|
.duckmage-hide-icon-overrides:not(.duckmage-hide-terrain-icons) .duckmage-hex[data-icon-override] .duckmage-hex-terrain-icon { display: block; }
|
2026-04-11 16:07:34 +00:00
|
|
|
.duckmage-hide-icon-overrides .duckmage-svg-icon-override { display: none; }
|
|
|
|
|
|
2026-04-04 15:51:53 +00:00
|
|
|
.duckmage-hex-map-viewport {
|
|
|
|
|
position: absolute;
|
|
|
|
|
top: 0;
|
|
|
|
|
left: 0;
|
|
|
|
|
transform-origin: 0 0;
|
|
|
|
|
will-change: transform;
|
|
|
|
|
cursor: grab;
|
|
|
|
|
padding: 1em;
|
2026-06-17 22:48:13 +00:00
|
|
|
/* Viewport pan/zoom values are written to these CSS custom properties
|
|
|
|
|
from JS via setCssProps. See `.duckmage-bg-image-layer` for the
|
|
|
|
|
motivation. */
|
|
|
|
|
transform:
|
|
|
|
|
translate(var(--duckmage-vp-tx, 0px), var(--duckmage-vp-ty, 0px))
|
|
|
|
|
scale(var(--duckmage-vp-scale, 1));
|
2026-04-04 15:51:53 +00:00
|
|
|
}
|
|
|
|
|
|
feat: background image + grid calibration mode (1.4.0)
Per-map background image with full interactive calibration — pick or
drag-drop an image from the OS, drop straight into calibration mode,
align the hex grid to features in the image.
Calibration UX:
- Drag image body or grid body to move; corner/edge handles to resize
(image is aspect-locked; grid has top/bottom edges for hex-aspect
stretch). Anchor math: the side you grab moves, the opposite stays
fixed — see topics/design/notes/resize-handle-anchor-rule in the KB
for the derivation.
- Click an element to "focus" it; arrow keys nudge by 1 px (Shift = 10).
Sequential nudges coalesce into one undo entry.
- Floating ✓ button bottom-right commits, Esc cancels. Wheel falls
through to viewport zoom (precision alignment).
- Hex outlines drawn as an SVG overlay during calibration (CSS `border`
and `drop-shadow` filter approaches both fail on clip-path-shaped
hexes — the SVG overlay was chosen after sandbox iteration; see
dev/hex-calibration-sandbox.html).
- Resize handles use `transform: scale(1/zoom)` so they stay constant
screen size regardless of viewport zoom.
- Full undo/redo for every calibration mutation (drag, resize, nudge);
Ctrl/Cmd+Z and Ctrl/Cmd+Shift+Z hotkeys wired at view scope.
Persistence + alignment:
- New MapData.savedViewport persists per-map zoom/pan/font-size across
view close, so calibration values (sized to a specific font-size)
don't drift when the view reopens.
- bakeZoom now scales calibration values by the bake factor so
calibrated images stay aligned with the hex grid after wheel zoom.
Suppressed entirely during calibration (and pending bake timers
cancelled on entry) so the SVG outline doesn't drift mid-session.
- Path / faction / region overlay SVGs moved inside the gridContainer
so they inherit the gridDisplay transform — previously they sat as
siblings of the grid and drew at un-scaled coordinates, producing
the "two grids" effect on calibrated maps.
Other UX:
- Top/left grid expand buttons now visually anchor — the existing grid
stays put and the new row/column appears above/left, instead of the
whole grid shifting down/right.
- New "Background image" section in both the MapModal Properties tab
and the New map tab (with drop zone).
- Drag-and-drop OS images import to {hexFolder}/{mapName}/_bg/, with
filename collision auto-rename.
- "No terrain" hexes become transparent when a bg image is set, so the
image shows through.
- Calibration banner anchored to the view's contentEl (was body) so it
can't survive across view-close; defensive sweep on open clears any
banner left by a prior buggy build.
Dev infrastructure:
- dev/hex-calibration-sandbox.html — minimal hex grid that mirrors the
plugin's CSS, used to iterate on calibration outline / overlay
alignment approaches outside the live editor.
- dev/screenshot-approaches.mjs + snapshot-calibration-outline.mjs —
runners against the shared frontend-testing tool that produce one
PNG per CSS variant for LLM-direct visual review (see the
frontend-testing-tools KB project).
2026-06-17 21:01:24 +00:00
|
|
|
.duckmage-bg-image-layer {
|
|
|
|
|
position: absolute;
|
|
|
|
|
top: 1em;
|
|
|
|
|
left: 1em;
|
|
|
|
|
transform-origin: 0 0;
|
|
|
|
|
pointer-events: none;
|
|
|
|
|
z-index: 0;
|
2026-06-17 22:48:13 +00:00
|
|
|
/* Dynamic transform values are written to these CSS custom properties
|
|
|
|
|
from JS via setCssProps. Doing it this way keeps the `transform`
|
|
|
|
|
declaration itself in CSS (satisfies obsidianmd's
|
|
|
|
|
no-static-styles-assignment rule) while still letting the values
|
|
|
|
|
come from JS state. */
|
|
|
|
|
transform:
|
|
|
|
|
translate(var(--duckmage-bg-tx, 0px), var(--duckmage-bg-ty, 0px))
|
|
|
|
|
rotate(var(--duckmage-bg-rot, 0deg))
|
|
|
|
|
scale(var(--duckmage-bg-scale, 1));
|
|
|
|
|
opacity: var(--duckmage-bg-opacity, 1);
|
feat: background image + grid calibration mode (1.4.0)
Per-map background image with full interactive calibration — pick or
drag-drop an image from the OS, drop straight into calibration mode,
align the hex grid to features in the image.
Calibration UX:
- Drag image body or grid body to move; corner/edge handles to resize
(image is aspect-locked; grid has top/bottom edges for hex-aspect
stretch). Anchor math: the side you grab moves, the opposite stays
fixed — see topics/design/notes/resize-handle-anchor-rule in the KB
for the derivation.
- Click an element to "focus" it; arrow keys nudge by 1 px (Shift = 10).
Sequential nudges coalesce into one undo entry.
- Floating ✓ button bottom-right commits, Esc cancels. Wheel falls
through to viewport zoom (precision alignment).
- Hex outlines drawn as an SVG overlay during calibration (CSS `border`
and `drop-shadow` filter approaches both fail on clip-path-shaped
hexes — the SVG overlay was chosen after sandbox iteration; see
dev/hex-calibration-sandbox.html).
- Resize handles use `transform: scale(1/zoom)` so they stay constant
screen size regardless of viewport zoom.
- Full undo/redo for every calibration mutation (drag, resize, nudge);
Ctrl/Cmd+Z and Ctrl/Cmd+Shift+Z hotkeys wired at view scope.
Persistence + alignment:
- New MapData.savedViewport persists per-map zoom/pan/font-size across
view close, so calibration values (sized to a specific font-size)
don't drift when the view reopens.
- bakeZoom now scales calibration values by the bake factor so
calibrated images stay aligned with the hex grid after wheel zoom.
Suppressed entirely during calibration (and pending bake timers
cancelled on entry) so the SVG outline doesn't drift mid-session.
- Path / faction / region overlay SVGs moved inside the gridContainer
so they inherit the gridDisplay transform — previously they sat as
siblings of the grid and drew at un-scaled coordinates, producing
the "two grids" effect on calibrated maps.
Other UX:
- Top/left grid expand buttons now visually anchor — the existing grid
stays put and the new row/column appears above/left, instead of the
whole grid shifting down/right.
- New "Background image" section in both the MapModal Properties tab
and the New map tab (with drop zone).
- Drag-and-drop OS images import to {hexFolder}/{mapName}/_bg/, with
filename collision auto-rename.
- "No terrain" hexes become transparent when a bg image is set, so the
image shows through.
- Calibration banner anchored to the view's contentEl (was body) so it
can't survive across view-close; defensive sweep on open clears any
banner left by a prior buggy build.
Dev infrastructure:
- dev/hex-calibration-sandbox.html — minimal hex grid that mirrors the
plugin's CSS, used to iterate on calibration outline / overlay
alignment approaches outside the live editor.
- dev/screenshot-approaches.mjs + snapshot-calibration-outline.mjs —
runners against the shared frontend-testing tool that produce one
PNG per CSS variant for LLM-direct visual review (see the
frontend-testing-tools KB project).
2026-06-17 21:01:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-bg-image {
|
|
|
|
|
display: block;
|
|
|
|
|
user-select: none;
|
|
|
|
|
max-width: none;
|
|
|
|
|
max-height: none;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-hex-map-viewport.is-bg-calibrating .duckmage-bg-image-layer {
|
|
|
|
|
pointer-events: auto;
|
|
|
|
|
cursor: move;
|
|
|
|
|
outline: 2px dashed var(--interactive-accent);
|
|
|
|
|
outline-offset: 4px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Focused calibration target: the last-clicked image/grid gets a solid,
|
|
|
|
|
brighter outline so the user knows arrow-key nudges will go there. */
|
|
|
|
|
.duckmage-hex-map-viewport.is-bg-calibrating .duckmage-bg-image-layer.is-focused {
|
|
|
|
|
outline: 3px solid var(--interactive-accent);
|
|
|
|
|
}
|
|
|
|
|
.duckmage-hex-map-viewport.is-bg-calibrating .duckmage-hex-map-grid.is-focused {
|
|
|
|
|
outline: 3px solid var(--color-accent);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Calibration mode: hexes are pointer-events: none so the grid container
|
|
|
|
|
captures the drag/wheel events. Hex outline is rendered via the SVG
|
|
|
|
|
overlay in HexMapView; see renderCalibrationOutlines. */
|
|
|
|
|
.duckmage-hex-map-viewport.is-bg-calibrating .duckmage-hex {
|
|
|
|
|
pointer-events: none;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-hex-map-viewport.is-bg-calibrating .duckmage-hex-map-grid {
|
|
|
|
|
cursor: move;
|
|
|
|
|
outline: 2px dashed var(--color-accent);
|
|
|
|
|
outline-offset: 4px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Grid container is position: relative always so overlay SVGs (paths,
|
|
|
|
|
faction, region, calibration outline) can be anchored as descendants and
|
|
|
|
|
inherit any gridDisplay transform — otherwise they drift when calibrated
|
|
|
|
|
maps re-scale the grid. */
|
|
|
|
|
.duckmage-hex-map-grid {
|
|
|
|
|
position: relative;
|
2026-06-17 22:48:13 +00:00
|
|
|
/* gridDisplay transform: values are written to these CSS custom
|
|
|
|
|
properties from JS. Defaults are identity, so an un-calibrated map
|
|
|
|
|
renders unchanged. See `.duckmage-bg-image-layer` for the same
|
|
|
|
|
pattern and why we do it this way. */
|
|
|
|
|
transform-origin: 0 0;
|
|
|
|
|
transform:
|
|
|
|
|
translate(var(--duckmage-grid-tx, 0px), var(--duckmage-grid-ty, 0px))
|
|
|
|
|
scale(var(--duckmage-grid-sx, 1), var(--duckmage-grid-sy, 1));
|
feat: background image + grid calibration mode (1.4.0)
Per-map background image with full interactive calibration — pick or
drag-drop an image from the OS, drop straight into calibration mode,
align the hex grid to features in the image.
Calibration UX:
- Drag image body or grid body to move; corner/edge handles to resize
(image is aspect-locked; grid has top/bottom edges for hex-aspect
stretch). Anchor math: the side you grab moves, the opposite stays
fixed — see topics/design/notes/resize-handle-anchor-rule in the KB
for the derivation.
- Click an element to "focus" it; arrow keys nudge by 1 px (Shift = 10).
Sequential nudges coalesce into one undo entry.
- Floating ✓ button bottom-right commits, Esc cancels. Wheel falls
through to viewport zoom (precision alignment).
- Hex outlines drawn as an SVG overlay during calibration (CSS `border`
and `drop-shadow` filter approaches both fail on clip-path-shaped
hexes — the SVG overlay was chosen after sandbox iteration; see
dev/hex-calibration-sandbox.html).
- Resize handles use `transform: scale(1/zoom)` so they stay constant
screen size regardless of viewport zoom.
- Full undo/redo for every calibration mutation (drag, resize, nudge);
Ctrl/Cmd+Z and Ctrl/Cmd+Shift+Z hotkeys wired at view scope.
Persistence + alignment:
- New MapData.savedViewport persists per-map zoom/pan/font-size across
view close, so calibration values (sized to a specific font-size)
don't drift when the view reopens.
- bakeZoom now scales calibration values by the bake factor so
calibrated images stay aligned with the hex grid after wheel zoom.
Suppressed entirely during calibration (and pending bake timers
cancelled on entry) so the SVG outline doesn't drift mid-session.
- Path / faction / region overlay SVGs moved inside the gridContainer
so they inherit the gridDisplay transform — previously they sat as
siblings of the grid and drew at un-scaled coordinates, producing
the "two grids" effect on calibrated maps.
Other UX:
- Top/left grid expand buttons now visually anchor — the existing grid
stays put and the new row/column appears above/left, instead of the
whole grid shifting down/right.
- New "Background image" section in both the MapModal Properties tab
and the New map tab (with drop zone).
- Drag-and-drop OS images import to {hexFolder}/{mapName}/_bg/, with
filename collision auto-rename.
- "No terrain" hexes become transparent when a bg image is set, so the
image shows through.
- Calibration banner anchored to the view's contentEl (was body) so it
can't survive across view-close; defensive sweep on open clears any
banner left by a prior buggy build.
Dev infrastructure:
- dev/hex-calibration-sandbox.html — minimal hex grid that mirrors the
plugin's CSS, used to iterate on calibration outline / overlay
alignment approaches outside the live editor.
- dev/screenshot-approaches.mjs + snapshot-calibration-outline.mjs —
runners against the shared frontend-testing tool that produce one
PNG per CSS variant for LLM-direct visual review (see the
frontend-testing-tools KB project).
2026-06-17 21:01:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-calibration-outlines-svg {
|
|
|
|
|
position: absolute;
|
|
|
|
|
top: 0;
|
|
|
|
|
left: 0;
|
|
|
|
|
pointer-events: none;
|
|
|
|
|
overflow: visible;
|
|
|
|
|
z-index: 10;
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-30 11:04:12 +00:00
|
|
|
/* During an active calibration drag/resize, replace the 3843 individually-
|
|
|
|
|
painted hex cells with ONE pre-drawn <canvas> "colour snapshot" (built in JS
|
|
|
|
|
at drag start — see buildCalibrationSnapshot). Moving/scaling 3843 clip-path
|
|
|
|
|
cells re-rasterises them every frame (a translate alone measured ~373ms);
|
|
|
|
|
moving one canvas is a single composite op. The cells are kept in the layout
|
|
|
|
|
with `visibility: hidden` (NOT `display: none`) so the grid keeps its size —
|
|
|
|
|
the resize handles are positioned against the grid box and would jump to the
|
|
|
|
|
origin if it collapsed. Icons/labels/coord-labels and the SVG outline are
|
|
|
|
|
redundant with the snapshot and dropped entirely. All restored on release. */
|
|
|
|
|
.duckmage-hex-map-viewport.duckmage-calibrating-drag .duckmage-hex {
|
|
|
|
|
visibility: hidden;
|
|
|
|
|
}
|
|
|
|
|
.duckmage-hex-map-viewport.duckmage-calibrating-drag .duckmage-coord-labels-layer,
|
|
|
|
|
.duckmage-hex-map-viewport.duckmage-calibrating-drag .duckmage-calibration-outlines-svg {
|
|
|
|
|
display: none;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-calibration-snapshot {
|
|
|
|
|
position: absolute;
|
|
|
|
|
top: 0;
|
|
|
|
|
left: 0;
|
|
|
|
|
pointer-events: none;
|
|
|
|
|
z-index: 1;
|
|
|
|
|
}
|
|
|
|
|
|
feat: background image + grid calibration mode (1.4.0)
Per-map background image with full interactive calibration — pick or
drag-drop an image from the OS, drop straight into calibration mode,
align the hex grid to features in the image.
Calibration UX:
- Drag image body or grid body to move; corner/edge handles to resize
(image is aspect-locked; grid has top/bottom edges for hex-aspect
stretch). Anchor math: the side you grab moves, the opposite stays
fixed — see topics/design/notes/resize-handle-anchor-rule in the KB
for the derivation.
- Click an element to "focus" it; arrow keys nudge by 1 px (Shift = 10).
Sequential nudges coalesce into one undo entry.
- Floating ✓ button bottom-right commits, Esc cancels. Wheel falls
through to viewport zoom (precision alignment).
- Hex outlines drawn as an SVG overlay during calibration (CSS `border`
and `drop-shadow` filter approaches both fail on clip-path-shaped
hexes — the SVG overlay was chosen after sandbox iteration; see
dev/hex-calibration-sandbox.html).
- Resize handles use `transform: scale(1/zoom)` so they stay constant
screen size regardless of viewport zoom.
- Full undo/redo for every calibration mutation (drag, resize, nudge);
Ctrl/Cmd+Z and Ctrl/Cmd+Shift+Z hotkeys wired at view scope.
Persistence + alignment:
- New MapData.savedViewport persists per-map zoom/pan/font-size across
view close, so calibration values (sized to a specific font-size)
don't drift when the view reopens.
- bakeZoom now scales calibration values by the bake factor so
calibrated images stay aligned with the hex grid after wheel zoom.
Suppressed entirely during calibration (and pending bake timers
cancelled on entry) so the SVG outline doesn't drift mid-session.
- Path / faction / region overlay SVGs moved inside the gridContainer
so they inherit the gridDisplay transform — previously they sat as
siblings of the grid and drew at un-scaled coordinates, producing
the "two grids" effect on calibrated maps.
Other UX:
- Top/left grid expand buttons now visually anchor — the existing grid
stays put and the new row/column appears above/left, instead of the
whole grid shifting down/right.
- New "Background image" section in both the MapModal Properties tab
and the New map tab (with drop zone).
- Drag-and-drop OS images import to {hexFolder}/{mapName}/_bg/, with
filename collision auto-rename.
- "No terrain" hexes become transparent when a bg image is set, so the
image shows through.
- Calibration banner anchored to the view's contentEl (was body) so it
can't survive across view-close; defensive sweep on open clears any
banner left by a prior buggy build.
Dev infrastructure:
- dev/hex-calibration-sandbox.html — minimal hex grid that mirrors the
plugin's CSS, used to iterate on calibration outline / overlay
alignment approaches outside the live editor.
- dev/screenshot-approaches.mjs + snapshot-calibration-outline.mjs —
runners against the shared frontend-testing tool that produce one
PNG per CSS variant for LLM-direct visual review (see the
frontend-testing-tools KB project).
2026-06-17 21:01:24 +00:00
|
|
|
/* Floating commit chip during calibration — single ✓ button */
|
|
|
|
|
.duckmage-calibration-help {
|
|
|
|
|
position: fixed;
|
|
|
|
|
bottom: 24px;
|
|
|
|
|
right: 24px;
|
|
|
|
|
z-index: 1000;
|
|
|
|
|
pointer-events: none;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-calibration-commit {
|
|
|
|
|
pointer-events: auto;
|
|
|
|
|
width: 44px;
|
|
|
|
|
height: 44px;
|
|
|
|
|
border-radius: 50%;
|
|
|
|
|
font-size: 1.4em;
|
|
|
|
|
line-height: 1;
|
|
|
|
|
padding: 0;
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
justify-content: center;
|
|
|
|
|
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.5);
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Resize handles for bg image + hex grid in calibration mode. Positioned at
|
|
|
|
|
each corner/edge of the parent (which is position: relative or absolute).
|
|
|
|
|
`--duckmage-cal-scale` is set by HexMapView.applyTransform as `1/zoom`, so
|
|
|
|
|
handles stay constant *screen* size regardless of viewport zoom — without
|
|
|
|
|
compensation the viewport's `transform: scale()` shrinks them at low zoom
|
|
|
|
|
to the point you can't grab them. transform-origin: center keeps the scale
|
|
|
|
|
centered around the offset anchor point. */
|
|
|
|
|
.duckmage-calibration-handle {
|
|
|
|
|
position: absolute;
|
|
|
|
|
width: 18px;
|
|
|
|
|
height: 18px;
|
|
|
|
|
background: #22d3ee;
|
|
|
|
|
border: 1px solid #0c111a;
|
|
|
|
|
border-radius: 3px;
|
|
|
|
|
pointer-events: auto;
|
|
|
|
|
z-index: 20;
|
|
|
|
|
transform-origin: center;
|
|
|
|
|
}
|
|
|
|
|
.duckmage-calibration-handle-nw { top: -9px; left: -9px; cursor: nwse-resize; transform: scale(var(--duckmage-cal-scale, 1)); }
|
|
|
|
|
.duckmage-calibration-handle-n { top: -9px; left: 50%; cursor: ns-resize; transform: translateX(-50%) scale(var(--duckmage-cal-scale, 1)); }
|
|
|
|
|
.duckmage-calibration-handle-ne { top: -9px; right: -9px; cursor: nesw-resize; transform: scale(var(--duckmage-cal-scale, 1)); }
|
|
|
|
|
.duckmage-calibration-handle-w { top: 50%; left: -9px; cursor: ew-resize; transform: translateY(-50%) scale(var(--duckmage-cal-scale, 1)); }
|
|
|
|
|
.duckmage-calibration-handle-e { top: 50%; right: -9px; cursor: ew-resize; transform: translateY(-50%) scale(var(--duckmage-cal-scale, 1)); }
|
|
|
|
|
.duckmage-calibration-handle-sw { bottom: -9px; left: -9px; cursor: nesw-resize; transform: scale(var(--duckmage-cal-scale, 1)); }
|
|
|
|
|
.duckmage-calibration-handle-s { bottom: -9px; left: 50%; cursor: ns-resize; transform: translateX(-50%) scale(var(--duckmage-cal-scale, 1)); }
|
|
|
|
|
.duckmage-calibration-handle-se { bottom: -9px; right: -9px; cursor: nwse-resize; transform: scale(var(--duckmage-cal-scale, 1)); }
|
|
|
|
|
|
|
|
|
|
/* When a background image is present, hexes without a terrain become visually
|
2026-06-30 11:01:23 +00:00
|
|
|
transparent so the underlying image shows through. Hexes with a terrain are
|
|
|
|
|
tagged `.duckmage-hex-painted` (see setHexColor) and render normally. A class
|
|
|
|
|
selector is used here rather than a `:not([style*="background-color"])`
|
|
|
|
|
attribute-substring match because the engine matches it far more cheaply
|
|
|
|
|
across the thousands of hexes on a large map. */
|
|
|
|
|
.duckmage-hex-map-viewport.has-bg-image .duckmage-hex:not(.duckmage-hex-painted) {
|
feat: background image + grid calibration mode (1.4.0)
Per-map background image with full interactive calibration — pick or
drag-drop an image from the OS, drop straight into calibration mode,
align the hex grid to features in the image.
Calibration UX:
- Drag image body or grid body to move; corner/edge handles to resize
(image is aspect-locked; grid has top/bottom edges for hex-aspect
stretch). Anchor math: the side you grab moves, the opposite stays
fixed — see topics/design/notes/resize-handle-anchor-rule in the KB
for the derivation.
- Click an element to "focus" it; arrow keys nudge by 1 px (Shift = 10).
Sequential nudges coalesce into one undo entry.
- Floating ✓ button bottom-right commits, Esc cancels. Wheel falls
through to viewport zoom (precision alignment).
- Hex outlines drawn as an SVG overlay during calibration (CSS `border`
and `drop-shadow` filter approaches both fail on clip-path-shaped
hexes — the SVG overlay was chosen after sandbox iteration; see
dev/hex-calibration-sandbox.html).
- Resize handles use `transform: scale(1/zoom)` so they stay constant
screen size regardless of viewport zoom.
- Full undo/redo for every calibration mutation (drag, resize, nudge);
Ctrl/Cmd+Z and Ctrl/Cmd+Shift+Z hotkeys wired at view scope.
Persistence + alignment:
- New MapData.savedViewport persists per-map zoom/pan/font-size across
view close, so calibration values (sized to a specific font-size)
don't drift when the view reopens.
- bakeZoom now scales calibration values by the bake factor so
calibrated images stay aligned with the hex grid after wheel zoom.
Suppressed entirely during calibration (and pending bake timers
cancelled on entry) so the SVG outline doesn't drift mid-session.
- Path / faction / region overlay SVGs moved inside the gridContainer
so they inherit the gridDisplay transform — previously they sat as
siblings of the grid and drew at un-scaled coordinates, producing
the "two grids" effect on calibrated maps.
Other UX:
- Top/left grid expand buttons now visually anchor — the existing grid
stays put and the new row/column appears above/left, instead of the
whole grid shifting down/right.
- New "Background image" section in both the MapModal Properties tab
and the New map tab (with drop zone).
- Drag-and-drop OS images import to {hexFolder}/{mapName}/_bg/, with
filename collision auto-rename.
- "No terrain" hexes become transparent when a bg image is set, so the
image shows through.
- Calibration banner anchored to the view's contentEl (was body) so it
can't survive across view-close; defensive sweep on open clears any
banner left by a prior buggy build.
Dev infrastructure:
- dev/hex-calibration-sandbox.html — minimal hex grid that mirrors the
plugin's CSS, used to iterate on calibration outline / overlay
alignment approaches outside the live editor.
- dev/screenshot-approaches.mjs + snapshot-calibration-outline.mjs —
runners against the shared frontend-testing tool that produce one
PNG per CSS variant for LLM-direct visual review (see the
frontend-testing-tools KB project).
2026-06-17 21:01:24 +00:00
|
|
|
background-color: transparent;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-bg-image-path {
|
|
|
|
|
flex: 1;
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
text-overflow: ellipsis;
|
|
|
|
|
white-space: nowrap;
|
|
|
|
|
color: var(--text-muted);
|
|
|
|
|
font-size: 0.85em;
|
|
|
|
|
padding-right: 8px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-bg-image-row {
|
|
|
|
|
border: 1px dashed transparent;
|
|
|
|
|
border-radius: 6px;
|
|
|
|
|
padding: 4px 6px;
|
|
|
|
|
transition: border-color 0.1s, background 0.1s;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-bg-image-row.is-drop-target {
|
|
|
|
|
border-color: var(--interactive-accent);
|
|
|
|
|
background: var(--background-modifier-hover);
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-04 15:51:53 +00:00
|
|
|
.duckmage-hex-map-viewport.is-dragging {
|
|
|
|
|
cursor: grabbing;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* SVG overlay for paths, coordinate labels and elevated icons */
|
|
|
|
|
.duckmage-path-svg {
|
|
|
|
|
position: absolute;
|
|
|
|
|
top: 0;
|
|
|
|
|
left: 0;
|
|
|
|
|
pointer-events: none;
|
|
|
|
|
z-index: 5;
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-10 00:49:23 +00:00
|
|
|
/* SVG overlay for faction color fills — sits below paths */
|
|
|
|
|
.duckmage-faction-svg {
|
|
|
|
|
position: absolute;
|
|
|
|
|
top: 0;
|
|
|
|
|
left: 0;
|
|
|
|
|
pointer-events: none;
|
|
|
|
|
z-index: 4;
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-04 15:51:53 +00:00
|
|
|
/* ── Pointy-top row layout ────────────────────────────────────────────────── */
|
|
|
|
|
|
|
|
|
|
.duckmage-hex-row {
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-wrap: nowrap;
|
|
|
|
|
justify-content: flex-start;
|
|
|
|
|
margin-bottom: calc(var(--hex-size, 2.2em) * -0.5);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-hex-row-offset {
|
|
|
|
|
margin-left: calc(var(--hex-size, 2.2em) * 1.732 / 2);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* ── Flat-top column layout ───────────────────────────────────────────────── */
|
|
|
|
|
|
|
|
|
|
.duckmage-grid-flat {
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-direction: row;
|
|
|
|
|
align-items: flex-start;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-hex-col {
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
margin-right: calc(var(--hex-size, 2.2em) * -0.5);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-hex-col-offset {
|
|
|
|
|
margin-top: calc(var(--hex-size, 2.2em) * 0.866);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Flat-top hex shape and dimensions */
|
|
|
|
|
.duckmage-grid-flat .duckmage-hex {
|
|
|
|
|
width: calc(var(--hex-size) * 2);
|
|
|
|
|
height: calc(var(--hex-size) * 1.732);
|
|
|
|
|
clip-path: polygon(25% 0%, 75% 0%, 100% 50%, 75% 100%, 25% 100%, 0% 50%);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* ── Hex cell (shared) ────────────────────────────────────────────────────── */
|
|
|
|
|
|
|
|
|
|
.duckmage-hex {
|
|
|
|
|
--hex-size: 2.2em;
|
|
|
|
|
width: calc(var(--hex-size) * 1.732);
|
|
|
|
|
height: calc(var(--hex-size) * 2);
|
|
|
|
|
clip-path: polygon(50% 0%, 100% 25%, 100% 75%, 50% 100%, 0% 75%, 0% 25%);
|
|
|
|
|
background: var(--background-secondary);
|
|
|
|
|
border: 2px solid var(--background-modifier-border);
|
|
|
|
|
margin: var(--duckmage-hex-gap, 0.15em);
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
justify-content: center;
|
|
|
|
|
flex-shrink: 0;
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
position: relative;
|
|
|
|
|
transition: background-color 0.15s ease;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-hex:focus {
|
|
|
|
|
outline: none;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-hex:hover::after,
|
|
|
|
|
.duckmage-hex-exists:hover::after {
|
|
|
|
|
content: "";
|
|
|
|
|
position: absolute;
|
|
|
|
|
inset: 0;
|
|
|
|
|
background: rgba(255, 255, 180, 0.35);
|
|
|
|
|
pointer-events: none;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-hex.is-selected::after {
|
|
|
|
|
content: "";
|
|
|
|
|
position: absolute;
|
|
|
|
|
inset: 0;
|
|
|
|
|
background: rgba(255, 210, 0, 0.3);
|
|
|
|
|
pointer-events: none;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-hex-brush-hover::before {
|
|
|
|
|
content: "";
|
|
|
|
|
position: absolute;
|
|
|
|
|
inset: 0;
|
|
|
|
|
background: rgba(255, 255, 255, 0.18);
|
|
|
|
|
border-radius: inherit;
|
|
|
|
|
pointer-events: none;
|
|
|
|
|
z-index: 2;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-hex-exists {
|
|
|
|
|
border-color: var(--background-modifier-border-hover);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Terrain icon fills the hex, sits behind the label */
|
|
|
|
|
.duckmage-hex-icon {
|
|
|
|
|
position: absolute;
|
|
|
|
|
width: 78%;
|
|
|
|
|
height: 78%;
|
|
|
|
|
object-fit: contain;
|
|
|
|
|
pointer-events: none;
|
|
|
|
|
opacity: 0.75;
|
|
|
|
|
}
|
|
|
|
|
/* When the icon is rendered as a tinted div (mask-image) instead of an img */
|
|
|
|
|
div.duckmage-hex-icon {
|
|
|
|
|
object-fit: unset; /* not applicable to divs */
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-21 15:29:11 +00:00
|
|
|
/* Coord label: absolutely-positioned inside the hex so the parent's
|
|
|
|
|
flex centering doesn't fight us. Vertical anchor is set by a
|
|
|
|
|
`duckmage-coord-{top,middle,bottom}` class on the viewport — see
|
|
|
|
|
the rules below. Horizontal is always centered via translate(-50%). */
|
2026-04-04 15:51:53 +00:00
|
|
|
.duckmage-hex-label {
|
2026-06-21 17:00:32 +00:00
|
|
|
/* Kept in DOM as an insertBefore anchor for hex-icon ordering, but
|
|
|
|
|
NOT visible. The actual coord labels render in
|
|
|
|
|
.duckmage-coord-labels-layer (a gridContainer-level sibling of the
|
|
|
|
|
path/faction SVGs) so they stack above paths, GM icons, faction
|
|
|
|
|
and region overlays. */
|
|
|
|
|
visibility: hidden;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Coord labels — gridContainer-level sibling above path SVG (z:5),
|
|
|
|
|
faction SVG (z:4), region SVG (z:3). Positioned per-hex by percentage
|
|
|
|
|
of gridContainer dimensions so the labels auto-rescale through
|
|
|
|
|
bake-zoom font-size mutations (same approach as region labels). */
|
|
|
|
|
.duckmage-coord-labels-layer {
|
2026-06-21 15:29:11 +00:00
|
|
|
position: absolute;
|
2026-06-21 17:00:32 +00:00
|
|
|
top: 0;
|
|
|
|
|
left: 0;
|
|
|
|
|
width: 100%;
|
|
|
|
|
height: 100%;
|
|
|
|
|
pointer-events: none;
|
|
|
|
|
z-index: 10;
|
|
|
|
|
}
|
|
|
|
|
.duckmage-coord-label-html {
|
|
|
|
|
position: absolute;
|
|
|
|
|
left: var(--duckmage-coord-x, 50%);
|
|
|
|
|
top: var(--duckmage-coord-y, 50%);
|
|
|
|
|
/* Center on (x,y) then apply the placement em-offset.
|
|
|
|
|
Default font-size and family come from the viewport-level CSS vars
|
|
|
|
|
set in HexMapView.renderGrid(). */
|
|
|
|
|
transform: translate(-50%, calc(-50% + var(--duckmage-coord-y-offset, 0em)));
|
|
|
|
|
font-size: var(--duckmage-coord-font-size, 0.8em);
|
|
|
|
|
font-family: var(--duckmage-coord-font-family, inherit);
|
|
|
|
|
font-weight: 700;
|
|
|
|
|
color: var(--duckmage-coord-color, #ffffff);
|
2026-04-04 15:51:53 +00:00
|
|
|
pointer-events: none;
|
|
|
|
|
user-select: none;
|
2026-06-21 15:29:11 +00:00
|
|
|
white-space: nowrap;
|
2026-06-21 17:00:32 +00:00
|
|
|
text-rendering: optimizeLegibility;
|
|
|
|
|
-webkit-font-smoothing: antialiased;
|
|
|
|
|
/* Outline keeps labels readable over any underlying terrain color
|
|
|
|
|
or path stroke. */
|
|
|
|
|
text-shadow:
|
|
|
|
|
-1px -1px 0 var(--background-primary),
|
|
|
|
|
1px -1px 0 var(--background-primary),
|
|
|
|
|
-1px 1px 0 var(--background-primary),
|
|
|
|
|
1px 1px 0 var(--background-primary);
|
2026-06-21 15:29:11 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Coord placement (settings → "Coordinate placement"). The %s are tuned
|
|
|
|
|
so the label sits inside the visible hex polygon (which is clipped at
|
|
|
|
|
25%/75% on the vertical axis for pointy-top hexes and at 0/100% for
|
|
|
|
|
flat-top — but the label needs to stay readable in both, so we use a
|
|
|
|
|
conservative margin from the edge). */
|
2026-06-21 17:00:32 +00:00
|
|
|
/* Placement — em offsets relative to the hex height. With the default
|
|
|
|
|
--hex-size of 2.2em the hex is 4.4em tall; ±1.2em matches the previous
|
|
|
|
|
22%/78% within-hex positioning. */
|
|
|
|
|
.duckmage-hex-map-viewport.duckmage-coord-top .duckmage-coord-label-html {
|
|
|
|
|
--duckmage-coord-y-offset: -1.2em;
|
2026-06-21 15:29:11 +00:00
|
|
|
}
|
2026-06-21 17:00:32 +00:00
|
|
|
.duckmage-hex-map-viewport.duckmage-coord-middle .duckmage-coord-label-html {
|
|
|
|
|
--duckmage-coord-y-offset: 0em;
|
2026-06-21 15:29:11 +00:00
|
|
|
}
|
2026-06-21 17:00:32 +00:00
|
|
|
.duckmage-hex-map-viewport.duckmage-coord-bottom .duckmage-coord-label-html {
|
|
|
|
|
--duckmage-coord-y-offset: 1.2em;
|
2026-04-04 15:51:53 +00:00
|
|
|
}
|
|
|
|
|
|
2026-06-21 17:00:32 +00:00
|
|
|
/* (Coord text-shadow + color now live on .duckmage-coord-label-html since
|
|
|
|
|
the labels are no longer rendered inside the hex DOM.) */
|
2026-04-04 15:51:53 +00:00
|
|
|
|
|
|
|
|
.duckmage-hex-dot {
|
|
|
|
|
position: absolute;
|
|
|
|
|
bottom: 0.35em;
|
|
|
|
|
width: 4px;
|
|
|
|
|
height: 4px;
|
|
|
|
|
border-radius: 50%;
|
|
|
|
|
background: var(--interactive-accent);
|
|
|
|
|
pointer-events: none;
|
|
|
|
|
z-index: 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-hex-link-badge {
|
|
|
|
|
position: absolute;
|
|
|
|
|
top: 0.2em;
|
|
|
|
|
right: 0.2em;
|
|
|
|
|
font-size: 0.55em;
|
|
|
|
|
line-height: 1;
|
|
|
|
|
pointer-events: none;
|
|
|
|
|
z-index: 2;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-hex-table-linked {
|
|
|
|
|
outline: 2px solid var(--interactive-accent);
|
|
|
|
|
outline-offset: -2px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Swap tool overlays — must be child spans inside the hex so they're shaped by its clip-path */
|
|
|
|
|
.duckmage-hex-swap-source,
|
|
|
|
|
.duckmage-hex-swap-dest {
|
|
|
|
|
position: absolute;
|
|
|
|
|
inset: 0;
|
|
|
|
|
z-index: 4;
|
|
|
|
|
pointer-events: none;
|
|
|
|
|
}
|
|
|
|
|
.duckmage-hex-swap-source {
|
|
|
|
|
background: rgba(245, 158, 11, 0.45);
|
|
|
|
|
}
|
|
|
|
|
.duckmage-hex-swap-dest {
|
|
|
|
|
background: rgba(167, 139, 250, 0.45);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@keyframes duckmage-blip {
|
|
|
|
|
0% {
|
|
|
|
|
transform: translate(-50%, -50%) scale(0);
|
|
|
|
|
opacity: 0.55;
|
|
|
|
|
}
|
|
|
|
|
60% {
|
|
|
|
|
opacity: 0.3;
|
|
|
|
|
}
|
|
|
|
|
100% {
|
|
|
|
|
transform: translate(-50%, -50%) scale(2.2);
|
|
|
|
|
opacity: 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-hex-blip {
|
|
|
|
|
position: absolute;
|
|
|
|
|
top: 50%;
|
|
|
|
|
left: 50%;
|
|
|
|
|
width: 100%;
|
|
|
|
|
aspect-ratio: 1;
|
|
|
|
|
border-radius: 50%;
|
|
|
|
|
background: var(--interactive-accent);
|
|
|
|
|
pointer-events: none;
|
|
|
|
|
z-index: 3;
|
|
|
|
|
animation: duckmage-blip 0.55s ease-out forwards;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* ── Hex editor modal ─────────────────────────────────────────────────────── */
|
|
|
|
|
|
|
|
|
|
.duckmage-hex-editor h2 {
|
|
|
|
|
margin-bottom: 0.75em;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-hex-editor h3 {
|
|
|
|
|
margin: 0.25em 0 0.5em;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-hex-editor h4 {
|
|
|
|
|
margin: 0;
|
|
|
|
|
font-size: 0.9em;
|
|
|
|
|
font-weight: 600;
|
|
|
|
|
}
|
2026-05-23 16:29:22 +00:00
|
|
|
.duckmage-hex-editor .modal-content {
|
|
|
|
|
padding:0.5em;
|
|
|
|
|
}
|
2026-04-04 15:51:53 +00:00
|
|
|
.duckmage-editor-title-row {
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: flex-start;
|
|
|
|
|
gap: 8px;
|
|
|
|
|
}
|
|
|
|
|
.duckmage-editor-title-left {
|
|
|
|
|
flex: 1;
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: baseline;
|
|
|
|
|
gap: 12px;
|
|
|
|
|
flex-wrap: wrap;
|
|
|
|
|
}
|
|
|
|
|
.duckmage-editor-title-row h2 {
|
|
|
|
|
margin: 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* ── Neighbour hex preview widget ──────────────────────────────────── */
|
|
|
|
|
.duckmage-neighbor-widget {
|
|
|
|
|
position: relative;
|
|
|
|
|
width: 60px;
|
|
|
|
|
height: 51px;
|
|
|
|
|
flex-shrink: 0;
|
|
|
|
|
}
|
|
|
|
|
.duckmage-neighbor-tile {
|
|
|
|
|
position: absolute;
|
|
|
|
|
width: 16px;
|
|
|
|
|
height: 16px;
|
|
|
|
|
border-radius: 50%;
|
2026-06-06 03:04:11 +00:00
|
|
|
background-color: var(--duckmage-bg, var(--background-modifier-border));
|
2026-04-04 15:51:53 +00:00
|
|
|
cursor: pointer;
|
|
|
|
|
border: 1px solid
|
|
|
|
|
color-mix(in srgb, var(--background-modifier-border) 60%, transparent);
|
|
|
|
|
transition:
|
|
|
|
|
border-color 0.15s,
|
|
|
|
|
transform 0.1s;
|
|
|
|
|
box-sizing: border-box;
|
|
|
|
|
}
|
|
|
|
|
.duckmage-neighbor-tile:hover {
|
|
|
|
|
border-color: var(--interactive-accent);
|
|
|
|
|
transform: scale(1.25);
|
|
|
|
|
z-index: 1;
|
|
|
|
|
}
|
|
|
|
|
.duckmage-neighbor-tile-offmap {
|
|
|
|
|
opacity: 0.2;
|
|
|
|
|
cursor: default;
|
|
|
|
|
pointer-events: none;
|
|
|
|
|
}
|
2026-05-23 16:29:22 +00:00
|
|
|
.duckmage-neighbor-centre {
|
|
|
|
|
position: absolute;
|
|
|
|
|
width: 12px;
|
|
|
|
|
height: 12px;
|
|
|
|
|
border-radius: 50%;
|
|
|
|
|
background: var(--background-modifier-border);
|
|
|
|
|
box-sizing: border-box;
|
|
|
|
|
pointer-events: none;
|
|
|
|
|
}
|
|
|
|
|
.duckmage-neighbor-centre--linked {
|
|
|
|
|
background: #1a1a1a;
|
|
|
|
|
border: 1px solid color-mix(in srgb, #fff 30%, transparent);
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
pointer-events: auto;
|
|
|
|
|
transition: transform 0.1s;
|
|
|
|
|
}
|
|
|
|
|
.duckmage-neighbor-centre--linked:hover {
|
|
|
|
|
transform: scale(1.3);
|
|
|
|
|
}
|
2026-04-04 15:51:53 +00:00
|
|
|
|
|
|
|
|
/* ── Draggable / resizable modal ───────────────────────────────────── */
|
2026-05-15 19:06:16 +00:00
|
|
|
.modal.duckmage-editor-modal-drag {
|
|
|
|
|
width: min(620px, 90vw);
|
|
|
|
|
max-height: min(90vh, 860px);
|
|
|
|
|
max-width: none;
|
2026-04-04 15:51:53 +00:00
|
|
|
min-width: 320px;
|
2026-05-23 18:26:22 +00:00
|
|
|
min-height: 420px;
|
2026-04-04 15:51:53 +00:00
|
|
|
}
|
|
|
|
|
.duckmage-editor-modal-drag .modal-content {
|
|
|
|
|
overflow-y: auto;
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
}
|
|
|
|
|
.duckmage-editor-title-drag {
|
|
|
|
|
cursor: move;
|
|
|
|
|
user-select: none;
|
|
|
|
|
}
|
|
|
|
|
/* Native close-button row above our content is also part of the drag zone */
|
|
|
|
|
.duckmage-editor-modal-drag .modal-close-button {
|
|
|
|
|
cursor: move;
|
|
|
|
|
}
|
|
|
|
|
.duckmage-editor-modal-drag .modal-title {
|
|
|
|
|
cursor: move;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-editor-center-btn {
|
|
|
|
|
font-size: 0.85em;
|
|
|
|
|
padding: 1px 6px;
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
border-radius: 4px;
|
|
|
|
|
background: transparent;
|
|
|
|
|
border: 1px solid var(--background-modifier-border);
|
|
|
|
|
color: var(--text-muted);
|
|
|
|
|
line-height: 1.4;
|
|
|
|
|
}
|
|
|
|
|
.duckmage-editor-center-btn:hover {
|
|
|
|
|
color: var(--text-normal);
|
|
|
|
|
border-color: var(--interactive-accent);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-editor-open-link {
|
|
|
|
|
font-size: 0.8em;
|
|
|
|
|
color: var(--text-accent);
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
white-space: nowrap;
|
|
|
|
|
}
|
|
|
|
|
.duckmage-editor-open-link:hover {
|
|
|
|
|
text-decoration: underline;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-editor-divider {
|
|
|
|
|
margin: 1em 0;
|
|
|
|
|
border: none;
|
|
|
|
|
border-top: 1px solid var(--background-modifier-border);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-editor-section {
|
|
|
|
|
margin-bottom: 1em;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-editor-collapsible-header {
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
gap: 0.4em;
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
user-select: none;
|
|
|
|
|
margin: 0.25em 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-editor-collapsible-header:hover .duckmage-editor-collapsible-title {
|
|
|
|
|
color: var(--text-normal);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-editor-collapsible-arrow {
|
|
|
|
|
font-size: 0.65em;
|
|
|
|
|
color: var(--text-muted);
|
|
|
|
|
line-height: 1;
|
|
|
|
|
flex-shrink: 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-editor-collapsible-title {
|
|
|
|
|
margin: 0;
|
|
|
|
|
color: var(--text-muted);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-editor-collapsible-body {
|
|
|
|
|
margin-bottom: 0.5em;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Terrain preview badge in the collapsible header */
|
|
|
|
|
.duckmage-terrain-header-preview {
|
|
|
|
|
display: inline-flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
gap: 0.35em;
|
|
|
|
|
margin-left: 0.6em;
|
|
|
|
|
pointer-events: none;
|
|
|
|
|
}
|
|
|
|
|
.duckmage-terrain-header-swatch {
|
|
|
|
|
width: 16px;
|
|
|
|
|
height: 16px;
|
|
|
|
|
border-radius: 3px;
|
|
|
|
|
flex-shrink: 0;
|
|
|
|
|
position: relative;
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
}
|
|
|
|
|
.duckmage-terrain-header-swatch img {
|
|
|
|
|
position: absolute;
|
|
|
|
|
inset: 0;
|
|
|
|
|
width: 100%;
|
|
|
|
|
height: 100%;
|
|
|
|
|
object-fit: contain;
|
|
|
|
|
}
|
|
|
|
|
.duckmage-terrain-header-name {
|
|
|
|
|
font-size: 0.75em;
|
|
|
|
|
color: var(--text-faint);
|
|
|
|
|
font-weight: normal;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-collapse-cb-label {
|
|
|
|
|
display: inline-flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
gap: 4px;
|
|
|
|
|
margin-right: 14px;
|
|
|
|
|
font-size: var(--font-ui-small);
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* ── Terrain picker modal header ─────────────────────────────────── */
|
|
|
|
|
.duckmage-tpe-header {
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
justify-content: space-between;
|
|
|
|
|
margin-bottom: 0.5em;
|
|
|
|
|
}
|
|
|
|
|
.duckmage-tpe-header h2 {
|
|
|
|
|
margin: 0;
|
|
|
|
|
}
|
|
|
|
|
.duckmage-tpe-edit-btn {
|
|
|
|
|
font-size: 0.8em;
|
|
|
|
|
padding: 2px 10px;
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
border-radius: 4px;
|
|
|
|
|
background: var(--background-secondary);
|
|
|
|
|
border: 1px solid var(--background-modifier-border);
|
|
|
|
|
color: var(--text-muted);
|
|
|
|
|
flex-shrink: 0;
|
|
|
|
|
}
|
|
|
|
|
.duckmage-tpe-edit-btn:hover {
|
|
|
|
|
color: var(--text-normal);
|
|
|
|
|
border-color: var(--interactive-accent);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-tpe-edit-footer {
|
|
|
|
|
margin-top: 12px;
|
|
|
|
|
padding-top: 10px;
|
|
|
|
|
border-top: 1px solid var(--background-modifier-border);
|
|
|
|
|
display: flex;
|
|
|
|
|
justify-content: flex-end;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-tpe-refresh-btn {
|
|
|
|
|
font-size: 0.8em;
|
|
|
|
|
padding: 3px 10px;
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
border-radius: 4px;
|
|
|
|
|
background: var(--background-secondary);
|
|
|
|
|
border: 1px solid var(--background-modifier-border);
|
|
|
|
|
color: var(--text-muted);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-tpe-refresh-btn:hover:not(:disabled) {
|
|
|
|
|
color: var(--text-normal);
|
|
|
|
|
border-color: var(--interactive-accent);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-tpe-refresh-btn:disabled {
|
|
|
|
|
opacity: 0.5;
|
|
|
|
|
cursor: default;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Terrain picker grid — capped at 2 rows, scrollable */
|
|
|
|
|
.duckmage-terrain-picker {
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-wrap: wrap;
|
|
|
|
|
gap: 0.4em;
|
|
|
|
|
margin-bottom: 0.75em;
|
|
|
|
|
max-height: 132px;
|
|
|
|
|
overflow-y: auto;
|
|
|
|
|
padding-right: 2px; /* avoid clipping scrollbar */
|
|
|
|
|
}
|
|
|
|
|
.duckmage-terrain-picker.duckmage-terrain-picker-full {
|
|
|
|
|
max-height: none;
|
|
|
|
|
overflow-y: visible;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-terrain-option {
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
align-items: center;
|
|
|
|
|
gap: 0.25em;
|
|
|
|
|
padding: 0.35em;
|
|
|
|
|
border-radius: 6px;
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
border: 2px solid transparent;
|
|
|
|
|
background: var(--background-secondary);
|
|
|
|
|
transition:
|
|
|
|
|
border-color 0.1s ease,
|
|
|
|
|
background 0.1s ease;
|
|
|
|
|
min-width: 52px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-terrain-option:hover {
|
|
|
|
|
background: var(--background-modifier-hover);
|
|
|
|
|
border-color: var(--interactive-accent);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-terrain-option.is-selected {
|
|
|
|
|
border-color: var(--interactive-accent);
|
|
|
|
|
background: var(--background-modifier-hover);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-terrain-preview {
|
|
|
|
|
width: 36px;
|
|
|
|
|
height: 36px;
|
|
|
|
|
border-radius: 4px;
|
|
|
|
|
position: relative;
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
flex-shrink: 0;
|
|
|
|
|
}
|
|
|
|
|
.duckmage-terrain-preview-eyedropper {
|
|
|
|
|
background: var(--background-secondary);
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
justify-content: center;
|
|
|
|
|
font-size: 1.1em;
|
|
|
|
|
color: var(--text-accent);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-terrain-option-eyedropper:hover .duckmage-terrain-preview-eyedropper {
|
|
|
|
|
color: var(--interactive-accent);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-terrain-preview-clear {
|
|
|
|
|
background: repeating-linear-gradient(
|
|
|
|
|
45deg,
|
|
|
|
|
var(--background-modifier-border) 0px,
|
|
|
|
|
var(--background-modifier-border) 2px,
|
|
|
|
|
var(--background-secondary) 2px,
|
|
|
|
|
var(--background-secondary) 8px
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-terrain-preview-icon {
|
|
|
|
|
position: absolute;
|
|
|
|
|
inset: 0;
|
|
|
|
|
width: 100%;
|
|
|
|
|
height: 100%;
|
|
|
|
|
object-fit: contain;
|
|
|
|
|
}
|
|
|
|
|
div.duckmage-terrain-preview-icon {
|
|
|
|
|
object-fit: unset;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-terrain-option-name {
|
|
|
|
|
font-size: 0.7em;
|
|
|
|
|
text-align: center;
|
|
|
|
|
color: var(--text-muted);
|
|
|
|
|
white-space: nowrap;
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
text-overflow: ellipsis;
|
|
|
|
|
max-width: 54px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-clear-btn {
|
|
|
|
|
margin-left: 4px;
|
|
|
|
|
flex-shrink: 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* ── Terrain edit-mode grid tiles ────────────────────────────────── */
|
|
|
|
|
.duckmage-terrain-option-editable {
|
|
|
|
|
position: relative;
|
|
|
|
|
cursor: grab;
|
|
|
|
|
}
|
|
|
|
|
.duckmage-terrain-option-editable:active {
|
|
|
|
|
cursor: grabbing;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Grip handle — top-left corner */
|
|
|
|
|
.duckmage-terrain-edit-grip {
|
|
|
|
|
position: absolute;
|
|
|
|
|
top: 3px;
|
|
|
|
|
left: 3px;
|
|
|
|
|
font-size: 0.75em;
|
|
|
|
|
color: var(--text-faint);
|
|
|
|
|
line-height: 1;
|
|
|
|
|
z-index: 2;
|
|
|
|
|
pointer-events: none;
|
|
|
|
|
opacity: 0.5;
|
|
|
|
|
}
|
|
|
|
|
.duckmage-terrain-option-editable:hover .duckmage-terrain-edit-grip {
|
|
|
|
|
opacity: 1;
|
|
|
|
|
color: var(--text-muted);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Pencil icon — top-right corner, decorative only */
|
|
|
|
|
.duckmage-terrain-edit-pencil {
|
|
|
|
|
position: absolute;
|
|
|
|
|
top: 2px;
|
|
|
|
|
right: 4px;
|
|
|
|
|
font-size: 0.75em;
|
|
|
|
|
line-height: 1;
|
|
|
|
|
z-index: 2;
|
|
|
|
|
pointer-events: none;
|
|
|
|
|
opacity: 0.4;
|
|
|
|
|
color: var(--text-faint);
|
|
|
|
|
}
|
|
|
|
|
.duckmage-terrain-option-editable:hover .duckmage-terrain-edit-pencil {
|
|
|
|
|
opacity: 0.8;
|
|
|
|
|
color: var(--text-muted);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Drop target highlight during drag-reorder */
|
|
|
|
|
.duckmage-terrain-option-editable.duckmage-palette-drop-target {
|
|
|
|
|
border-color: var(--interactive-accent);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Duplicate-name error state on terrain name input */
|
2026-05-15 19:06:16 +00:00
|
|
|
input.duckmage-input-error {
|
|
|
|
|
border-color: var(--color-red);
|
2026-04-04 15:51:53 +00:00
|
|
|
outline: 1px solid var(--color-red);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* "Add" tile preview */
|
|
|
|
|
.duckmage-terrain-preview-add {
|
|
|
|
|
background: var(--background-modifier-border);
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
justify-content: center;
|
|
|
|
|
font-size: 1.4em;
|
|
|
|
|
font-weight: 300;
|
|
|
|
|
color: var(--text-muted);
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-25 16:49:41 +00:00
|
|
|
.duckmage-terrain-preview-remove {
|
|
|
|
|
background: var(--background-modifier-border);
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
justify-content: center;
|
|
|
|
|
font-size: 1.2em;
|
|
|
|
|
color: var(--color-red);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-terrain-option-remove:hover .duckmage-terrain-preview-remove {
|
|
|
|
|
background: var(--background-modifier-error);
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-04 15:51:53 +00:00
|
|
|
/* ── Terrain entry editor modal buttons ──────────────────────────── */
|
|
|
|
|
.duckmage-tee-buttons {
|
|
|
|
|
display: flex;
|
|
|
|
|
gap: 0.5em;
|
|
|
|
|
flex-wrap: wrap;
|
|
|
|
|
margin-top: 1.25em;
|
|
|
|
|
padding-top: 0.75em;
|
|
|
|
|
border-top: 1px solid var(--background-modifier-border);
|
|
|
|
|
}
|
|
|
|
|
.duckmage-btn-danger {
|
|
|
|
|
color: var(--text-error);
|
|
|
|
|
border-color: var(--background-modifier-border);
|
|
|
|
|
margin-left: auto;
|
|
|
|
|
}
|
|
|
|
|
.duckmage-btn-danger:hover {
|
|
|
|
|
background: var(--background-modifier-error);
|
|
|
|
|
border-color: var(--text-error);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Link sections */
|
|
|
|
|
.duckmage-editor-link-section {
|
|
|
|
|
margin-bottom: 0.85em;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-link-section-title {
|
|
|
|
|
margin: 0 0 0.3em 0;
|
|
|
|
|
font-size: 0.9em;
|
|
|
|
|
color: var(--text-muted);
|
|
|
|
|
font-weight: 600;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* ── Link combo box ─────────────────────────────────────────────────────── */
|
|
|
|
|
.duckmage-link-combo {
|
|
|
|
|
position: relative;
|
|
|
|
|
display: flex;
|
|
|
|
|
gap: 4px;
|
|
|
|
|
margin-bottom: 0.4em;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-link-combo-input {
|
|
|
|
|
flex: 1;
|
|
|
|
|
min-width: 0;
|
|
|
|
|
padding: 3px 6px;
|
|
|
|
|
border: 1px solid var(--background-modifier-border);
|
|
|
|
|
border-radius: 4px;
|
|
|
|
|
background: var(--background-secondary);
|
|
|
|
|
color: var(--text-normal);
|
|
|
|
|
font-size: var(--font-ui-small);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-link-combo-input:focus {
|
|
|
|
|
outline: none;
|
|
|
|
|
border-color: var(--interactive-accent);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-link-combo-arrow {
|
|
|
|
|
flex-shrink: 0;
|
|
|
|
|
padding: 2px 7px;
|
|
|
|
|
border: 1px solid var(--background-modifier-border);
|
|
|
|
|
border-radius: 4px;
|
|
|
|
|
background: var(--background-secondary);
|
|
|
|
|
color: var(--text-muted);
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
font-size: 0.9em;
|
|
|
|
|
line-height: 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-link-combo-arrow:hover {
|
|
|
|
|
background: var(--background-modifier-hover);
|
|
|
|
|
color: var(--text-normal);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-link-combo-dropdown {
|
2026-06-23 23:02:46 +00:00
|
|
|
/* position: fixed (anchored from JS via anchorDropdown) so the dropdown
|
|
|
|
|
escapes the modal's scroll container without that container needing
|
|
|
|
|
`overflow: visible` — which would reset its scrollTop and jump the
|
|
|
|
|
modal to the top (issue #31). left/top/width are set inline. */
|
|
|
|
|
position: fixed;
|
2026-05-23 16:42:04 +00:00
|
|
|
z-index: 100;
|
2026-04-04 15:51:53 +00:00
|
|
|
background: var(--background-primary);
|
|
|
|
|
border: 1px solid var(--background-modifier-border);
|
|
|
|
|
border-radius: 4px;
|
|
|
|
|
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2);
|
|
|
|
|
max-height: 180px;
|
|
|
|
|
overflow-y: auto;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-link-combo-item {
|
|
|
|
|
padding: 5px 8px;
|
|
|
|
|
font-size: var(--font-ui-small);
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
color: var(--text-normal);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-link-combo-item:hover {
|
|
|
|
|
background: var(--background-modifier-hover);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-link-combo-create {
|
|
|
|
|
color: var(--interactive-accent);
|
|
|
|
|
border-top: 1px solid var(--background-modifier-border-focus);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-link-combo-empty {
|
|
|
|
|
padding: 5px 8px;
|
|
|
|
|
font-size: var(--font-ui-small);
|
|
|
|
|
color: var(--text-faint);
|
|
|
|
|
font-style: italic;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-link-list {
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
gap: 0.2em;
|
|
|
|
|
padding-left: 0.5em;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-link-item {
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
gap: 4px;
|
|
|
|
|
font-size: 0.85em;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-link-item-label {
|
|
|
|
|
color: var(--text-accent);
|
|
|
|
|
flex: 1;
|
|
|
|
|
min-width: 0;
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
text-overflow: ellipsis;
|
|
|
|
|
white-space: nowrap;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-link-item-clickable {
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-link-item-clickable:hover {
|
|
|
|
|
text-decoration: underline;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-link-remove-btn,
|
|
|
|
|
.duckmage-link-roll-btn {
|
|
|
|
|
flex-shrink: 0;
|
|
|
|
|
background: none;
|
|
|
|
|
border: none;
|
|
|
|
|
color: var(--text-faint);
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
font-size: 1em;
|
|
|
|
|
line-height: 1;
|
|
|
|
|
padding: 0 2px;
|
|
|
|
|
border-radius: 3px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-link-remove-btn:hover {
|
|
|
|
|
color: var(--text-error);
|
|
|
|
|
background: var(--background-modifier-error-hover);
|
|
|
|
|
}
|
|
|
|
|
.duckmage-link-roll-btn:hover {
|
|
|
|
|
color: var(--text-normal);
|
|
|
|
|
background: var(--background-modifier-hover);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-link-empty {
|
|
|
|
|
font-size: 0.85em;
|
|
|
|
|
color: var(--text-faint);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-suggestion-path {
|
|
|
|
|
color: var(--text-muted);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* ── Roads & Rivers toolbar ───────────────────────────────────────────────── */
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
.duckmage-draw-btn {
|
|
|
|
|
height: 26px;
|
|
|
|
|
padding: 0 10px;
|
|
|
|
|
border-radius: 4px;
|
|
|
|
|
background: var(--background-secondary);
|
|
|
|
|
border: 1px solid var(--background-modifier-border);
|
|
|
|
|
color: var(--text-muted);
|
|
|
|
|
font-size: 0.75em;
|
|
|
|
|
font-weight: 600;
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
opacity: 0.6;
|
|
|
|
|
transition:
|
|
|
|
|
opacity 0.15s ease,
|
|
|
|
|
border-color 0.15s ease,
|
|
|
|
|
color 0.15s ease;
|
|
|
|
|
user-select: none;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-draw-btn:hover {
|
|
|
|
|
opacity: 1;
|
|
|
|
|
color: var(--text-normal);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-draw-btn.is-active {
|
|
|
|
|
opacity: 1;
|
|
|
|
|
border-color: var(--interactive-accent);
|
|
|
|
|
color: var(--interactive-accent);
|
|
|
|
|
background: var(--background-modifier-hover);
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-25 16:49:41 +00:00
|
|
|
.duckmage-draw-btn.is-erase {
|
|
|
|
|
border-color: var(--color-red);
|
|
|
|
|
color: var(--color-red);
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-04 15:51:53 +00:00
|
|
|
.duckmage-draw-btn-terrain,
|
|
|
|
|
.duckmage-draw-btn-tablelink {
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
gap: 5px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-terrain-btn-preview {
|
|
|
|
|
display: none;
|
|
|
|
|
width: 10px;
|
|
|
|
|
height: 10px;
|
|
|
|
|
border-radius: 2px;
|
|
|
|
|
flex-shrink: 0;
|
|
|
|
|
border: 1px solid rgba(255, 255, 255, 0.25);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-path-btn-swatch {
|
|
|
|
|
display: none;
|
|
|
|
|
width: 12px;
|
|
|
|
|
height: 12px;
|
|
|
|
|
border-radius: 2px;
|
|
|
|
|
margin-left: 4px;
|
|
|
|
|
vertical-align: middle;
|
|
|
|
|
flex-shrink: 0;
|
|
|
|
|
border: 1px solid rgba(255, 255, 255, 0.25);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-path-preview {
|
|
|
|
|
width: 36px;
|
|
|
|
|
height: 36px;
|
|
|
|
|
flex-shrink: 0;
|
|
|
|
|
border-radius: 4px;
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-path-editor-preview {
|
|
|
|
|
margin: 0 auto 12px;
|
|
|
|
|
width: 72px;
|
|
|
|
|
height: 72px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-path-editor-preview svg {
|
|
|
|
|
width: 100%;
|
|
|
|
|
height: 100%;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-path-type-list {
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
gap: 4px;
|
|
|
|
|
margin: 8px 0;
|
|
|
|
|
}
|
|
|
|
|
.duckmage-path-type-row {
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
gap: 8px;
|
|
|
|
|
font-size: 0.85em;
|
|
|
|
|
}
|
|
|
|
|
.duckmage-path-type-swatch {
|
|
|
|
|
display: inline-block;
|
|
|
|
|
width: 14px;
|
|
|
|
|
height: 14px;
|
|
|
|
|
border-radius: 3px;
|
|
|
|
|
flex-shrink: 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-tpe-brush-row {
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
gap: 6px;
|
|
|
|
|
padding: 6px 12px 2px;
|
|
|
|
|
flex-wrap: wrap;
|
|
|
|
|
}
|
|
|
|
|
.duckmage-tpe-brush-label {
|
|
|
|
|
font-size: 0.85em;
|
|
|
|
|
color: var(--text-muted);
|
|
|
|
|
margin-right: 2px;
|
|
|
|
|
}
|
|
|
|
|
.duckmage-tpe-brush-btn {
|
|
|
|
|
padding: 2px 10px;
|
|
|
|
|
border-radius: 4px;
|
|
|
|
|
font-size: 0.85em;
|
|
|
|
|
border: 1px solid var(--background-modifier-border);
|
|
|
|
|
background: var(--background-secondary);
|
|
|
|
|
color: var(--text-muted);
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
}
|
|
|
|
|
.duckmage-tpe-brush-btn.is-active {
|
|
|
|
|
background: var(--interactive-accent);
|
|
|
|
|
color: var(--text-on-accent);
|
|
|
|
|
border-color: var(--interactive-accent);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-hex-map-viewport.duckmage-draw-mode,
|
|
|
|
|
.duckmage-hex-map-viewport.duckmage-draw-mode .duckmage-hex {
|
|
|
|
|
cursor: crosshair;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* While terrain picker modal is open */
|
|
|
|
|
.duckmage-hex-map-viewport.duckmage-terrain-picking,
|
|
|
|
|
.duckmage-hex-map-viewport.duckmage-terrain-picking .duckmage-hex {
|
|
|
|
|
cursor: crosshair;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* While actively painting terrain (terrain selected, not eyedropper) */
|
|
|
|
|
.duckmage-hex-map-viewport.duckmage-terrain-paint .duckmage-hex {
|
|
|
|
|
cursor:
|
|
|
|
|
url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='20' height='20'%3E%3Cpath d='M16 2 L18 4 L6 16 L2 18 L4 14 Z' fill='%23333'/%3E%3Ccircle cx='3' cy='17' r='2' fill='%235080ff'/%3E%3C/svg%3E")
|
|
|
|
|
2 17,
|
|
|
|
|
crosshair;
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-21 15:21:08 +00:00
|
|
|
/* Coord labels live in each hex's DOM (.duckmage-hex-label) — the SVG
|
|
|
|
|
path overlay no longer renders a duplicate. The old rule that hid
|
|
|
|
|
HTML labels while the SVG overlay was active is gone with that code;
|
|
|
|
|
HTML labels are always visible, and they track the hex layout
|
|
|
|
|
perfectly across font-size changes (no slip-on-zoom). */
|
2026-04-04 15:51:53 +00:00
|
|
|
|
2026-05-21 07:38:01 +00:00
|
|
|
/* ── Expand / shrink button groups ───────────────────────────────────────── */
|
2026-04-04 15:51:53 +00:00
|
|
|
|
2026-05-21 07:38:01 +00:00
|
|
|
.duckmage-expand-group {
|
2026-04-04 15:51:53 +00:00
|
|
|
position: absolute;
|
|
|
|
|
z-index: 10;
|
2026-05-21 07:38:01 +00:00
|
|
|
display: flex;
|
|
|
|
|
gap: 4px;
|
|
|
|
|
align-items: center;
|
|
|
|
|
justify-content: center;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-expand-group-top,
|
|
|
|
|
.duckmage-expand-group-bottom { flex-direction: row; }
|
|
|
|
|
.duckmage-expand-group-left,
|
|
|
|
|
.duckmage-expand-group-right { flex-direction: column; }
|
|
|
|
|
|
|
|
|
|
.duckmage-expand-group-top { top: 4px; left: 50%; transform: translateX(-50%); }
|
|
|
|
|
.duckmage-expand-group-bottom { bottom: 4px; left: 50%; transform: translateX(-50%); }
|
|
|
|
|
.duckmage-expand-group-left { left: 4px; top: 50%; transform: translateY(-50%); }
|
|
|
|
|
.duckmage-expand-group-right { right: 4px; top: 50%; transform: translateY(-50%); }
|
|
|
|
|
|
|
|
|
|
.duckmage-expand-btn,
|
|
|
|
|
.duckmage-shrink-btn {
|
2026-04-04 15:51:53 +00:00
|
|
|
width: 26px;
|
|
|
|
|
height: 26px;
|
|
|
|
|
padding: 0;
|
|
|
|
|
border-radius: 50%;
|
|
|
|
|
background: var(--background-secondary);
|
|
|
|
|
border: 1px solid var(--background-modifier-border);
|
|
|
|
|
color: var(--text-muted);
|
|
|
|
|
font-size: 1.1em;
|
|
|
|
|
line-height: 1;
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
justify-content: center;
|
|
|
|
|
transition:
|
2026-05-21 07:38:01 +00:00
|
|
|
opacity 0.2s ease,
|
|
|
|
|
color 0.15s ease,
|
|
|
|
|
border-color 0.15s ease;
|
2026-04-04 15:51:53 +00:00
|
|
|
}
|
|
|
|
|
|
2026-05-21 07:38:01 +00:00
|
|
|
.duckmage-expand-btn { opacity: 0.5; }
|
|
|
|
|
.duckmage-expand-btn:hover { opacity: 1; color: var(--text-normal); }
|
2026-04-04 15:51:53 +00:00
|
|
|
|
2026-05-21 07:38:01 +00:00
|
|
|
/* Shrink button: hidden until hovering the group */
|
|
|
|
|
.duckmage-shrink-btn { opacity: 0; }
|
|
|
|
|
.duckmage-expand-group:hover .duckmage-shrink-btn { opacity: 0.5; }
|
2026-05-21 07:51:47 +00:00
|
|
|
.duckmage-expand-group:hover .duckmage-shrink-btn:hover { opacity: 1; color: var(--text-normal); }
|
2026-05-21 07:38:01 +00:00
|
|
|
|
|
|
|
|
.duckmage-shrink-btn.is-dirty {
|
|
|
|
|
color: var(--text-error);
|
|
|
|
|
border-color: var(--background-modifier-error);
|
2026-04-04 15:51:53 +00:00
|
|
|
}
|
2026-05-21 07:38:01 +00:00
|
|
|
.duckmage-shrink-btn.is-dirty:hover { color: var(--text-error); }
|
|
|
|
|
|
2026-05-21 07:51:47 +00:00
|
|
|
.duckmage-expand-group:hover .duckmage-shrink-btn.is-confirming {
|
|
|
|
|
opacity: 1;
|
2026-05-21 07:38:01 +00:00
|
|
|
color: var(--text-on-accent);
|
|
|
|
|
background: var(--background-modifier-error);
|
|
|
|
|
border-color: var(--background-modifier-error);
|
|
|
|
|
font-size: 0.7em;
|
|
|
|
|
width: auto;
|
|
|
|
|
padding: 0 5px;
|
2026-04-04 15:51:53 +00:00
|
|
|
}
|
2026-05-21 07:38:01 +00:00
|
|
|
|
|
|
|
|
/* Warning label — shown inline via is-visible class */
|
|
|
|
|
.duckmage-shrink-warn {
|
|
|
|
|
display: none;
|
|
|
|
|
position: absolute;
|
|
|
|
|
font-size: 0.65em;
|
|
|
|
|
color: var(--text-error);
|
|
|
|
|
white-space: nowrap;
|
|
|
|
|
pointer-events: none;
|
|
|
|
|
background: var(--background-primary);
|
|
|
|
|
border: 1px solid var(--background-modifier-error);
|
|
|
|
|
border-radius: 4px;
|
|
|
|
|
padding: 1px 5px;
|
2026-04-04 15:51:53 +00:00
|
|
|
}
|
2026-05-21 07:38:01 +00:00
|
|
|
.duckmage-shrink-warn.is-visible { display: block; }
|
|
|
|
|
|
|
|
|
|
.duckmage-expand-group-top .duckmage-shrink-warn { top: calc(100% + 4px); left: 50%; transform: translateX(-50%); }
|
|
|
|
|
.duckmage-expand-group-bottom .duckmage-shrink-warn { bottom: calc(100% + 4px); left: 50%; transform: translateX(-50%); }
|
|
|
|
|
.duckmage-expand-group-left .duckmage-shrink-warn { left: calc(100% + 4px); top: 50%; transform: translateY(-50%); }
|
|
|
|
|
.duckmage-expand-group-right .duckmage-shrink-warn { right: calc(100% + 4px); top: 50%; transform: translateY(-50%); }
|
2026-04-04 15:51:53 +00:00
|
|
|
|
|
|
|
|
.duckmage-table-btn,
|
|
|
|
|
.duckmage-rt-btn,
|
|
|
|
|
.duckmage-undo-btn-map {
|
|
|
|
|
position: absolute;
|
|
|
|
|
top: 8px;
|
|
|
|
|
z-index: 10;
|
|
|
|
|
height: 26px;
|
|
|
|
|
padding: 0 8px;
|
|
|
|
|
border-radius: 4px;
|
|
|
|
|
background: var(--background-secondary);
|
|
|
|
|
border: 1px solid var(--background-modifier-border);
|
|
|
|
|
color: var(--text-muted);
|
|
|
|
|
font-size: 1em;
|
|
|
|
|
line-height: 1;
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
opacity: 0.6;
|
|
|
|
|
transition:
|
|
|
|
|
opacity 0.15s ease,
|
|
|
|
|
color 0.15s ease;
|
|
|
|
|
user-select: none;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-table-btn {
|
|
|
|
|
left: 8px;
|
|
|
|
|
}
|
|
|
|
|
.duckmage-rt-btn {
|
|
|
|
|
left: 44px;
|
|
|
|
|
}
|
|
|
|
|
|
feat: export module — PDF/Markdown for notes, hexes, maps, tables, workflows (1.2.0)
Adds a unified export pipeline under src/export/ with exporters for single notes,
structured hexes, maps (with reference table), random tables, and workflows
(with rolled samples). PDF rendering reuses Obsidian's MarkdownRenderer + theme
CSS; map PNG/PDF goes through a dedicated canvas renderer.
UI surface:
- "Export" tab in MapModal (PNG/PDF with overlay + size options)
- "Export" link in HexEditorModal + HexExportModal
- "Export PDF/Markdown" links in RandomTableView header
- "Export…" button in WorkflowEditorModal + WorkflowExportModal
- Commands: export current note (PDF/MD), current hex, current workflow
- File-menu items on any .md file: Export to PDF / Markdown
Other:
- New `exportFolder` setting (defaults to {worldFolder}/exports)
- hexGeometry: add hexSize / hexCenter / hexPolygonPoints / gridBoundingBox
- HexTableView + HexEditorModal: first-wins on duplicate palette names
(matches HexMapView's .find() behaviour)
- Tests: unit + integration suites for all exporters; sim-vault fixture
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-06-06 00:46:41 +00:00
|
|
|
/* ── Export tab in the Maps modal ─────────────────────────────────────────── */
|
|
|
|
|
.duckmage-export-tab {
|
|
|
|
|
padding: 0.5em 0;
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
gap: 0.8em;
|
|
|
|
|
}
|
|
|
|
|
.duckmage-export-tab-hint {
|
|
|
|
|
color: var(--text-muted);
|
|
|
|
|
font-size: 0.9em;
|
|
|
|
|
margin: 0;
|
|
|
|
|
}
|
|
|
|
|
.duckmage-export-tab-options {
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
gap: 0.4em;
|
|
|
|
|
}
|
|
|
|
|
.duckmage-export-tab-row {
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
gap: 0.5em;
|
|
|
|
|
padding: 0.2em 0;
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
}
|
|
|
|
|
.duckmage-export-tab-label {
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
}
|
|
|
|
|
.duckmage-export-tab-number {
|
|
|
|
|
width: 80px;
|
|
|
|
|
margin-left: auto;
|
|
|
|
|
}
|
|
|
|
|
.duckmage-export-tab-text {
|
|
|
|
|
flex: 1;
|
|
|
|
|
margin-left: 0.5em;
|
|
|
|
|
min-width: 0;
|
|
|
|
|
}
|
|
|
|
|
.duckmage-export-tab-select {
|
|
|
|
|
flex: 1;
|
|
|
|
|
margin-left: 0.5em;
|
|
|
|
|
min-width: 0;
|
|
|
|
|
}
|
|
|
|
|
.duckmage-export-tab-preview {
|
|
|
|
|
font-family: var(--font-monospace);
|
|
|
|
|
font-size: 0.85em;
|
|
|
|
|
color: var(--text-muted);
|
|
|
|
|
padding: 0.3em 0;
|
|
|
|
|
word-break: break-all;
|
|
|
|
|
}
|
|
|
|
|
.duckmage-export-tab-actions {
|
|
|
|
|
display: flex;
|
|
|
|
|
justify-content: flex-end;
|
|
|
|
|
gap: 0.5em;
|
|
|
|
|
padding-top: 0.5em;
|
|
|
|
|
border-top: 1px solid var(--background-modifier-border);
|
|
|
|
|
flex-wrap: wrap;
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-04 15:51:53 +00:00
|
|
|
.duckmage-undo-btn-map {
|
|
|
|
|
left: 8px;
|
|
|
|
|
top: 42px;
|
|
|
|
|
}
|
|
|
|
|
.duckmage-redo-btn-map {
|
|
|
|
|
left: 44px;
|
|
|
|
|
top: 42px;
|
|
|
|
|
}
|
|
|
|
|
.duckmage-undo-btn-map:disabled {
|
|
|
|
|
opacity: 0.3;
|
|
|
|
|
cursor: default;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-table-btn:hover,
|
|
|
|
|
.duckmage-rt-btn:hover,
|
|
|
|
|
.duckmage-undo-btn-map:not(:disabled):hover {
|
|
|
|
|
opacity: 1;
|
|
|
|
|
color: var(--text-normal);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Text note sections */
|
|
|
|
|
.duckmage-editor-text-section {
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
gap: 0.25em;
|
|
|
|
|
margin-bottom: 0.85em;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-text-section-label {
|
|
|
|
|
font-size: 0.85em;
|
|
|
|
|
font-weight: 600;
|
|
|
|
|
color: var(--text-muted);
|
|
|
|
|
text-transform: uppercase;
|
|
|
|
|
letter-spacing: 0.04em;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-text-section-textarea {
|
|
|
|
|
width: 100%;
|
|
|
|
|
resize: vertical;
|
|
|
|
|
font-size: 0.9em;
|
|
|
|
|
font-family: var(--font-text);
|
|
|
|
|
background: var(--background-secondary);
|
|
|
|
|
border: 1px solid var(--background-modifier-border);
|
|
|
|
|
border-radius: 4px;
|
|
|
|
|
padding: 0.4em 0.5em;
|
|
|
|
|
color: var(--text-normal);
|
|
|
|
|
line-height: 1.5;
|
|
|
|
|
box-sizing: border-box;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-text-section-textarea:focus {
|
|
|
|
|
outline: none;
|
|
|
|
|
border-color: var(--interactive-accent);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* ── Icon picker modal ────────────────────────────────────────────────────── */
|
|
|
|
|
|
|
|
|
|
.duckmage-icon-picker {
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-wrap: wrap;
|
|
|
|
|
gap: 0.4em;
|
|
|
|
|
margin-bottom: 0.75em;
|
|
|
|
|
max-height: 420px;
|
|
|
|
|
overflow-y: auto;
|
|
|
|
|
padding-right: 2px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-icon-option {
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
align-items: center;
|
|
|
|
|
gap: 0.25em;
|
|
|
|
|
padding: 0.35em;
|
|
|
|
|
border-radius: 6px;
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
border: 2px solid transparent;
|
|
|
|
|
background: var(--background-secondary);
|
|
|
|
|
transition:
|
|
|
|
|
border-color 0.1s ease,
|
|
|
|
|
background 0.1s ease;
|
|
|
|
|
min-width: 52px;
|
2026-05-15 19:06:16 +00:00
|
|
|
position: relative;
|
2026-04-04 15:51:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-icon-option:hover {
|
|
|
|
|
background: var(--background-modifier-hover);
|
|
|
|
|
border-color: var(--interactive-accent);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-icon-preview {
|
|
|
|
|
width: 36px;
|
|
|
|
|
height: 36px;
|
|
|
|
|
border-radius: 4px;
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
justify-content: center;
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
flex-shrink: 0;
|
|
|
|
|
background: var(--background-primary-alt);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-icon-preview-clear {
|
|
|
|
|
background: repeating-linear-gradient(
|
|
|
|
|
45deg,
|
|
|
|
|
var(--background-modifier-border) 0px,
|
|
|
|
|
var(--background-modifier-border) 2px,
|
|
|
|
|
var(--background-secondary) 2px,
|
|
|
|
|
var(--background-secondary) 8px
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-icon-preview-img {
|
|
|
|
|
width: 100%;
|
|
|
|
|
height: 100%;
|
|
|
|
|
object-fit: contain;
|
|
|
|
|
opacity: 0.85;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-icon-option-name {
|
|
|
|
|
font-size: 0.7em;
|
|
|
|
|
text-align: center;
|
|
|
|
|
color: var(--text-muted);
|
|
|
|
|
white-space: nowrap;
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
text-overflow: ellipsis;
|
|
|
|
|
max-width: 54px;
|
|
|
|
|
}
|
|
|
|
|
|
add token layer: place, edit, and inspect tokens on the hex map
- Tokens placed per-hex link to vault notes; support circle/square shape,
sm/md/lg size, fill color, optional border color, icon, and description
- TokenInfoModal shows note content (formatted via MarkdownRenderer, images
included) with jump-to-hex, open-note, edit, and two-step remove actions
- Edit flow reopens TokenModal pre-filled; proxy notes handle multiple tokens
referencing the same source note
- Icon picker replaced with scrollable palette grid (2-row cap, drag-to-reorder,
hide/show) in both HexEditorModal and TokenModal; order persisted in settings
- Token layer toggle added to layers menu (default on)
- Fix metadata-cache race on token delete (wasToken check prevents missed updates)
- Token preview rendered inline in TokenModal title row; real-time color preview
via native input event on color pickers
- Bump version to 1.1.6
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-22 12:26:20 +00:00
|
|
|
/* Selected state */
|
|
|
|
|
.duckmage-icon-option.is-selected {
|
|
|
|
|
border-color: var(--interactive-accent);
|
|
|
|
|
background: var(--background-modifier-hover);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Inline variant used inside HexEditorModal / TokenModal (2-row, scrollable) */
|
|
|
|
|
.duckmage-icon-picker-inline {
|
|
|
|
|
max-height: 132px;
|
|
|
|
|
margin-bottom: 0.5em;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Token modal title row with inline preview */
|
|
|
|
|
.duckmage-token-modal-title-row {
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
gap: 10px;
|
|
|
|
|
margin-bottom: 4px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-token-modal-title-row h2 {
|
|
|
|
|
margin: 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-icon-inline-label {
|
|
|
|
|
font-size: 0.8em;
|
|
|
|
|
color: var(--text-muted);
|
|
|
|
|
margin: 8px 0 3px;
|
|
|
|
|
font-weight: 600;
|
|
|
|
|
text-transform: uppercase;
|
|
|
|
|
letter-spacing: 0.04em;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Drag state for icon tiles in manage mode */
|
|
|
|
|
.duckmage-icon-option.duckmage-palette-dragging {
|
|
|
|
|
opacity: 0.4;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-icon-option.duckmage-palette-drop-target {
|
|
|
|
|
border-color: var(--interactive-accent);
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-11 16:07:34 +00:00
|
|
|
/* ── Icon picker: header + manage mode ───────────────────────────────────── */
|
|
|
|
|
|
|
|
|
|
.duckmage-icon-picker-header {
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
justify-content: space-between;
|
|
|
|
|
gap: 8px;
|
|
|
|
|
margin-bottom: 6px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-icon-picker-header h2 {
|
|
|
|
|
margin: 0;
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-10 20:22:38 +00:00
|
|
|
.duckmage-icon-picker-header-right {
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
align-items: flex-end;
|
|
|
|
|
gap: 4px;
|
2026-04-11 16:07:34 +00:00
|
|
|
flex-shrink: 0;
|
2026-05-10 20:22:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-icon-gm-toggle-row {
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
gap: 5px;
|
|
|
|
|
font-size: 0.82em;
|
|
|
|
|
color: var(--text-muted);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-icon-gm-toggle-label {
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
user-select: none;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-icon-manage-btn {
|
2026-04-11 16:07:34 +00:00
|
|
|
padding: 3px 10px;
|
|
|
|
|
font-size: 0.8em;
|
|
|
|
|
border-radius: 4px;
|
|
|
|
|
border: 1px solid var(--background-modifier-border);
|
|
|
|
|
background: var(--background-secondary);
|
|
|
|
|
color: var(--text-muted);
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-icon-manage-btn:hover {
|
|
|
|
|
border-color: var(--interactive-accent);
|
|
|
|
|
color: var(--text-normal);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Hide button — appears on hover over each icon tile in manage mode */
|
|
|
|
|
.duckmage-icon-hide-btn {
|
|
|
|
|
position: absolute;
|
|
|
|
|
top: 2px;
|
|
|
|
|
right: 2px;
|
|
|
|
|
width: 20px;
|
|
|
|
|
height: 20px;
|
|
|
|
|
padding: 0;
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
justify-content: center;
|
|
|
|
|
border-radius: 4px;
|
|
|
|
|
background: var(--background-secondary);
|
|
|
|
|
border: 1px solid var(--background-modifier-border);
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
opacity: 0;
|
|
|
|
|
transition: opacity 0.1s;
|
|
|
|
|
z-index: 2;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-icon-hide-btn svg {
|
|
|
|
|
width: 12px;
|
|
|
|
|
height: 12px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-icon-option:hover .duckmage-icon-hide-btn {
|
|
|
|
|
opacity: 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-icon-hidden .duckmage-icon-hide-btn {
|
|
|
|
|
opacity: 0.7;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Dim hidden icons in manage mode */
|
|
|
|
|
.duckmage-icon-hidden {
|
|
|
|
|
opacity: 0.35;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* ── Icon picker: add section ────────────────────────────────────────────── */
|
|
|
|
|
|
|
|
|
|
.duckmage-icon-add-section {
|
|
|
|
|
margin-bottom: 12px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-icon-drop-zone {
|
|
|
|
|
border: 2px dashed var(--background-modifier-border);
|
|
|
|
|
border-radius: 6px;
|
|
|
|
|
padding: 16px 12px;
|
|
|
|
|
text-align: center;
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
color: var(--text-muted);
|
|
|
|
|
font-size: 0.88em;
|
|
|
|
|
transition: border-color 0.15s ease, background 0.15s ease;
|
|
|
|
|
user-select: none;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-icon-drop-zone:hover,
|
|
|
|
|
.duckmage-icon-drop-zone.is-dragover {
|
|
|
|
|
border-color: var(--interactive-accent);
|
|
|
|
|
background: var(--background-modifier-hover);
|
|
|
|
|
color: var(--text-normal);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-icon-add-status {
|
|
|
|
|
font-size: 0.8em;
|
|
|
|
|
color: var(--text-muted);
|
|
|
|
|
margin: 4px 0 0;
|
|
|
|
|
min-height: 1.2em;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-icon-add-warning {
|
|
|
|
|
font-size: 0.85em;
|
|
|
|
|
color: var(--color-orange);
|
|
|
|
|
margin: 0;
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-04 15:51:53 +00:00
|
|
|
.duckmage-icon-btn-preview {
|
|
|
|
|
display: none;
|
|
|
|
|
width: 14px;
|
|
|
|
|
height: 14px;
|
|
|
|
|
object-fit: contain;
|
|
|
|
|
flex-shrink: 0;
|
|
|
|
|
opacity: 0.9;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* ── Modal: icon override ─────────────────────────────────────────────────── */
|
|
|
|
|
|
|
|
|
|
.duckmage-icon-override-row {
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
gap: 0.5em;
|
|
|
|
|
margin-top: 0.75em;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-icon-override-label {
|
|
|
|
|
color: var(--text-muted);
|
|
|
|
|
font-size: var(--font-ui-small);
|
|
|
|
|
white-space: nowrap;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* ── Settings: terrain palette ────────────────────────────────────────────── */
|
|
|
|
|
|
|
|
|
|
.duckmage-palette-list .duckmage-palette-item {
|
|
|
|
|
margin-bottom: 0.5em;
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
gap: 0.25em;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-palette-drag-handle {
|
|
|
|
|
cursor: grab;
|
|
|
|
|
color: var(--text-muted);
|
|
|
|
|
font-size: 1.2em;
|
|
|
|
|
line-height: 1;
|
|
|
|
|
user-select: none;
|
|
|
|
|
flex-shrink: 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-palette-drag-handle:active {
|
|
|
|
|
cursor: grabbing;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-palette-item.duckmage-palette-dragging {
|
|
|
|
|
opacity: 0.4;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-palette-item.duckmage-palette-drop-target {
|
|
|
|
|
outline: 2px dashed var(--interactive-accent);
|
|
|
|
|
border-radius: 4px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* ── Hex table view ───────────────────────────────────────────────────────── */
|
|
|
|
|
|
|
|
|
|
.duckmage-hex-table-container {
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
height: 100%;
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-hex-table-toolbar {
|
|
|
|
|
flex-shrink: 0;
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
flex-wrap: wrap;
|
|
|
|
|
gap: 4px 6px;
|
|
|
|
|
padding: 6px 8px;
|
|
|
|
|
border-bottom: 1px solid var(--background-modifier-border);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-filter-separator {
|
|
|
|
|
width: 1px;
|
|
|
|
|
height: 18px;
|
|
|
|
|
background: var(--background-modifier-border);
|
|
|
|
|
flex-shrink: 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-filter-label {
|
|
|
|
|
font-size: 0.8em;
|
|
|
|
|
color: var(--text-muted);
|
|
|
|
|
white-space: nowrap;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-filter-range-input {
|
|
|
|
|
height: 24px;
|
|
|
|
|
width: 36px;
|
|
|
|
|
padding: 0 4px;
|
|
|
|
|
border-radius: 4px;
|
|
|
|
|
background: var(--background-secondary);
|
|
|
|
|
border: 1px solid var(--background-modifier-border);
|
|
|
|
|
color: var(--text-normal);
|
|
|
|
|
font-size: 0.5em;
|
|
|
|
|
text-align: center;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-filter-range-input:focus {
|
|
|
|
|
outline: none;
|
|
|
|
|
border-color: var(--interactive-accent);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Hide browser spin buttons on the range inputs */
|
|
|
|
|
.duckmage-filter-range-input::-webkit-inner-spin-button,
|
|
|
|
|
.duckmage-filter-range-input::-webkit-outer-spin-button {
|
|
|
|
|
-webkit-appearance: none;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-filter-btn {
|
|
|
|
|
height: 24px;
|
|
|
|
|
padding: 0 8px;
|
|
|
|
|
border-radius: 4px;
|
|
|
|
|
background: var(--background-secondary);
|
|
|
|
|
border: 1px solid var(--background-modifier-border);
|
|
|
|
|
color: var(--text-muted);
|
|
|
|
|
font-size: 0.8em;
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
white-space: nowrap;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-filter-btn:hover {
|
|
|
|
|
color: var(--text-normal);
|
|
|
|
|
border-color: var(--background-modifier-border-hover);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-filter-btn.duckmage-filter-active {
|
|
|
|
|
border-color: var(--interactive-accent);
|
|
|
|
|
color: var(--interactive-accent);
|
|
|
|
|
background: var(--background-modifier-hover);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-filter-check-label {
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
gap: 4px;
|
|
|
|
|
font-size: 0.8em;
|
|
|
|
|
color: var(--text-muted);
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
white-space: nowrap;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-filter-check-label:hover {
|
|
|
|
|
color: var(--text-normal);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-hex-table-scroll {
|
|
|
|
|
flex: 1;
|
|
|
|
|
overflow: auto;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-hex-table {
|
|
|
|
|
border-collapse: collapse;
|
|
|
|
|
table-layout: fixed;
|
|
|
|
|
font-size: 0.85em;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-hex-table th {
|
|
|
|
|
position: sticky;
|
|
|
|
|
top: 0;
|
|
|
|
|
background: var(--background-secondary);
|
|
|
|
|
border: 1px solid var(--background-modifier-border);
|
|
|
|
|
padding: 4px 8px;
|
|
|
|
|
text-align: left;
|
|
|
|
|
white-space: nowrap;
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
text-overflow: ellipsis;
|
|
|
|
|
min-width: 0;
|
|
|
|
|
z-index: 2;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-hex-table td {
|
|
|
|
|
border: 1px solid var(--background-modifier-border);
|
|
|
|
|
padding: 3px 8px;
|
|
|
|
|
vertical-align: top;
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
white-space: nowrap;
|
|
|
|
|
text-overflow: ellipsis;
|
|
|
|
|
color: var(--text-normal);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-col-resizer {
|
|
|
|
|
position: absolute;
|
|
|
|
|
right: 0;
|
|
|
|
|
top: 0;
|
|
|
|
|
bottom: 0;
|
|
|
|
|
width: 5px;
|
|
|
|
|
cursor: col-resize;
|
|
|
|
|
z-index: 3;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-col-resizer:hover,
|
|
|
|
|
.duckmage-col-resizer:active {
|
|
|
|
|
background: var(--interactive-accent);
|
|
|
|
|
opacity: 0.5;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-hex-table tr:hover td {
|
|
|
|
|
background: var(--background-modifier-hover);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-hex-table td:first-child {
|
|
|
|
|
min-width: 64px;
|
|
|
|
|
white-space: nowrap;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-hex-table-coords {
|
|
|
|
|
color: var(--text-accent);
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
font-weight: 600;
|
|
|
|
|
white-space: nowrap;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-hex-table-coords:hover {
|
|
|
|
|
text-decoration: underline;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-hex-table-jump-btn {
|
|
|
|
|
margin-right: 3px;
|
|
|
|
|
padding: 0;
|
|
|
|
|
width: 12px;
|
|
|
|
|
height: 12px;
|
|
|
|
|
font-size: 0.65em;
|
|
|
|
|
line-height: 12px;
|
|
|
|
|
background: none;
|
|
|
|
|
border: none;
|
|
|
|
|
color: var(--text-faint);
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
opacity: 0.5;
|
|
|
|
|
vertical-align: middle;
|
|
|
|
|
display: inline-flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
justify-content: center;
|
|
|
|
|
}
|
|
|
|
|
.duckmage-hex-table-jump-btn:hover {
|
|
|
|
|
opacity: 1;
|
|
|
|
|
color: var(--text-accent);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-hex-table-swatch {
|
|
|
|
|
display: inline-block;
|
|
|
|
|
width: 10px;
|
|
|
|
|
height: 10px;
|
|
|
|
|
border-radius: 2px;
|
|
|
|
|
margin-right: 4px;
|
|
|
|
|
vertical-align: middle;
|
|
|
|
|
flex-shrink: 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-hex-table-empty {
|
|
|
|
|
color: var(--text-faint);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-hex-table-cell-clickable {
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-hex-table-cell-clickable:hover {
|
|
|
|
|
color: var(--text-accent);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-cell-modal-btn-row {
|
|
|
|
|
display: flex;
|
|
|
|
|
gap: 8px;
|
|
|
|
|
margin-top: 8px;
|
|
|
|
|
align-items: center;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-cell-modal-roll-btn {
|
|
|
|
|
flex: 0 0 auto;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* ── Hex cell detail modal ────────────────────────────────────────────────── */
|
|
|
|
|
|
|
|
|
|
.duckmage-cell-modal {
|
|
|
|
|
max-height: 60vh;
|
|
|
|
|
overflow-y: auto;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-cell-modal-pre {
|
|
|
|
|
white-space: pre-wrap;
|
|
|
|
|
word-break: break-word;
|
|
|
|
|
font-family: var(--font-text);
|
|
|
|
|
font-size: var(--font-ui-medium);
|
|
|
|
|
line-height: 1.6;
|
|
|
|
|
margin: 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-cell-modal-list {
|
|
|
|
|
margin: 0;
|
|
|
|
|
padding-left: 1.5em;
|
|
|
|
|
line-height: 1.8;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-cell-modal-textarea {
|
|
|
|
|
width: 100%;
|
|
|
|
|
min-height: 200px;
|
|
|
|
|
resize: vertical;
|
|
|
|
|
font-family: var(--font-text);
|
|
|
|
|
font-size: var(--font-ui-medium);
|
|
|
|
|
line-height: 1.6;
|
|
|
|
|
background: var(--background-secondary);
|
|
|
|
|
border: 1px solid var(--background-modifier-border);
|
|
|
|
|
border-radius: 4px;
|
|
|
|
|
padding: 0.5em;
|
|
|
|
|
color: var(--text-normal);
|
|
|
|
|
box-sizing: border-box;
|
|
|
|
|
display: block;
|
|
|
|
|
margin-bottom: 0.75em;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-cell-modal-textarea:focus {
|
|
|
|
|
outline: none;
|
|
|
|
|
border-color: var(--interactive-accent);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* ── Terrain filter modal ─────────────────────────────────────────────────── */
|
|
|
|
|
|
|
|
|
|
.duckmage-terrain-filter-list {
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
gap: 6px;
|
|
|
|
|
margin-bottom: 1em;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-terrain-filter-row {
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
gap: 6px;
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-terrain-filter-row input[type="checkbox"] {
|
|
|
|
|
flex-shrink: 0;
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-terrain-filter-hint {
|
|
|
|
|
font-size: 0.8em;
|
|
|
|
|
color: var(--text-faint);
|
|
|
|
|
margin: 0 0 8px 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-terrain-filter-excluded {
|
|
|
|
|
opacity: 0.5;
|
|
|
|
|
text-decoration: line-through;
|
|
|
|
|
color: var(--text-error);
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-15 19:06:16 +00:00
|
|
|
tr.duckmage-row-hidden {
|
|
|
|
|
display: none;
|
2026-04-04 15:51:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-terrain-filter-category-heading {
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
gap: 6px;
|
|
|
|
|
padding: 3px 4px;
|
|
|
|
|
margin-top: 4px;
|
|
|
|
|
font-weight: 600;
|
|
|
|
|
font-size: 0.85em;
|
|
|
|
|
text-transform: uppercase;
|
|
|
|
|
letter-spacing: 0.04em;
|
|
|
|
|
color: var(--text-muted);
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
border-radius: 4px;
|
|
|
|
|
user-select: none;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-terrain-filter-category-heading:hover {
|
|
|
|
|
background: var(--background-modifier-hover);
|
|
|
|
|
color: var(--text-normal);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-terrain-filter-row-indented {
|
|
|
|
|
padding-left: 14px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-terrain-filter-btns {
|
|
|
|
|
display: flex;
|
|
|
|
|
justify-content: flex-end;
|
|
|
|
|
gap: 8px;
|
|
|
|
|
padding-top: 4px;
|
|
|
|
|
border-top: 1px solid var(--background-modifier-border);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* ── Link picker modal (Towns / Dungeons) ───────────────────────────────────── */
|
|
|
|
|
|
|
|
|
|
.duckmage-link-picker-modal {
|
|
|
|
|
min-width: 320px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-link-picker-heading {
|
|
|
|
|
font-size: 0.8em;
|
|
|
|
|
font-weight: 600;
|
|
|
|
|
color: var(--text-muted);
|
|
|
|
|
margin: 0.75em 0 0.25em;
|
|
|
|
|
text-transform: uppercase;
|
|
|
|
|
letter-spacing: 0.04em;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-link-picker-list {
|
|
|
|
|
list-style: none;
|
|
|
|
|
margin: 0 0 0.5em;
|
|
|
|
|
padding: 0;
|
|
|
|
|
max-height: 220px;
|
|
|
|
|
overflow-y: auto;
|
|
|
|
|
border: 1px solid var(--background-modifier-border);
|
|
|
|
|
border-radius: 4px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-link-picker-item {
|
|
|
|
|
padding: 5px 10px;
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
border-radius: 3px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-link-picker-item:hover {
|
|
|
|
|
background: var(--background-modifier-hover);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-link-picker-create-row {
|
|
|
|
|
display: flex;
|
|
|
|
|
gap: 6px;
|
|
|
|
|
margin-top: 0.25em;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-link-picker-input {
|
|
|
|
|
flex: 1;
|
|
|
|
|
padding: 4px 8px;
|
|
|
|
|
border: 1px solid var(--background-modifier-border);
|
|
|
|
|
border-radius: 4px;
|
|
|
|
|
background: var(--background-secondary);
|
|
|
|
|
color: var(--text-normal);
|
|
|
|
|
font-size: var(--font-ui-small);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* ── Context menu create-new row ────────────────────────────────────────────── */
|
|
|
|
|
|
|
|
|
|
/* ? help button — bottom-left of the map controls overlay */
|
|
|
|
|
.duckmage-help-btn {
|
|
|
|
|
position: absolute;
|
|
|
|
|
bottom: 42px;
|
|
|
|
|
left: 8px;
|
|
|
|
|
z-index: 10;
|
|
|
|
|
width: 26px;
|
|
|
|
|
height: 26px;
|
|
|
|
|
border-radius: 50%;
|
|
|
|
|
background: var(--background-secondary);
|
|
|
|
|
border: 1px solid var(--background-modifier-border);
|
|
|
|
|
color: var(--text-muted);
|
|
|
|
|
font-size: 0.85em;
|
|
|
|
|
font-weight: 700;
|
|
|
|
|
line-height: 1;
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
opacity: 0.5;
|
|
|
|
|
pointer-events: auto;
|
|
|
|
|
transition:
|
|
|
|
|
opacity 0.15s ease,
|
|
|
|
|
border-color 0.15s ease,
|
|
|
|
|
color 0.15s ease;
|
|
|
|
|
user-select: none;
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
justify-content: center;
|
|
|
|
|
}
|
|
|
|
|
.duckmage-help-btn:hover {
|
|
|
|
|
opacity: 1;
|
|
|
|
|
border-color: var(--interactive-accent);
|
|
|
|
|
color: var(--interactive-accent);
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-10 20:22:38 +00:00
|
|
|
/* GM icon SVG overlay — small badge rendered in top-right quadrant of hex */
|
|
|
|
|
.duckmage-svg-gm-icon {
|
|
|
|
|
opacity: 0.9;
|
|
|
|
|
}
|
|
|
|
|
|
feat: multi-GM-icon support per hex (closes hexmaker#28)
A hex can now carry multiple GM icons. The HexEditorModal picker uses
left-click to add and right-click to remove individual icons, with a
"×N" badge on each tile when an icon is stacked. On the map, the icons
lay out in a hex-subgrid arrangement (reusing the same
tokenGroupOffsets helper the token layer uses, so multi-icon layout
stays visually consistent across the two systems).
Storage:
- New frontmatter key `gm-icons: [name, name, ...]` (array, duplicates
preserved for count).
- Legacy `gm-icon: name` (singular) still READ for back-compat —
existing notes Just Work. Writers always emit the new array key AND
clean up the legacy singular at the same time, so a note touched by
the new model standardises on the new shape.
Helpers (src/frontmatter.ts):
- New `getGmIconsFromFile(app, path): string[]` — authoritative reader.
- New `setGmIconsInFile(app, path, icons: string[])` — list writer.
- `getGmIconFromFile` / `setGmIconInFile` kept as thin compat wrappers
on top of the list helpers so the on-map painter and any future
caller that wants single-set semantics still has them.
HexEditorModal:
- `renderGmIconCountGrid` replaces the prior single-pick grid for GM
icons. Left-click increments, right-click decrements (clamped at 0),
the "— clear all —" tile wipes the list. Each tile shows ✓ for 1 and
×N for 2+. Writes the full list to frontmatter via `setGmIconsInFile`.
- Authoritative read via `getGmIconsFromFile`, keeping `directGmIcon`
populated as the first entry for the rest of the modal code that
still uses the singular field.
HexMapView:
- `data-gm-icon` (single) becomes `data-gm-icons` (JSON-encoded array)
on each hex element. New `parseGmIconsDataset` helper decodes it.
- `renderPathOverlay` GM-icon section: iterates unique icons in
first-seen order; positions each via `tokenGroupOffsets(n)` so they
cluster nicely in a sub-hex pattern. Icon size shrinks with slot
count (38%→22%) to keep them inside the hex. "×N" SVG text badge in
the lower-right when count > 1.
- Painter (paintIconGmOnly mode) still SETs to a single-icon list —
same overwriting behavior as before; multi-add is editor-only.
Painter undo records the FIRST prior icon (acceptable fidelity loss
for the painter's single-set semantics).
Styles:
- New `.duckmage-svg-gm-icon-count` for the on-map ×N badge (white
fill, stroked black for legibility over any terrain).
- New `.duckmage-icon-count-badge` for the picker tile badge.
Validation: 351 unit tests + 18 e2e pass. Manual UX test pending.
2026-06-21 14:27:12 +00:00
|
|
|
/* Multi-GM-icon count badge (hexmaker#28). Sits at the lower-right
|
|
|
|
|
of a stacked icon when the same icon was added 2+ times. */
|
|
|
|
|
.duckmage-svg-gm-icon-count {
|
|
|
|
|
font-family: system-ui, sans-serif;
|
|
|
|
|
font-weight: 700;
|
|
|
|
|
font-size: 11px;
|
|
|
|
|
fill: #fff;
|
|
|
|
|
stroke: rgba(0, 0, 0, 0.85);
|
|
|
|
|
stroke-width: 3;
|
|
|
|
|
paint-order: stroke fill;
|
|
|
|
|
pointer-events: none;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Picker tile count badge — small chip in the top-right of an icon tile
|
|
|
|
|
in the GM icon picker (HexEditorModal multi-add UX). */
|
|
|
|
|
.duckmage-icon-count-badge {
|
|
|
|
|
position: absolute;
|
|
|
|
|
top: 2px;
|
|
|
|
|
right: 2px;
|
|
|
|
|
min-width: 18px;
|
|
|
|
|
height: 18px;
|
|
|
|
|
padding: 0 4px;
|
|
|
|
|
border-radius: 9px;
|
|
|
|
|
background: var(--interactive-accent);
|
|
|
|
|
color: var(--text-on-accent);
|
|
|
|
|
font-size: 0.7em;
|
|
|
|
|
font-weight: 700;
|
|
|
|
|
line-height: 18px;
|
|
|
|
|
text-align: center;
|
|
|
|
|
pointer-events: none;
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-04 15:51:53 +00:00
|
|
|
/* Help modal content */
|
|
|
|
|
.duckmage-help-modal {
|
|
|
|
|
max-width: 560px;
|
|
|
|
|
}
|
|
|
|
|
.duckmage-help-modal h2 {
|
|
|
|
|
margin-top: 0;
|
|
|
|
|
}
|
|
|
|
|
.duckmage-help-modal table {
|
|
|
|
|
width: 100%;
|
|
|
|
|
border-collapse: collapse;
|
|
|
|
|
margin-bottom: 1em;
|
|
|
|
|
}
|
|
|
|
|
.duckmage-help-modal th,
|
|
|
|
|
.duckmage-help-modal td {
|
|
|
|
|
border: 1px solid var(--background-modifier-border);
|
|
|
|
|
padding: 4px 10px;
|
|
|
|
|
text-align: left;
|
|
|
|
|
}
|
|
|
|
|
.duckmage-help-modal th {
|
|
|
|
|
background: var(--background-secondary);
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-25 16:49:41 +00:00
|
|
|
.duckmage-table-picker-topbar {
|
|
|
|
|
display: flex;
|
|
|
|
|
margin-bottom: 10px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-table-picker-view-btn {
|
|
|
|
|
font-size: var(--font-ui-small);
|
|
|
|
|
padding: 3px 8px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-table-picker-combo {
|
|
|
|
|
margin-bottom: 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-table-picker-modal .duckmage-link-combo-dropdown {
|
|
|
|
|
max-height: 280px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-submap-picker-modal .duckmage-link-combo-dropdown {
|
|
|
|
|
max-height: 110px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.modal.duckmage-submap-picker-modal {
|
|
|
|
|
min-height: 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-submap-current-row {
|
|
|
|
|
font-size: 0.9em;
|
|
|
|
|
color: var(--text-muted);
|
|
|
|
|
margin-bottom: 0.5em;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-submap-current-value {
|
|
|
|
|
color: var(--text-normal);
|
|
|
|
|
font-weight: 500;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-submap-create-row {
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
gap: 0.5em;
|
|
|
|
|
margin-bottom: 0.4em;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-submap-create-label {
|
|
|
|
|
min-width: 56px;
|
|
|
|
|
font-size: 0.85em;
|
|
|
|
|
color: var(--text-muted);
|
|
|
|
|
flex-shrink: 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-submap-create-name {
|
|
|
|
|
flex: 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-submap-create-sep {
|
|
|
|
|
color: var(--text-muted);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-submap-create-palette {
|
|
|
|
|
flex: 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-submap-terrain-heading {
|
|
|
|
|
margin: 0.6em 0 0.3em;
|
|
|
|
|
font-size: 0.85em;
|
|
|
|
|
color: var(--text-muted);
|
|
|
|
|
font-weight: 500;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-submap-create-btn {
|
|
|
|
|
width: 100%;
|
|
|
|
|
margin-top: 0.5em;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-table-picker-new-heading {
|
|
|
|
|
margin: 16px 0 8px;
|
|
|
|
|
font-size: var(--font-ui-small);
|
|
|
|
|
color: var(--text-muted);
|
|
|
|
|
text-transform: uppercase;
|
|
|
|
|
letter-spacing: 0.04em;
|
|
|
|
|
font-weight: 600;
|
2026-04-04 15:51:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-goto-modal .duckmage-goto-row {
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
gap: 8px;
|
|
|
|
|
margin-bottom: 12px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-goto-input {
|
|
|
|
|
width: 70px;
|
|
|
|
|
padding: 4px 6px;
|
|
|
|
|
border: 1px solid var(--background-modifier-border);
|
|
|
|
|
border-radius: 4px;
|
|
|
|
|
background: var(--background-secondary);
|
|
|
|
|
color: var(--text-normal);
|
|
|
|
|
font-size: var(--font-ui-small);
|
|
|
|
|
text-align: center;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-goto-btn-confirm {
|
|
|
|
|
width: 100%;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* ── Random table view ──────────────────────────────────────────────────── */
|
|
|
|
|
|
|
|
|
|
.duckmage-rt-container {
|
|
|
|
|
display: flex;
|
|
|
|
|
height: 100%;
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-rt-left {
|
|
|
|
|
width: 220px;
|
|
|
|
|
flex-shrink: 0;
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
border-right: 1px solid var(--background-modifier-border);
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-rt-list-header {
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
justify-content: space-between;
|
|
|
|
|
padding: 8px 10px 4px;
|
|
|
|
|
border-bottom: 1px solid var(--background-modifier-border);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-rt-list-title {
|
|
|
|
|
font-weight: 600;
|
|
|
|
|
font-size: var(--font-ui-small);
|
|
|
|
|
color: var(--text-muted);
|
|
|
|
|
text-transform: uppercase;
|
|
|
|
|
letter-spacing: 0.04em;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-rt-icon-btn {
|
|
|
|
|
background: none;
|
|
|
|
|
border: none;
|
|
|
|
|
color: var(--text-muted);
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
padding: 2px 4px;
|
|
|
|
|
border-radius: 3px;
|
|
|
|
|
font-size: 1em;
|
|
|
|
|
}
|
|
|
|
|
.duckmage-rt-icon-btn:hover {
|
|
|
|
|
color: var(--text-normal);
|
|
|
|
|
background: var(--background-modifier-hover);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-rt-search {
|
|
|
|
|
width: 100%;
|
|
|
|
|
box-sizing: border-box;
|
|
|
|
|
padding: 4px 8px;
|
|
|
|
|
font-size: var(--font-ui-small);
|
|
|
|
|
border: none;
|
|
|
|
|
border-bottom: 1px solid var(--background-modifier-border);
|
|
|
|
|
background: var(--background-secondary);
|
|
|
|
|
color: var(--text-normal);
|
|
|
|
|
outline: none;
|
|
|
|
|
flex-shrink: 0;
|
|
|
|
|
}
|
|
|
|
|
.duckmage-rt-search:focus {
|
|
|
|
|
background: var(--background-modifier-hover);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-rt-list {
|
|
|
|
|
flex: 1;
|
|
|
|
|
overflow-y: auto;
|
|
|
|
|
padding: 4px 0;
|
|
|
|
|
}
|
2026-05-25 16:49:41 +00:00
|
|
|
.duckmage-rt-list.duckmage-picker-list {
|
|
|
|
|
flex: unset;
|
|
|
|
|
max-height: 180px;
|
|
|
|
|
border: 1px solid var(--background-modifier-border);
|
|
|
|
|
border-radius: 6px;
|
|
|
|
|
margin-bottom: 8px;
|
|
|
|
|
}
|
|
|
|
|
.duckmage-picker-list-empty {
|
|
|
|
|
padding: 8px 12px;
|
|
|
|
|
color: var(--text-muted);
|
|
|
|
|
font-size: var(--font-ui-small);
|
|
|
|
|
}
|
2026-04-04 15:51:53 +00:00
|
|
|
|
|
|
|
|
.duckmage-rt-list-item {
|
|
|
|
|
padding: 5px 12px;
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
font-size: var(--font-ui-small);
|
|
|
|
|
border-radius: 4px;
|
|
|
|
|
margin: 1px 4px;
|
|
|
|
|
white-space: nowrap;
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
text-overflow: ellipsis;
|
|
|
|
|
}
|
|
|
|
|
.duckmage-rt-list-item:hover {
|
|
|
|
|
background: var(--background-modifier-hover);
|
|
|
|
|
}
|
|
|
|
|
.duckmage-rt-list-item.is-active {
|
|
|
|
|
background: var(--background-modifier-active-hover);
|
|
|
|
|
color: var(--text-accent);
|
|
|
|
|
font-weight: 600;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-rt-folder {
|
|
|
|
|
margin: 1px 0;
|
|
|
|
|
}
|
|
|
|
|
.duckmage-rt-folder-header {
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
gap: 4px;
|
|
|
|
|
padding: 4px 8px;
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
border-radius: 4px;
|
|
|
|
|
margin: 0 4px;
|
|
|
|
|
font-size: var(--font-ui-small);
|
|
|
|
|
font-weight: 600;
|
|
|
|
|
color: var(--text-muted);
|
|
|
|
|
user-select: none;
|
|
|
|
|
}
|
|
|
|
|
.duckmage-rt-folder-header:hover {
|
|
|
|
|
background: var(--background-modifier-hover);
|
|
|
|
|
color: var(--text-normal);
|
|
|
|
|
}
|
|
|
|
|
.duckmage-rt-folder-arrow {
|
|
|
|
|
font-size: 0.65em;
|
|
|
|
|
flex-shrink: 0;
|
|
|
|
|
}
|
|
|
|
|
.duckmage-rt-folder-name {
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
text-overflow: ellipsis;
|
|
|
|
|
white-space: nowrap;
|
|
|
|
|
}
|
|
|
|
|
.duckmage-rt-folder-children {
|
|
|
|
|
padding-left: 12px;
|
|
|
|
|
}
|
|
|
|
|
.duckmage-rt-list-item.is-dragging {
|
|
|
|
|
opacity: 0.4;
|
|
|
|
|
cursor: grabbing;
|
|
|
|
|
}
|
|
|
|
|
.duckmage-rt-folder-header.is-drag-over {
|
|
|
|
|
background: var(--background-modifier-hover);
|
|
|
|
|
outline: 2px dashed var(--color-accent);
|
|
|
|
|
color: var(--text-normal);
|
|
|
|
|
}
|
|
|
|
|
.duckmage-rt-list.is-drag-over-root {
|
|
|
|
|
outline: 1px dashed var(--background-modifier-border-hover);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-rt-list-footer {
|
|
|
|
|
padding: 6px 8px;
|
|
|
|
|
border-top: 1px solid var(--background-modifier-border);
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
gap: 4px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-rt-new-row {
|
|
|
|
|
display: flex;
|
|
|
|
|
gap: 4px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-rt-new-input {
|
|
|
|
|
flex: 1;
|
|
|
|
|
min-width: 0;
|
|
|
|
|
padding: 3px 6px;
|
|
|
|
|
border: 1px solid var(--background-modifier-border);
|
|
|
|
|
border-radius: 4px;
|
|
|
|
|
background: var(--background-secondary);
|
|
|
|
|
color: var(--text-normal);
|
|
|
|
|
font-size: var(--font-ui-small);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-rt-new-btn {
|
|
|
|
|
padding: 3px 8px;
|
|
|
|
|
font-size: var(--font-ui-small);
|
|
|
|
|
white-space: nowrap;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-rt-detail {
|
|
|
|
|
flex: 1;
|
|
|
|
|
overflow-y: auto;
|
|
|
|
|
padding: 12px 16px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-rt-placeholder {
|
|
|
|
|
color: var(--text-faint);
|
|
|
|
|
font-style: italic;
|
|
|
|
|
margin-top: 24px;
|
|
|
|
|
text-align: center;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-rt-detail-header {
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
gap: 10px;
|
|
|
|
|
margin-bottom: 12px;
|
|
|
|
|
flex-wrap: wrap;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-rt-detail-title {
|
|
|
|
|
margin: 0;
|
|
|
|
|
flex: 1;
|
|
|
|
|
min-width: 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-rt-edit-link {
|
|
|
|
|
font-size: var(--font-ui-small);
|
|
|
|
|
color: var(--text-accent);
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
white-space: nowrap;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-rt-die-select {
|
|
|
|
|
font-size: var(--font-ui-small);
|
|
|
|
|
padding: 2px 4px;
|
|
|
|
|
border-radius: 4px;
|
|
|
|
|
border: 1px solid var(--background-modifier-border);
|
|
|
|
|
background: var(--background-secondary);
|
|
|
|
|
color: var(--text-normal);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* ── Random table description ───────────────────────────────────────────── */
|
|
|
|
|
|
|
|
|
|
.duckmage-rt-desc-section {
|
|
|
|
|
margin-bottom: 12px;
|
|
|
|
|
}
|
|
|
|
|
.duckmage-rt-desc-header {
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
margin-bottom: 4px;
|
|
|
|
|
}
|
|
|
|
|
.duckmage-rt-section-label {
|
|
|
|
|
font-size: 0.8em;
|
|
|
|
|
color: var(--text-muted);
|
|
|
|
|
text-transform: uppercase;
|
|
|
|
|
letter-spacing: 0.05em;
|
|
|
|
|
user-select: none;
|
|
|
|
|
}
|
|
|
|
|
.duckmage-rt-desc-body {
|
|
|
|
|
font-size: 0.9em;
|
|
|
|
|
color: var(--text-muted);
|
|
|
|
|
padding: 6px 8px;
|
|
|
|
|
border-left: 2px solid var(--background-modifier-border);
|
|
|
|
|
}
|
|
|
|
|
.duckmage-rt-desc-body p:first-child {
|
|
|
|
|
margin-top: 0;
|
|
|
|
|
}
|
|
|
|
|
.duckmage-rt-desc-body p:last-child {
|
|
|
|
|
margin-bottom: 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* ── Random table (odds table) ─────────────────────────────────────────── */
|
|
|
|
|
|
|
|
|
|
.duckmage-rt-table-section-header {
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
margin-bottom: 4px;
|
|
|
|
|
}
|
|
|
|
|
.duckmage-rt-collapse-btn {
|
|
|
|
|
background: none;
|
|
|
|
|
border: none;
|
|
|
|
|
padding: 0 4px;
|
|
|
|
|
font-size: 0.75em;
|
|
|
|
|
color: var(--text-muted);
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
line-height: 1;
|
|
|
|
|
}
|
|
|
|
|
.duckmage-rt-collapse-btn:hover {
|
|
|
|
|
color: var(--text-normal);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-random-table {
|
|
|
|
|
border-collapse: collapse;
|
|
|
|
|
width: 100%;
|
|
|
|
|
margin-bottom: 12px;
|
|
|
|
|
font-size: var(--font-ui-small);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-random-table th {
|
|
|
|
|
position: sticky;
|
|
|
|
|
top: 0;
|
|
|
|
|
background: var(--background-secondary);
|
|
|
|
|
border: 1px solid var(--background-modifier-border);
|
|
|
|
|
padding: 4px 8px;
|
|
|
|
|
text-align: left;
|
|
|
|
|
white-space: nowrap;
|
|
|
|
|
z-index: 2;
|
|
|
|
|
font-weight: 600;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-random-table td {
|
|
|
|
|
border: 1px solid var(--background-modifier-border);
|
|
|
|
|
padding: 4px 8px;
|
|
|
|
|
vertical-align: top;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-random-table tr.is-rolled td {
|
|
|
|
|
background: var(--background-modifier-hover);
|
|
|
|
|
font-weight: 600;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-rt-range-cell {
|
|
|
|
|
color: var(--text-faint);
|
|
|
|
|
white-space: nowrap;
|
|
|
|
|
font-variant-numeric: tabular-nums;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-rt-odds-cell {
|
|
|
|
|
color: var(--text-faint);
|
|
|
|
|
white-space: nowrap;
|
|
|
|
|
text-align: right;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* ── Roll area ─────────────────────────────────────────────────────────── */
|
|
|
|
|
|
|
|
|
|
.duckmage-rt-roll-area {
|
|
|
|
|
display: grid;
|
|
|
|
|
grid-template-columns: 1fr auto 1fr;
|
|
|
|
|
align-items: center;
|
|
|
|
|
margin-bottom: 8px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-rt-copy-link-btn {
|
|
|
|
|
justify-self: start;
|
|
|
|
|
font-size: 0.8em;
|
|
|
|
|
color: var(--text-muted);
|
|
|
|
|
background: none;
|
|
|
|
|
border: 1px solid var(--background-modifier-border);
|
|
|
|
|
border-radius: 4px;
|
|
|
|
|
padding: 2px 8px;
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
white-space: nowrap;
|
|
|
|
|
}
|
|
|
|
|
.duckmage-rt-copy-link-btn:hover {
|
|
|
|
|
color: var(--text-normal);
|
|
|
|
|
border-color: var(--interactive-accent);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-rt-roll-btn {
|
|
|
|
|
justify-self: center;
|
|
|
|
|
min-width: 80px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-roll-result {
|
|
|
|
|
border: 1px solid var(--background-modifier-border);
|
|
|
|
|
border-radius: 6px;
|
|
|
|
|
padding: 10px 12px;
|
|
|
|
|
margin-bottom: 10px;
|
|
|
|
|
background: var(--background-secondary);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-roll-result-textarea {
|
|
|
|
|
width: 100%;
|
|
|
|
|
min-height: 50px;
|
|
|
|
|
resize: vertical;
|
|
|
|
|
border: 1px solid var(--background-modifier-border);
|
|
|
|
|
border-radius: 4px;
|
|
|
|
|
background: var(--background-primary);
|
|
|
|
|
color: var(--text-normal);
|
|
|
|
|
padding: 6px 8px;
|
|
|
|
|
font-size: var(--font-ui-small);
|
|
|
|
|
box-sizing: border-box;
|
|
|
|
|
margin-bottom: 6px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-roll-result-btns {
|
|
|
|
|
display: flex;
|
|
|
|
|
gap: 6px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-roll-copy-btn {
|
|
|
|
|
font-size: var(--font-ui-small);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* ── Roll history ──────────────────────────────────────────────────────── */
|
|
|
|
|
|
|
|
|
|
.duckmage-rt-history {
|
|
|
|
|
margin-top: 8px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-rt-history-label {
|
|
|
|
|
font-size: var(--font-ui-small);
|
|
|
|
|
color: var(--text-faint);
|
|
|
|
|
margin: 0 0 4px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-rt-history-list {
|
|
|
|
|
list-style: none;
|
|
|
|
|
margin: 0;
|
|
|
|
|
padding: 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-rt-history-item {
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
gap: 6px;
|
|
|
|
|
font-size: var(--font-ui-small);
|
|
|
|
|
color: var(--text-muted);
|
|
|
|
|
padding: 1px 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-rt-history-copy {
|
|
|
|
|
background: none;
|
|
|
|
|
border: none;
|
|
|
|
|
color: var(--text-faint);
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
padding: 0 2px;
|
|
|
|
|
font-size: 0.9em;
|
|
|
|
|
opacity: 0.6;
|
|
|
|
|
}
|
|
|
|
|
.duckmage-rt-history-copy:hover {
|
|
|
|
|
opacity: 1;
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-11 16:07:34 +00:00
|
|
|
/* ── Embedded roller code block widget ─────────────────────────────────── */
|
|
|
|
|
|
|
|
|
|
.duckmage-roller-block {
|
|
|
|
|
border: 1px solid var(--background-modifier-border);
|
|
|
|
|
border-radius: 6px;
|
|
|
|
|
padding: 10px 12px;
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
gap: 8px;
|
|
|
|
|
max-width: 420px;
|
|
|
|
|
background: var(--background-secondary);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-roller-header {
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
justify-content: space-between;
|
|
|
|
|
gap: 8px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-roller-title {
|
|
|
|
|
font-weight: 600;
|
|
|
|
|
font-size: 0.9em;
|
|
|
|
|
color: var(--text-normal);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-roller-die {
|
|
|
|
|
font-size: 0.8em;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-roller-btn {
|
|
|
|
|
align-self: flex-start;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-roller-history {
|
|
|
|
|
max-height: 130px;
|
|
|
|
|
overflow-y: auto;
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
gap: 2px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-roller-history-item {
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
justify-content: space-between;
|
|
|
|
|
gap: 6px;
|
|
|
|
|
font-size: 0.8em;
|
|
|
|
|
color: var(--text-muted);
|
|
|
|
|
padding: 1px 2px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-roller-history-text {
|
|
|
|
|
flex: 1;
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
text-overflow: ellipsis;
|
|
|
|
|
white-space: nowrap;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-roller-history-copy {
|
|
|
|
|
background: none;
|
|
|
|
|
border: none;
|
|
|
|
|
color: var(--text-faint);
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
font-size: 0.9em;
|
|
|
|
|
padding: 0 2px;
|
|
|
|
|
flex-shrink: 0;
|
|
|
|
|
}
|
|
|
|
|
.duckmage-roller-history-copy:hover {
|
|
|
|
|
color: var(--text-normal);
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-04 15:51:53 +00:00
|
|
|
/* Per-entry copy button in the table odds grid */
|
2026-05-15 19:06:16 +00:00
|
|
|
.duckmage-random-table .duckmage-rt-copy-col-header {
|
2026-04-04 15:51:53 +00:00
|
|
|
width: 24px;
|
2026-05-15 19:06:16 +00:00
|
|
|
padding: 0;
|
2026-04-04 15:51:53 +00:00
|
|
|
}
|
|
|
|
|
|
2026-05-15 19:06:16 +00:00
|
|
|
.duckmage-random-table .duckmage-rt-entry-copy-cell {
|
2026-04-04 15:51:53 +00:00
|
|
|
width: 24px;
|
2026-05-15 19:06:16 +00:00
|
|
|
padding: 0 2px;
|
2026-04-04 15:51:53 +00:00
|
|
|
text-align: center;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-rt-entry-copy-btn {
|
|
|
|
|
background: none;
|
|
|
|
|
border: none;
|
|
|
|
|
color: var(--text-faint);
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
padding: 0 2px;
|
|
|
|
|
font-size: 0.9em;
|
|
|
|
|
opacity: 0;
|
|
|
|
|
transition: opacity 0.1s;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
tr:hover .duckmage-rt-entry-copy-btn {
|
|
|
|
|
opacity: 0.6;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
tr:hover .duckmage-rt-entry-copy-btn:hover {
|
|
|
|
|
opacity: 1;
|
|
|
|
|
color: var(--text-normal);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* ── Random table modal ────────────────────────────────────────────────── */
|
|
|
|
|
|
|
|
|
|
.duckmage-roll-modal {
|
|
|
|
|
min-width: 380px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-roll-modal-select {
|
|
|
|
|
width: 100%;
|
|
|
|
|
margin-bottom: 10px;
|
|
|
|
|
padding: 4px 6px;
|
|
|
|
|
border: 1px solid var(--background-modifier-border);
|
|
|
|
|
border-radius: 4px;
|
|
|
|
|
background: var(--background-secondary);
|
|
|
|
|
color: var(--text-normal);
|
|
|
|
|
font-size: var(--font-ui-small);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-roll-modal-table-wrap {
|
|
|
|
|
max-height: 200px;
|
|
|
|
|
overflow-y: auto;
|
|
|
|
|
margin-bottom: 10px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-wf-description {
|
|
|
|
|
color: var(--text-muted);
|
|
|
|
|
font-size: 0.9em;
|
|
|
|
|
margin-bottom: 12px;
|
|
|
|
|
white-space: pre-wrap;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-rt-empty {
|
|
|
|
|
color: var(--text-faint);
|
|
|
|
|
font-style: italic;
|
|
|
|
|
font-size: var(--font-ui-small);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* ── Section roll button (hex editor text sections) ────────────────────── */
|
|
|
|
|
|
|
|
|
|
.duckmage-text-section-label-row {
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
justify-content: space-between;
|
|
|
|
|
margin-bottom: 2px;
|
|
|
|
|
}
|
|
|
|
|
.duckmage-text-section-btn-group {
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
gap: 2px;
|
|
|
|
|
flex-shrink: 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-section-desc-table-btn {
|
|
|
|
|
background: none;
|
|
|
|
|
border: none;
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
font-size: 0.85em;
|
|
|
|
|
padding: 0 2px;
|
|
|
|
|
color: var(--text-muted);
|
|
|
|
|
opacity: 0.7;
|
|
|
|
|
line-height: 1;
|
|
|
|
|
}
|
|
|
|
|
.duckmage-section-desc-table-btn:hover {
|
|
|
|
|
opacity: 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-roll-modal-header-row {
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
gap: 8px;
|
|
|
|
|
margin-bottom: 8px;
|
|
|
|
|
}
|
|
|
|
|
.duckmage-roll-modal-edit-link,
|
|
|
|
|
.duckmage-roll-modal-open-link {
|
|
|
|
|
font-size: 0.8em;
|
|
|
|
|
color: var(--text-accent);
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
}
|
|
|
|
|
.duckmage-roll-modal-edit-link:hover,
|
|
|
|
|
.duckmage-roll-modal-open-link:hover {
|
|
|
|
|
text-decoration: underline;
|
|
|
|
|
}
|
|
|
|
|
.duckmage-roll-modal-open-link {
|
|
|
|
|
margin-left: auto;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* ── Random table editor modal ───────────────────────────────────────────── */
|
|
|
|
|
.duckmage-table-editor {
|
|
|
|
|
min-width: 420px;
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
flex: 1;
|
|
|
|
|
min-height: 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-table-editor-entries-section {
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
flex: 1;
|
|
|
|
|
min-height: 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-table-editor-entries-section .duckmage-table-editor-rows {
|
|
|
|
|
flex: 1;
|
|
|
|
|
overflow-y: auto;
|
|
|
|
|
min-height: 60px;
|
|
|
|
|
}
|
|
|
|
|
.duckmage-table-editor-entries-heading-row {
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
gap: 8px;
|
|
|
|
|
}
|
|
|
|
|
.duckmage-table-editor-entries-heading-row .duckmage-table-editor-heading {
|
|
|
|
|
flex: 1;
|
|
|
|
|
margin: 12px 0 4px;
|
|
|
|
|
}
|
|
|
|
|
.duckmage-table-editor-add-one-btn {
|
|
|
|
|
font-size: 0.78em;
|
|
|
|
|
padding: 2px 7px;
|
|
|
|
|
color: var(--text-muted);
|
|
|
|
|
background: transparent;
|
|
|
|
|
border: 1px solid var(--background-modifier-border);
|
|
|
|
|
border-radius: 4px;
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
white-space: nowrap;
|
|
|
|
|
margin-top: 2px;
|
|
|
|
|
margin-bottom: 0.5em;
|
|
|
|
|
}
|
|
|
|
|
.duckmage-table-editor-add-one-btn:hover {
|
|
|
|
|
color: var(--text-normal);
|
|
|
|
|
background: var(--background-modifier-hover);
|
|
|
|
|
}
|
|
|
|
|
.duckmage-table-editor-heading {
|
|
|
|
|
font-weight: 600;
|
|
|
|
|
font-size: 0.85em;
|
|
|
|
|
color: var(--text-muted);
|
|
|
|
|
text-transform: uppercase;
|
|
|
|
|
letter-spacing: 0.04em;
|
|
|
|
|
margin: 12px 0 4px;
|
|
|
|
|
}
|
|
|
|
|
.duckmage-table-editor-rows {
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
gap: 4px;
|
|
|
|
|
max-height: 320px;
|
|
|
|
|
overflow-y: auto;
|
|
|
|
|
margin-bottom: 4px;
|
|
|
|
|
}
|
|
|
|
|
.duckmage-table-editor-row,
|
|
|
|
|
.duckmage-table-editor-add-row {
|
|
|
|
|
display: flex;
|
|
|
|
|
gap: 6px;
|
|
|
|
|
align-items: flex-start;
|
|
|
|
|
}
|
|
|
|
|
.duckmage-table-editor-drag-handle {
|
|
|
|
|
cursor: grab;
|
|
|
|
|
color: var(--text-faint);
|
|
|
|
|
padding: 4px 2px;
|
|
|
|
|
flex-shrink: 0;
|
|
|
|
|
font-size: 1.1em;
|
|
|
|
|
line-height: 1;
|
|
|
|
|
user-select: none;
|
|
|
|
|
}
|
|
|
|
|
.duckmage-table-editor-drag-handle:hover {
|
|
|
|
|
color: var(--text-muted);
|
|
|
|
|
}
|
|
|
|
|
.duckmage-table-editor-dragging {
|
|
|
|
|
opacity: 0.4;
|
|
|
|
|
}
|
|
|
|
|
.duckmage-table-editor-drop-target {
|
|
|
|
|
outline: 2px solid var(--interactive-accent);
|
|
|
|
|
outline-offset: -2px;
|
|
|
|
|
border-radius: 4px;
|
|
|
|
|
}
|
|
|
|
|
.duckmage-table-editor-result {
|
|
|
|
|
flex: 1;
|
|
|
|
|
resize: vertical;
|
|
|
|
|
min-height: 28px;
|
|
|
|
|
overflow-y: hidden;
|
|
|
|
|
font-family: inherit;
|
|
|
|
|
font-size: inherit;
|
|
|
|
|
}
|
|
|
|
|
.duckmage-table-editor-weight {
|
|
|
|
|
width: 64px;
|
|
|
|
|
}
|
|
|
|
|
.duckmage-table-editor-del {
|
|
|
|
|
background: none;
|
|
|
|
|
border: none;
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
color: var(--text-muted);
|
|
|
|
|
font-size: 1.1em;
|
|
|
|
|
padding: 0 4px;
|
|
|
|
|
line-height: 1;
|
|
|
|
|
}
|
|
|
|
|
.duckmage-table-editor-del:hover {
|
|
|
|
|
color: var(--text-error);
|
|
|
|
|
}
|
|
|
|
|
.duckmage-table-editor-add-btn {
|
|
|
|
|
white-space: nowrap;
|
|
|
|
|
}
|
|
|
|
|
.duckmage-table-editor-add-error {
|
|
|
|
|
color: var(--text-error);
|
|
|
|
|
font-size: 0.85em;
|
|
|
|
|
margin-top: 4px;
|
|
|
|
|
}
|
|
|
|
|
.duckmage-table-editor-footer {
|
|
|
|
|
display: flex;
|
|
|
|
|
justify-content: flex-end;
|
|
|
|
|
margin-top: 14px;
|
|
|
|
|
padding-top: 10px;
|
|
|
|
|
border-top: 1px solid var(--background-modifier-border);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* ── Random table editor — name row ────────────────────────────────────────── */
|
|
|
|
|
|
|
|
|
|
.duckmage-table-editor-name-row {
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
gap: 8px;
|
|
|
|
|
margin-bottom: 12px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-table-editor-name-label {
|
|
|
|
|
font-size: 0.85em;
|
|
|
|
|
color: var(--text-muted);
|
|
|
|
|
white-space: nowrap;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-table-editor-name-input {
|
|
|
|
|
flex: 1;
|
|
|
|
|
font-size: 1em;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* ── Random table editor — filter section ──────────────────────────────────── */
|
|
|
|
|
|
|
|
|
|
.duckmage-table-editor-filter-section {
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
gap: 6px;
|
|
|
|
|
margin-bottom: 12px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-table-editor-filter-row {
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
gap: 8px;
|
|
|
|
|
font-size: 0.9em;
|
|
|
|
|
color: var(--text-muted);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-table-editor-filter-row input[type="checkbox"] {
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* ── Random table view — folder exclusion badges ───────────────────────────── */
|
|
|
|
|
|
|
|
|
|
.duckmage-rt-folder-filter-badges {
|
|
|
|
|
margin-left: auto;
|
|
|
|
|
display: flex;
|
|
|
|
|
gap: 4px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-rt-folder-badge {
|
|
|
|
|
font-size: 0.7em;
|
|
|
|
|
opacity: 0.6;
|
|
|
|
|
user-select: none;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* ── Random table editor — linked folder row ───────────────────────────────── */
|
|
|
|
|
|
|
|
|
|
.duckmage-table-editor-folder-row {
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
gap: 8px;
|
|
|
|
|
margin-bottom: 12px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-table-editor-folder-label {
|
|
|
|
|
font-size: 0.85em;
|
|
|
|
|
color: var(--text-muted);
|
|
|
|
|
white-space: nowrap;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-table-editor-folder-input {
|
|
|
|
|
flex: 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Description row in editor modal */
|
|
|
|
|
.duckmage-table-editor-desc-row {
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
gap: 4px;
|
|
|
|
|
margin-bottom: 12px;
|
|
|
|
|
}
|
|
|
|
|
.duckmage-table-editor-desc-label {
|
|
|
|
|
font-size: 0.85em;
|
|
|
|
|
color: var(--text-muted);
|
|
|
|
|
}
|
|
|
|
|
.duckmage-table-editor-desc-input {
|
|
|
|
|
width: 100%;
|
|
|
|
|
resize: vertical;
|
|
|
|
|
font-size: 0.9em;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* ── Random table view — clickable entry links ─────────────────────────────── */
|
|
|
|
|
|
|
|
|
|
.duckmage-rt-entry-link {
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
text-decoration: underline;
|
|
|
|
|
color: var(--link-color);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-rt-entry-link:hover {
|
|
|
|
|
color: var(--link-color-hover);
|
|
|
|
|
}
|
|
|
|
|
.duckmage-wf-step-formula {
|
|
|
|
|
font-family: var(--font-monospace);
|
|
|
|
|
font-size: 0.9em;
|
|
|
|
|
}
|
|
|
|
|
.duckmage-wf-step-alias {
|
|
|
|
|
color: var(--text-muted);
|
|
|
|
|
font-size: 0.9em;
|
|
|
|
|
}
|
|
|
|
|
.duckmage-wf-template-section {
|
|
|
|
|
margin-top: 20px;
|
|
|
|
|
}
|
|
|
|
|
.duckmage-wf-template-preview {
|
|
|
|
|
margin-top: 8px;
|
|
|
|
|
padding: 8px 10px;
|
|
|
|
|
border-radius: 6px;
|
|
|
|
|
background: var(--background-secondary);
|
|
|
|
|
font-size: var(--font-ui-small);
|
|
|
|
|
max-height: 240px;
|
|
|
|
|
overflow-y: auto;
|
|
|
|
|
}
|
|
|
|
|
.duckmage-wf-template-preview > *:first-child {
|
|
|
|
|
margin-top: 0;
|
|
|
|
|
}
|
|
|
|
|
.duckmage-wf-template-preview > *:last-child {
|
|
|
|
|
margin-bottom: 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* ── Random table view — from-folder input ─────────────────────────────────── */
|
|
|
|
|
|
|
|
|
|
.duckmage-rt-from-folder-input {
|
|
|
|
|
width: 100%;
|
|
|
|
|
font-size: 0.85em;
|
2026-06-06 03:04:11 +00:00
|
|
|
margin-top: 6px;
|
2026-04-04 15:51:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Confirm delete modal */
|
|
|
|
|
.duckmage-confirm-btn-row {
|
|
|
|
|
display: flex;
|
|
|
|
|
gap: 8px;
|
|
|
|
|
margin-top: 12px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* ── Region button & modal ────────────────────────────────────────────────── */
|
|
|
|
|
|
|
|
|
|
/* Region selector button — always-visible top-left area, after ⊞ and 🎲 */
|
2026-05-23 16:29:22 +00:00
|
|
|
.duckmage-map-nav-group {
|
2026-04-04 15:51:53 +00:00
|
|
|
position: absolute;
|
|
|
|
|
top: 8px;
|
|
|
|
|
left: 92px;
|
|
|
|
|
z-index: 10;
|
2026-05-23 16:29:22 +00:00
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
gap: 4px;
|
|
|
|
|
pointer-events: none;
|
|
|
|
|
}
|
|
|
|
|
.duckmage-map-nav-group > * {
|
|
|
|
|
pointer-events: auto;
|
|
|
|
|
}
|
|
|
|
|
.duckmage-region-btn {
|
2026-04-04 15:51:53 +00:00
|
|
|
height: 26px;
|
|
|
|
|
padding: 0 8px;
|
|
|
|
|
max-width: 140px;
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
text-overflow: ellipsis;
|
|
|
|
|
white-space: nowrap;
|
|
|
|
|
border-radius: 4px;
|
|
|
|
|
background: var(--background-secondary);
|
|
|
|
|
border: 1px solid var(--background-modifier-border);
|
|
|
|
|
color: var(--text-muted);
|
|
|
|
|
font-size: 0.85em;
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
opacity: 0.8;
|
|
|
|
|
transition:
|
|
|
|
|
opacity 0.15s ease,
|
|
|
|
|
color 0.15s ease;
|
|
|
|
|
user-select: none;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-region-btn:hover {
|
|
|
|
|
opacity: 1;
|
|
|
|
|
color: var(--text-normal);
|
|
|
|
|
}
|
2026-05-23 16:29:22 +00:00
|
|
|
.duckmage-map-back-btn {
|
|
|
|
|
height: 26px;
|
|
|
|
|
font-size: 0.78em;
|
|
|
|
|
padding: 0 8px;
|
|
|
|
|
opacity: 0.75;
|
|
|
|
|
background: transparent;
|
|
|
|
|
border: 1px solid var(--background-modifier-border);
|
|
|
|
|
border-radius: 4px;
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
color: var(--text-muted);
|
|
|
|
|
white-space: nowrap;
|
|
|
|
|
}
|
|
|
|
|
.duckmage-map-back-btn:hover {
|
|
|
|
|
opacity: 1;
|
|
|
|
|
color: var(--text-normal);
|
|
|
|
|
border-color: var(--interactive-accent);
|
|
|
|
|
}
|
2026-04-04 15:51:53 +00:00
|
|
|
|
2026-05-29 23:43:50 +00:00
|
|
|
/* Faction hover tooltip */
|
|
|
|
|
.duckmage-faction-tooltip {
|
|
|
|
|
position: absolute;
|
|
|
|
|
z-index: 50;
|
|
|
|
|
pointer-events: none;
|
|
|
|
|
background: var(--background-primary);
|
|
|
|
|
border: 1px solid var(--background-modifier-border);
|
|
|
|
|
border-radius: 6px;
|
|
|
|
|
padding: 6px 10px;
|
|
|
|
|
font-size: 0.82em;
|
|
|
|
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2);
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
gap: 5px;
|
|
|
|
|
min-width: 80px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-faction-tooltip-row {
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
gap: 6px;
|
|
|
|
|
white-space: nowrap;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-faction-tooltip-swatch {
|
|
|
|
|
width: 9px;
|
|
|
|
|
height: 9px;
|
|
|
|
|
border-radius: 50%;
|
|
|
|
|
flex-shrink: 0;
|
|
|
|
|
background: var(--background-modifier-border);
|
|
|
|
|
border: 1px solid color-mix(in srgb, currentColor 20%, transparent);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-faction-tooltip-name {
|
|
|
|
|
color: var(--text-normal);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Region / Map modal */
|
|
|
|
|
.duckmage-map-modal-tabs {
|
|
|
|
|
margin-bottom: 2px;
|
|
|
|
|
padding: 6px 8px 0;
|
|
|
|
|
border-bottom: 1px solid var(--background-modifier-border);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-map-modal-tab-content {
|
|
|
|
|
padding: 4px 0;
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-04 15:51:53 +00:00
|
|
|
.duckmage-region-modal h4 {
|
|
|
|
|
margin: 1em 0 0.4em;
|
|
|
|
|
font-size: 0.9em;
|
|
|
|
|
color: var(--text-muted);
|
|
|
|
|
text-transform: uppercase;
|
|
|
|
|
letter-spacing: 0.05em;
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-29 23:43:50 +00:00
|
|
|
.duckmage-map-field-label {
|
|
|
|
|
display: block;
|
|
|
|
|
font-size: 0.8em;
|
|
|
|
|
color: var(--text-muted);
|
|
|
|
|
text-transform: uppercase;
|
|
|
|
|
letter-spacing: 0.05em;
|
|
|
|
|
margin: 0.9em 0 0.25em;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-map-origin-desc {
|
|
|
|
|
font-size: 0.8em;
|
|
|
|
|
color: var(--text-faint);
|
|
|
|
|
margin: 0 0 0.4em;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-map-origin-label {
|
|
|
|
|
font-size: 0.82em;
|
|
|
|
|
color: var(--text-muted);
|
|
|
|
|
font-weight: 600;
|
|
|
|
|
min-width: 12px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-map-size-sep {
|
|
|
|
|
font-size: 0.85em;
|
|
|
|
|
color: var(--text-muted);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-map-presets-row {
|
|
|
|
|
gap: 5px;
|
|
|
|
|
flex-wrap: wrap;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-map-presets-row button {
|
|
|
|
|
font-size: 0.82em;
|
|
|
|
|
padding: 2px 8px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-map-new-name-input {
|
|
|
|
|
flex: 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-map-new-palette-select {
|
|
|
|
|
flex: 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-map-create-row {
|
|
|
|
|
margin-top: 1.2em;
|
|
|
|
|
justify-content: flex-end;
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-04 15:51:53 +00:00
|
|
|
.duckmage-region-list {
|
|
|
|
|
list-style: none;
|
|
|
|
|
padding: 0;
|
|
|
|
|
margin: 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-region-item {
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
gap: 8px;
|
|
|
|
|
padding: 5px 10px;
|
|
|
|
|
border-radius: 4px;
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
color: var(--text-normal);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-region-palette-badge {
|
|
|
|
|
font-size: 0.75em;
|
|
|
|
|
color: var(--text-muted);
|
|
|
|
|
background: var(--background-modifier-hover);
|
|
|
|
|
border-radius: 3px;
|
|
|
|
|
padding: 1px 5px;
|
|
|
|
|
margin-left: auto;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-palette-mgmt-list {
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
gap: 6px;
|
|
|
|
|
margin-bottom: 12px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-palette-mgmt-row {
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
gap: 8px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-palette-mgmt-name {
|
|
|
|
|
flex: 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-region-item:hover {
|
|
|
|
|
background: var(--background-modifier-hover);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-region-item.is-active {
|
|
|
|
|
background: var(--background-modifier-active-hover);
|
|
|
|
|
font-weight: 600;
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-19 20:51:03 +00:00
|
|
|
/* Map list delete button */
|
|
|
|
|
.duckmage-map-list-item {
|
|
|
|
|
cursor: default;
|
|
|
|
|
}
|
2026-05-25 16:49:41 +00:00
|
|
|
/* Flex row used in the submap picker list */
|
|
|
|
|
.duckmage-map-list-item-row {
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
gap: 6px;
|
|
|
|
|
}
|
2026-05-19 20:51:03 +00:00
|
|
|
|
2026-05-25 16:49:41 +00:00
|
|
|
.duckmage-map-list-name {
|
2026-05-19 20:51:03 +00:00
|
|
|
cursor: pointer;
|
|
|
|
|
flex: 1;
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-25 16:49:41 +00:00
|
|
|
.duckmage-map-terrain-swatch {
|
|
|
|
|
position: relative;
|
|
|
|
|
width: 10px;
|
|
|
|
|
height: 10px;
|
|
|
|
|
border-radius: 50%;
|
|
|
|
|
flex-shrink: 0;
|
|
|
|
|
background: var(--background-modifier-border);
|
|
|
|
|
border: 1px solid var(--background-modifier-border-hover);
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
}
|
|
|
|
|
.duckmage-map-terrain-swatch-icon {
|
|
|
|
|
position: absolute;
|
|
|
|
|
inset: 0;
|
|
|
|
|
width: 100%;
|
|
|
|
|
height: 100%;
|
|
|
|
|
object-fit: contain;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-map-terrain-swatch--set {
|
|
|
|
|
border-color: transparent;
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-19 20:51:03 +00:00
|
|
|
.duckmage-map-delete-btn {
|
|
|
|
|
margin-left: auto;
|
2026-05-19 21:00:02 +00:00
|
|
|
width: 20px;
|
|
|
|
|
height: 20px;
|
|
|
|
|
padding: 0;
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
justify-content: center;
|
|
|
|
|
font-size: 0.75em;
|
|
|
|
|
line-height: 1;
|
2026-05-19 20:51:03 +00:00
|
|
|
flex-shrink: 0;
|
2026-05-19 21:00:02 +00:00
|
|
|
color: var(--text-muted);
|
2026-05-19 20:51:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Inline delete confirmation row */
|
|
|
|
|
.duckmage-map-item-confirming {
|
|
|
|
|
cursor: default;
|
|
|
|
|
flex-wrap: wrap;
|
|
|
|
|
gap: 6px;
|
|
|
|
|
padding: 7px 10px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-map-delete-warning {
|
|
|
|
|
flex: 1 1 100%;
|
|
|
|
|
font-size: 0.85em;
|
2026-05-19 21:00:02 +00:00
|
|
|
color: var(--text-muted);
|
2026-05-19 20:51:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-map-confirm-btn {
|
|
|
|
|
padding: 3px 10px;
|
|
|
|
|
font-size: 0.82em;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-map-cancel-btn {
|
|
|
|
|
padding: 3px 10px;
|
|
|
|
|
font-size: 0.82em;
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-04 15:51:53 +00:00
|
|
|
.duckmage-region-row {
|
|
|
|
|
display: flex;
|
|
|
|
|
gap: 6px;
|
|
|
|
|
align-items: center;
|
|
|
|
|
flex-wrap: wrap;
|
|
|
|
|
margin-bottom: 0.5em;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-region-row input[type="text"] {
|
|
|
|
|
flex: 1;
|
|
|
|
|
min-width: 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-region-row input[type="number"] {
|
|
|
|
|
width: 55px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Saving / background-write indicator */
|
|
|
|
|
.duckmage-saving-indicator {
|
|
|
|
|
position: absolute;
|
|
|
|
|
bottom: 8px;
|
|
|
|
|
left: 8px;
|
|
|
|
|
z-index: 10;
|
|
|
|
|
background: var(--background-secondary);
|
|
|
|
|
border: 1px solid var(--background-modifier-border);
|
|
|
|
|
border-radius: 4px;
|
|
|
|
|
padding: 3px 8px;
|
|
|
|
|
font-size: 0.78em;
|
|
|
|
|
color: var(--text-muted);
|
|
|
|
|
opacity: 0;
|
|
|
|
|
pointer-events: none;
|
|
|
|
|
transition: opacity 0.2s ease;
|
|
|
|
|
user-select: none;
|
|
|
|
|
}
|
|
|
|
|
.duckmage-saving-indicator.is-active {
|
|
|
|
|
opacity: 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* ── Workflow styles ──────────────────────────────────────────────────────── */
|
|
|
|
|
|
|
|
|
|
/* View-mode toggle */
|
|
|
|
|
.duckmage-rt-mode-tabs {
|
|
|
|
|
display: flex;
|
|
|
|
|
gap: 4px;
|
|
|
|
|
padding: 4px 8px;
|
|
|
|
|
border-bottom: 1px solid var(--background-modifier-border);
|
|
|
|
|
}
|
|
|
|
|
.duckmage-rt-mode-tab {
|
|
|
|
|
background: none;
|
|
|
|
|
border: 1px solid transparent;
|
|
|
|
|
border-radius: 4px;
|
|
|
|
|
padding: 2px 10px;
|
|
|
|
|
font-size: 0.85em;
|
|
|
|
|
color: var(--text-muted);
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
box-shadow: none;
|
|
|
|
|
}
|
|
|
|
|
.duckmage-rt-mode-tab.is-active {
|
|
|
|
|
border-color: var(--background-modifier-border);
|
|
|
|
|
color: var(--text-normal);
|
|
|
|
|
background: var(--background-secondary);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Workflow list item */
|
|
|
|
|
.duckmage-rt-workflow-item {
|
|
|
|
|
padding: 4px 8px;
|
|
|
|
|
border-radius: 4px;
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
}
|
|
|
|
|
.duckmage-rt-workflow-item:hover {
|
|
|
|
|
background: var(--background-modifier-hover);
|
|
|
|
|
}
|
|
|
|
|
.duckmage-rt-workflow-item.is-active {
|
|
|
|
|
background: var(--background-modifier-active-hover);
|
|
|
|
|
font-weight: 600;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Workflow wizard step rows */
|
|
|
|
|
.duckmage-wf-wizard-header {
|
|
|
|
|
display: flex;
|
|
|
|
|
gap: 8px;
|
|
|
|
|
align-items: center;
|
|
|
|
|
margin-bottom: 12px;
|
|
|
|
|
}
|
|
|
|
|
.duckmage-wf-sum-toggle {
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
gap: 3px;
|
|
|
|
|
flex: 0 0 auto;
|
|
|
|
|
font-size: var(--font-ui-small);
|
|
|
|
|
color: var(--text-muted);
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
user-select: none;
|
|
|
|
|
}
|
|
|
|
|
.duckmage-wf-sum-all {
|
|
|
|
|
margin-left: auto;
|
|
|
|
|
}
|
|
|
|
|
.duckmage-wf-wizard-step {
|
|
|
|
|
margin-bottom: 12px;
|
|
|
|
|
padding: 8px;
|
|
|
|
|
border: 1px solid var(--background-modifier-border);
|
|
|
|
|
border-radius: 6px;
|
|
|
|
|
}
|
|
|
|
|
.duckmage-wf-wizard-step-header {
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
gap: 6px;
|
|
|
|
|
margin-bottom: 6px;
|
|
|
|
|
}
|
|
|
|
|
.duckmage-wf-roll-badge {
|
|
|
|
|
font-size: 0.75em;
|
|
|
|
|
background: var(--background-modifier-border);
|
|
|
|
|
border-radius: 3px;
|
|
|
|
|
padding: 1px 5px;
|
|
|
|
|
color: var(--text-muted);
|
|
|
|
|
}
|
|
|
|
|
.duckmage-wf-dice-badge {
|
|
|
|
|
background: var(--color-purple);
|
|
|
|
|
color: var(--text-on-accent);
|
|
|
|
|
font-family: var(--font-monospace);
|
|
|
|
|
}
|
|
|
|
|
.duckmage-wf-step-row {
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
gap: 8px;
|
|
|
|
|
padding: 4px 0;
|
|
|
|
|
width: 100%;
|
|
|
|
|
box-sizing: border-box;
|
|
|
|
|
}
|
|
|
|
|
.duckmage-wf-roll-input {
|
|
|
|
|
flex: 1 1 0;
|
|
|
|
|
min-width: 0;
|
|
|
|
|
font-style: italic;
|
|
|
|
|
color: var(--text-muted);
|
|
|
|
|
}
|
|
|
|
|
.duckmage-wf-pick-select {
|
|
|
|
|
flex: 0 0 90px;
|
|
|
|
|
width: 90px;
|
|
|
|
|
min-width: 0;
|
|
|
|
|
}
|
|
|
|
|
.duckmage-wf-roll-input.is-rolled {
|
|
|
|
|
font-style: normal;
|
|
|
|
|
color: var(--text-normal);
|
|
|
|
|
}
|
|
|
|
|
.duckmage-wf-roll-btn {
|
|
|
|
|
flex: 0 0 auto;
|
|
|
|
|
width: 60px;
|
|
|
|
|
text-align: center;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Workflow editor content — flex column so template section can grow */
|
|
|
|
|
.duckmage-wf-editor {
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
flex: 1;
|
|
|
|
|
min-height: 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Template section */
|
|
|
|
|
.duckmage-wf-template-section-wrap {
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Workflow template area */
|
|
|
|
|
.duckmage-wf-template-area {
|
|
|
|
|
width: 100%;
|
|
|
|
|
height: 220px;
|
|
|
|
|
font-family: var(--font-monospace);
|
|
|
|
|
font-size: 0.85em;
|
|
|
|
|
resize: vertical;
|
|
|
|
|
box-sizing: border-box;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Validation */
|
|
|
|
|
.duckmage-wf-validation-ok {
|
|
|
|
|
color: var(--color-green);
|
|
|
|
|
font-size: 0.85em;
|
|
|
|
|
margin-top: 4px;
|
|
|
|
|
}
|
|
|
|
|
.duckmage-wf-validation-err {
|
|
|
|
|
color: var(--color-red);
|
|
|
|
|
font-size: 0.85em;
|
|
|
|
|
margin-top: 4px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Workflow editor step rows */
|
|
|
|
|
.duckmage-wf-editor-steps {
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
gap: 4px;
|
|
|
|
|
margin-bottom: 8px;
|
|
|
|
|
}
|
|
|
|
|
.duckmage-wf-editor-step-row {
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
gap: 6px;
|
|
|
|
|
margin-bottom: 4px;
|
|
|
|
|
width: 100%;
|
|
|
|
|
box-sizing: border-box;
|
|
|
|
|
}
|
|
|
|
|
.duckmage-wf-editor-step-row select {
|
|
|
|
|
flex: 2 1 0;
|
|
|
|
|
width: 0;
|
|
|
|
|
min-width: 0;
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
text-overflow: ellipsis;
|
|
|
|
|
}
|
|
|
|
|
.duckmage-wf-kind-badge {
|
|
|
|
|
font-size: 1em;
|
|
|
|
|
flex: 0 0 auto;
|
|
|
|
|
cursor: default;
|
|
|
|
|
}
|
|
|
|
|
.duckmage-wf-formula-input {
|
|
|
|
|
flex: 2 1 0;
|
|
|
|
|
min-width: 0;
|
|
|
|
|
font-family: var(--font-monospace);
|
|
|
|
|
font-size: 0.9em;
|
|
|
|
|
}
|
|
|
|
|
.duckmage-wf-add-type-select {
|
|
|
|
|
flex: 0 0 auto;
|
|
|
|
|
max-width: 5em;
|
|
|
|
|
}
|
|
|
|
|
.duckmage-wf-add-specific {
|
|
|
|
|
flex: 2 1 0;
|
|
|
|
|
min-width: 0;
|
|
|
|
|
display: flex;
|
|
|
|
|
}
|
|
|
|
|
.duckmage-wf-add-specific select,
|
|
|
|
|
.duckmage-wf-add-specific input {
|
|
|
|
|
flex: 1 1 0;
|
|
|
|
|
min-width: 0;
|
|
|
|
|
width: 0;
|
|
|
|
|
}
|
|
|
|
|
.duckmage-wf-editor-step-row input[type="number"] {
|
|
|
|
|
flex: 0 0 52px;
|
|
|
|
|
width: 52px;
|
|
|
|
|
min-width: 0;
|
|
|
|
|
}
|
|
|
|
|
.duckmage-wf-editor-step-row input[type="text"] {
|
|
|
|
|
flex: 1 1 0;
|
|
|
|
|
min-width: 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* "Used by workflows" section in table detail */
|
|
|
|
|
.duckmage-rt-used-by {
|
|
|
|
|
margin-top: 12px;
|
|
|
|
|
padding-top: 8px;
|
|
|
|
|
border-top: 1px solid var(--background-modifier-border);
|
|
|
|
|
}
|
|
|
|
|
.duckmage-rt-used-by-label {
|
|
|
|
|
font-size: 0.8em;
|
|
|
|
|
color: var(--text-muted);
|
|
|
|
|
text-transform: uppercase;
|
|
|
|
|
letter-spacing: 0.05em;
|
|
|
|
|
margin-bottom: 4px;
|
|
|
|
|
}
|
|
|
|
|
.duckmage-rt-used-by-links {
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
gap: 4px;
|
|
|
|
|
margin-top: 4px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Save status */
|
|
|
|
|
.duckmage-wf-save-status {
|
|
|
|
|
margin-top: 4px;
|
|
|
|
|
font-size: 0.85em;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-wf-editor-add-area {
|
|
|
|
|
margin-bottom: 8px;
|
|
|
|
|
}
|
|
|
|
|
.duckmage-wf-editor-add-btn-row {
|
|
|
|
|
margin-top: 6px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* ── Terrain template picker (add-from-existing) ─────────────────────────── */
|
|
|
|
|
|
|
|
|
|
.duckmage-terrain-template-list {
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
gap: 2px;
|
|
|
|
|
margin-top: 8px;
|
|
|
|
|
max-height: 320px;
|
|
|
|
|
overflow-y: auto;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-terrain-template-group {
|
|
|
|
|
font-size: 0.75em;
|
|
|
|
|
font-weight: 600;
|
|
|
|
|
color: var(--text-muted);
|
|
|
|
|
text-transform: uppercase;
|
|
|
|
|
letter-spacing: 0.05em;
|
|
|
|
|
padding: 8px 4px 2px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-terrain-template-row {
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
gap: 8px;
|
|
|
|
|
padding: 5px 8px;
|
|
|
|
|
border-radius: 4px;
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-terrain-template-row:hover {
|
|
|
|
|
background: var(--background-modifier-hover);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-terrain-template-swatch {
|
|
|
|
|
width: 14px;
|
|
|
|
|
height: 14px;
|
|
|
|
|
border-radius: 3px;
|
|
|
|
|
flex-shrink: 0;
|
|
|
|
|
border: 1px solid var(--background-modifier-border);
|
|
|
|
|
}
|
2026-05-10 00:49:23 +00:00
|
|
|
|
|
|
|
|
/* ── Faction picker modal ──────────────────────────────────────────────────── */
|
|
|
|
|
|
|
|
|
|
.duckmage-faction-picker {
|
|
|
|
|
min-width: 320px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-faction-picker-empty {
|
|
|
|
|
color: var(--text-muted);
|
|
|
|
|
font-style: italic;
|
|
|
|
|
padding: 8px 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Hidden native color picker — triggered via .click() */
|
|
|
|
|
.duckmage-faction-color-input {
|
|
|
|
|
position: absolute;
|
|
|
|
|
opacity: 0;
|
|
|
|
|
width: 0;
|
|
|
|
|
height: 0;
|
|
|
|
|
pointer-events: none;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Palette grid */
|
|
|
|
|
.duckmage-faction-palette {
|
|
|
|
|
display: grid;
|
|
|
|
|
grid-template-columns: repeat(auto-fill, minmax(72px, 1fr));
|
|
|
|
|
gap: 6px;
|
|
|
|
|
padding: 4px 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-faction-tile {
|
|
|
|
|
position: relative;
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
align-items: center;
|
|
|
|
|
gap: 4px;
|
|
|
|
|
padding: 6px 4px 4px;
|
|
|
|
|
border-radius: 6px;
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
border: 2px solid transparent;
|
|
|
|
|
transition: border-color 0.1s, background 0.1s;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-faction-tile:hover {
|
|
|
|
|
background: var(--background-modifier-hover);
|
|
|
|
|
border-color: var(--color-accent);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-faction-tile-preview {
|
fix: overlay pattern editor — hex preview, palette refresh, thin outline
Three refinements to the 1.3.0 overlay pattern editor based on user
feedback:
- Preview: was a 120×36 rect at the pattern's own scale. Now a hex
polygon at the live on-map hex size (read from .duckmage-hex,
falling back to 96 px), in the configured orientation — so the
preview shows exactly what the overlay will look like on the map.
- Palette tiles: were never updating on Save because the picker
re-read style from the metadata cache, which lags Obsidian's
frontmatter write by a tick. The editor's onSaved callback now
passes the new OverlayStyle directly, so the picker renders from
the editor's in-memory state and updates immediately.
- Outline width: was hardcoded to gapPx*2+2 (often 14-22 px), which
made overlay borders read as solid blobs. Now per-overlay,
defaulting to 1.5 px, with a slider (0-20, step 0.5) in the editor.
Persisted as faction-outline-width / region-outline-width
frontmatter keys. Crank it up to recover the old thick-blob look.
2026-06-17 16:22:28 +00:00
|
|
|
width: 56px;
|
|
|
|
|
height: 56px;
|
2026-05-10 00:49:23 +00:00
|
|
|
border-radius: 4px;
|
fix: overlay pattern editor — hex preview, palette refresh, thin outline
Three refinements to the 1.3.0 overlay pattern editor based on user
feedback:
- Preview: was a 120×36 rect at the pattern's own scale. Now a hex
polygon at the live on-map hex size (read from .duckmage-hex,
falling back to 96 px), in the configured orientation — so the
preview shows exactly what the overlay will look like on the map.
- Palette tiles: were never updating on Save because the picker
re-read style from the metadata cache, which lags Obsidian's
frontmatter write by a tick. The editor's onSaved callback now
passes the new OverlayStyle directly, so the picker renders from
the editor's in-memory state and updates immediately.
- Outline width: was hardcoded to gapPx*2+2 (often 14-22 px), which
made overlay borders read as solid blobs. Now per-overlay,
defaulting to 1.5 px, with a slider (0-20, step 0.5) in the editor.
Persisted as faction-outline-width / region-outline-width
frontmatter keys. Crank it up to recover the old thick-blob look.
2026-06-17 16:22:28 +00:00
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
justify-content: center;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-faction-tile-preview svg {
|
|
|
|
|
display: block;
|
2026-05-10 00:49:23 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Empty / no-color state: diagonal stripe */
|
|
|
|
|
.duckmage-faction-tile-preview-empty {
|
|
|
|
|
background:
|
|
|
|
|
linear-gradient(
|
|
|
|
|
to bottom right,
|
|
|
|
|
transparent calc(50% - 1px),
|
|
|
|
|
var(--background-modifier-border) calc(50% - 1px),
|
|
|
|
|
var(--background-modifier-border) calc(50% + 1px),
|
|
|
|
|
transparent calc(50% + 1px)
|
|
|
|
|
),
|
|
|
|
|
var(--background-secondary);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-faction-tile-name {
|
|
|
|
|
font-size: 0.75em;
|
|
|
|
|
text-align: center;
|
|
|
|
|
word-break: break-word;
|
|
|
|
|
max-width: 100%;
|
|
|
|
|
color: var(--text-normal);
|
|
|
|
|
line-height: 1.2;
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-10 13:10:15 +00:00
|
|
|
/* Edit button — top-right corner, faintly visible at rest */
|
2026-05-10 00:49:23 +00:00
|
|
|
.duckmage-faction-tile-edit {
|
|
|
|
|
position: absolute;
|
|
|
|
|
top: 2px;
|
|
|
|
|
right: 2px;
|
|
|
|
|
width: 18px;
|
|
|
|
|
height: 18px;
|
|
|
|
|
padding: 0;
|
|
|
|
|
font-size: 0.7em;
|
|
|
|
|
background: var(--background-secondary);
|
|
|
|
|
border: 1px solid var(--background-modifier-border);
|
|
|
|
|
border-radius: 3px;
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
justify-content: center;
|
2026-05-10 13:10:15 +00:00
|
|
|
opacity: 0.35;
|
2026-05-10 00:49:23 +00:00
|
|
|
transition: opacity 0.1s;
|
|
|
|
|
z-index: 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-faction-tile:hover .duckmage-faction-tile-edit {
|
|
|
|
|
opacity: 1;
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-25 16:49:41 +00:00
|
|
|
.duckmage-faction-tile-preview-remove {
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
justify-content: center;
|
|
|
|
|
font-size: 1.4em;
|
|
|
|
|
color: var(--color-red);
|
|
|
|
|
background: var(--background-secondary);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-faction-tile-remove:hover .duckmage-faction-tile-preview-remove {
|
|
|
|
|
background: var(--background-modifier-error);
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-10 00:49:23 +00:00
|
|
|
/* Faction editor sub-modal */
|
|
|
|
|
.duckmage-faction-editor-header {
|
|
|
|
|
display: flex;
|
|
|
|
|
justify-content: flex-end;
|
|
|
|
|
margin-bottom: 8px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-faction-editor-open-btn {
|
|
|
|
|
font-size: 0.85em;
|
|
|
|
|
padding: 2px 8px;
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-faction-editor-desc {
|
|
|
|
|
margin-bottom: 12px;
|
|
|
|
|
padding: 8px 10px;
|
|
|
|
|
background: var(--background-secondary);
|
|
|
|
|
border-radius: 4px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-faction-editor-desc-label {
|
|
|
|
|
display: block;
|
|
|
|
|
font-size: 0.75em;
|
|
|
|
|
font-weight: 600;
|
|
|
|
|
text-transform: uppercase;
|
|
|
|
|
letter-spacing: 0.05em;
|
|
|
|
|
color: var(--text-muted);
|
|
|
|
|
margin-bottom: 4px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-faction-editor-desc-text {
|
|
|
|
|
margin: 0;
|
|
|
|
|
font-size: 0.88em;
|
|
|
|
|
line-height: 1.5;
|
|
|
|
|
color: var(--text-normal);
|
|
|
|
|
white-space: pre-wrap;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-faction-editor-btns {
|
|
|
|
|
display: flex;
|
|
|
|
|
gap: 8px;
|
|
|
|
|
margin-top: 12px;
|
|
|
|
|
justify-content: flex-end;
|
|
|
|
|
}
|
2026-05-10 13:10:15 +00:00
|
|
|
|
feat: faction & region overlay patterns (1.3.0)
Adds per-overlay pattern fills so factions and regions can be visually
distinguished when they overlap (issue #25). Each faction note and each
region note now carries a pattern, scale, and opacity in its
frontmatter, edited via the existing FactionEditor / GeoRegionEditor
modals (dropdown + sliders + live preview swatch).
Pattern set (10 total): solid (the existing flat fill, default),
diagonal stripes ↗ / ↖, crosshatch, polka dots, grid, zigzag,
triangles, scales, checker.
On screen: SVG <pattern> defs are deduped per (key,color,scale) and
referenced from each overlay path's fill. Stroke stays solid color so
blob boundaries remain crisp. Per-overlay opacity replaces the
hardcoded 0.45 group opacity.
PNG export: mirrored via Canvas patterns built from an offscreen tile
per (key,color,scale), cached for the render pass.
Backwards compatible — overlays without the new frontmatter keys fall
back to solid + 0.45 opacity, matching pre-1.3.0 behavior.
2026-06-17 15:53:04 +00:00
|
|
|
.duckmage-pattern-preview-wrap {
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
fix: overlay pattern editor — hex preview, palette refresh, thin outline
Three refinements to the 1.3.0 overlay pattern editor based on user
feedback:
- Preview: was a 120×36 rect at the pattern's own scale. Now a hex
polygon at the live on-map hex size (read from .duckmage-hex,
falling back to 96 px), in the configured orientation — so the
preview shows exactly what the overlay will look like on the map.
- Palette tiles: were never updating on Save because the picker
re-read style from the metadata cache, which lags Obsidian's
frontmatter write by a tick. The editor's onSaved callback now
passes the new OverlayStyle directly, so the picker renders from
the editor's in-memory state and updates immediately.
- Outline width: was hardcoded to gapPx*2+2 (often 14-22 px), which
made overlay borders read as solid blobs. Now per-overlay,
defaulting to 1.5 px, with a slider (0-20, step 0.5) in the editor.
Persisted as faction-outline-width / region-outline-width
frontmatter keys. Crank it up to recover the old thick-blob look.
2026-06-17 16:22:28 +00:00
|
|
|
gap: 14px;
|
|
|
|
|
margin: 10px 0 6px;
|
|
|
|
|
padding: 8px 12px;
|
feat: faction & region overlay patterns (1.3.0)
Adds per-overlay pattern fills so factions and regions can be visually
distinguished when they overlap (issue #25). Each faction note and each
region note now carries a pattern, scale, and opacity in its
frontmatter, edited via the existing FactionEditor / GeoRegionEditor
modals (dropdown + sliders + live preview swatch).
Pattern set (10 total): solid (the existing flat fill, default),
diagonal stripes ↗ / ↖, crosshatch, polka dots, grid, zigzag,
triangles, scales, checker.
On screen: SVG <pattern> defs are deduped per (key,color,scale) and
referenced from each overlay path's fill. Stroke stays solid color so
blob boundaries remain crisp. Per-overlay opacity replaces the
hardcoded 0.45 group opacity.
PNG export: mirrored via Canvas patterns built from an offscreen tile
per (key,color,scale), cached for the render pass.
Backwards compatible — overlays without the new frontmatter keys fall
back to solid + 0.45 opacity, matching pre-1.3.0 behavior.
2026-06-17 15:53:04 +00:00
|
|
|
border-radius: 6px;
|
|
|
|
|
background: var(--background-secondary);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-pattern-preview-label {
|
|
|
|
|
font-size: 0.85em;
|
|
|
|
|
color: var(--text-muted);
|
|
|
|
|
font-weight: 500;
|
|
|
|
|
}
|
|
|
|
|
|
fix: overlay pattern editor — hex preview, palette refresh, thin outline
Three refinements to the 1.3.0 overlay pattern editor based on user
feedback:
- Preview: was a 120×36 rect at the pattern's own scale. Now a hex
polygon at the live on-map hex size (read from .duckmage-hex,
falling back to 96 px), in the configured orientation — so the
preview shows exactly what the overlay will look like on the map.
- Palette tiles: were never updating on Save because the picker
re-read style from the metadata cache, which lags Obsidian's
frontmatter write by a tick. The editor's onSaved callback now
passes the new OverlayStyle directly, so the picker renders from
the editor's in-memory state and updates immediately.
- Outline width: was hardcoded to gapPx*2+2 (often 14-22 px), which
made overlay borders read as solid blobs. Now per-overlay,
defaulting to 1.5 px, with a slider (0-20, step 0.5) in the editor.
Persisted as faction-outline-width / region-outline-width
frontmatter keys. Crank it up to recover the old thick-blob look.
2026-06-17 16:22:28 +00:00
|
|
|
.duckmage-pattern-preview-host {
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
justify-content: center;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-hex-preview-svg {
|
feat: faction & region overlay patterns (1.3.0)
Adds per-overlay pattern fills so factions and regions can be visually
distinguished when they overlap (issue #25). Each faction note and each
region note now carries a pattern, scale, and opacity in its
frontmatter, edited via the existing FactionEditor / GeoRegionEditor
modals (dropdown + sliders + live preview swatch).
Pattern set (10 total): solid (the existing flat fill, default),
diagonal stripes ↗ / ↖, crosshatch, polka dots, grid, zigzag,
triangles, scales, checker.
On screen: SVG <pattern> defs are deduped per (key,color,scale) and
referenced from each overlay path's fill. Stroke stays solid color so
blob boundaries remain crisp. Per-overlay opacity replaces the
hardcoded 0.45 group opacity.
PNG export: mirrored via Canvas patterns built from an offscreen tile
per (key,color,scale), cached for the render pass.
Backwards compatible — overlays without the new frontmatter keys fall
back to solid + 0.45 opacity, matching pre-1.3.0 behavior.
2026-06-17 15:53:04 +00:00
|
|
|
display: block;
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-10 13:10:15 +00:00
|
|
|
/* Region overlay SVG */
|
|
|
|
|
.duckmage-region-svg {
|
|
|
|
|
position: absolute;
|
|
|
|
|
top: 0;
|
|
|
|
|
left: 0;
|
|
|
|
|
pointer-events: none;
|
|
|
|
|
z-index: 3; /* below faction (4) and path (5) SVGs */
|
|
|
|
|
overflow: visible;
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-21 15:40:39 +00:00
|
|
|
/* Region name labels — rendered as HTML inside gridContainer so the
|
|
|
|
|
label position tracks the em-based hex layout perfectly across
|
|
|
|
|
font-size changes (no slip-on-zoom). Same approach as coord labels.
|
|
|
|
|
The layer fills gridContainer; each label is positioned as a
|
|
|
|
|
percentage of that container so bake-zoom font-size mutations don't
|
|
|
|
|
strand the label at a stale pixel offset. PCA rotation is applied
|
|
|
|
|
AFTER the centering translate so the label spins around its own
|
|
|
|
|
centre. */
|
|
|
|
|
.duckmage-region-labels-layer {
|
|
|
|
|
position: absolute;
|
|
|
|
|
top: 0;
|
|
|
|
|
left: 0;
|
|
|
|
|
width: 100%;
|
|
|
|
|
height: 100%;
|
|
|
|
|
pointer-events: none;
|
|
|
|
|
z-index: 4;
|
|
|
|
|
}
|
2026-05-10 13:10:15 +00:00
|
|
|
.duckmage-region-label {
|
2026-06-21 15:40:39 +00:00
|
|
|
position: absolute;
|
|
|
|
|
left: var(--duckmage-region-label-left, 50%);
|
|
|
|
|
top: var(--duckmage-region-label-top, 50%);
|
|
|
|
|
transform: translate(-50%, -50%) rotate(var(--duckmage-region-label-angle, 0deg));
|
|
|
|
|
font-size: var(--duckmage-region-label-font-size, 16px);
|
2026-05-10 13:10:15 +00:00
|
|
|
font-weight: 700;
|
|
|
|
|
letter-spacing: 0.04em;
|
2026-06-21 15:40:39 +00:00
|
|
|
color: var(--text-normal);
|
|
|
|
|
text-shadow:
|
|
|
|
|
-1px -1px 0 var(--background-primary),
|
|
|
|
|
1px -1px 0 var(--background-primary),
|
|
|
|
|
-1px 1px 0 var(--background-primary),
|
|
|
|
|
1px 1px 0 var(--background-primary),
|
|
|
|
|
0 -1px 0 var(--background-primary),
|
|
|
|
|
0 1px 0 var(--background-primary),
|
|
|
|
|
-1px 0 0 var(--background-primary),
|
|
|
|
|
1px 0 0 var(--background-primary);
|
2026-05-10 13:10:15 +00:00
|
|
|
pointer-events: none;
|
|
|
|
|
user-select: none;
|
2026-06-21 15:40:39 +00:00
|
|
|
white-space: nowrap;
|
2026-05-10 13:10:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Faction overlay legend — floats in the void to the right of the hex grid */
|
|
|
|
|
.duckmage-faction-legend {
|
|
|
|
|
position: absolute;
|
|
|
|
|
z-index: 6;
|
|
|
|
|
background: var(--background-primary);
|
|
|
|
|
border: 1px solid var(--background-modifier-border);
|
|
|
|
|
border-radius: 6px;
|
|
|
|
|
padding: 8px 10px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-faction-legend-row {
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
gap: 6px;
|
|
|
|
|
padding: 2px 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-faction-legend-swatch {
|
2026-06-17 16:33:00 +00:00
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
justify-content: center;
|
2026-05-10 13:10:15 +00:00
|
|
|
flex-shrink: 0;
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-17 16:33:00 +00:00
|
|
|
.duckmage-faction-legend-swatch svg {
|
|
|
|
|
display: block;
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-10 13:10:15 +00:00
|
|
|
.duckmage-faction-legend-name {
|
|
|
|
|
font-size: 0.8em;
|
|
|
|
|
color: var(--text-normal);
|
|
|
|
|
white-space: nowrap;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* New faction add row */
|
|
|
|
|
.duckmage-faction-picker-add {
|
|
|
|
|
display: flex;
|
|
|
|
|
gap: 8px;
|
|
|
|
|
align-items: center;
|
|
|
|
|
margin-top: 12px;
|
|
|
|
|
padding-top: 10px;
|
|
|
|
|
border-top: 1px solid var(--background-modifier-border);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-faction-picker-add-input {
|
|
|
|
|
flex: 1;
|
|
|
|
|
min-width: 0;
|
|
|
|
|
padding: 4px 8px;
|
|
|
|
|
background: var(--background-primary);
|
|
|
|
|
border: 1px solid var(--background-modifier-border);
|
|
|
|
|
border-radius: 4px;
|
|
|
|
|
color: var(--text-normal);
|
|
|
|
|
font-size: 0.9em;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-faction-picker-add-btn {
|
|
|
|
|
flex-shrink: 0;
|
|
|
|
|
}
|
2026-05-19 20:39:01 +00:00
|
|
|
|
|
|
|
|
/* ─── Setup Wizard ─────────────────────────────────────────────────────────── */
|
|
|
|
|
|
|
|
|
|
.duckmage-setup-wizard {
|
|
|
|
|
padding: 40px 48px;
|
|
|
|
|
max-width: 640px;
|
|
|
|
|
margin: 0 auto;
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-wizard-header {
|
|
|
|
|
margin-bottom: 4px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-wizard-title {
|
|
|
|
|
font-size: 0.7em;
|
|
|
|
|
font-weight: 700;
|
|
|
|
|
color: var(--text-muted);
|
|
|
|
|
text-transform: uppercase;
|
|
|
|
|
letter-spacing: 0.1em;
|
|
|
|
|
margin: 0 0 14px 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-wizard-dots {
|
|
|
|
|
display: flex;
|
|
|
|
|
gap: 8px;
|
|
|
|
|
margin-bottom: 6px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-wizard-dot {
|
|
|
|
|
width: 10px;
|
|
|
|
|
height: 10px;
|
|
|
|
|
border-radius: 50%;
|
|
|
|
|
background: var(--background-modifier-border);
|
|
|
|
|
transition: background 0.2s;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-wizard-dot.is-current {
|
|
|
|
|
background: var(--interactive-accent);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-wizard-dot.is-done {
|
|
|
|
|
background: var(--text-muted);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-wizard-step-label {
|
|
|
|
|
font-size: 0.75em;
|
|
|
|
|
color: var(--text-faint);
|
|
|
|
|
margin: 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-wizard-step-title {
|
|
|
|
|
font-size: 1.5em;
|
|
|
|
|
font-weight: 600;
|
|
|
|
|
margin: 20px 0 24px 0;
|
|
|
|
|
line-height: 1.2;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-wizard-step-content {
|
|
|
|
|
margin-bottom: 28px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-wizard-text {
|
|
|
|
|
line-height: 1.65;
|
|
|
|
|
color: var(--text-normal);
|
|
|
|
|
margin: 0 0 16px 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-wizard-tip {
|
|
|
|
|
color: var(--text-muted);
|
|
|
|
|
font-size: 0.88em;
|
|
|
|
|
padding: 10px 14px;
|
|
|
|
|
border-left: 3px solid var(--interactive-accent);
|
|
|
|
|
background: var(--background-secondary);
|
|
|
|
|
border-radius: 0 4px 4px 0;
|
|
|
|
|
margin-top: 8px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* ── Fields ── */
|
|
|
|
|
|
|
|
|
|
.duckmage-wizard-field {
|
|
|
|
|
margin-bottom: 22px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-wizard-label {
|
|
|
|
|
display: block;
|
|
|
|
|
font-size: 0.78em;
|
|
|
|
|
font-weight: 600;
|
|
|
|
|
color: var(--text-muted);
|
|
|
|
|
text-transform: uppercase;
|
|
|
|
|
letter-spacing: 0.06em;
|
|
|
|
|
margin-bottom: 7px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-wizard-input {
|
|
|
|
|
width: 100%;
|
|
|
|
|
padding: 9px 11px;
|
|
|
|
|
background: var(--background-primary);
|
|
|
|
|
border: 1px solid var(--background-modifier-border);
|
|
|
|
|
border-radius: 5px;
|
|
|
|
|
color: var(--text-normal);
|
|
|
|
|
font-size: 0.95em;
|
|
|
|
|
box-sizing: border-box;
|
|
|
|
|
transition: border-color 0.15s;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-wizard-input:focus {
|
|
|
|
|
border-color: var(--interactive-accent);
|
|
|
|
|
outline: none;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-wizard-input-sm {
|
|
|
|
|
font-size: 0.85em;
|
|
|
|
|
padding: 7px 9px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* ── Subfolder preview ── */
|
|
|
|
|
|
|
|
|
|
.duckmage-wizard-preview-wrap {
|
|
|
|
|
margin-top: -10px;
|
|
|
|
|
margin-bottom: 22px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-wizard-preview-label {
|
|
|
|
|
font-size: 0.78em;
|
|
|
|
|
color: var(--text-muted);
|
|
|
|
|
margin: 0 0 7px 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-wizard-preview-list {
|
|
|
|
|
list-style: none;
|
|
|
|
|
padding: 10px 14px;
|
|
|
|
|
margin: 0;
|
|
|
|
|
background: var(--background-secondary);
|
|
|
|
|
border-radius: 5px;
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-wrap: wrap;
|
|
|
|
|
gap: 4px 20px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-wizard-preview-item {
|
|
|
|
|
font-size: 0.8em;
|
|
|
|
|
color: var(--text-muted);
|
|
|
|
|
font-family: var(--font-monospace);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* ── Advanced section ── */
|
|
|
|
|
|
|
|
|
|
.duckmage-wizard-advanced-row {
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
gap: 8px;
|
|
|
|
|
margin-bottom: 14px;
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-wizard-advanced-label {
|
|
|
|
|
font-size: 0.85em;
|
|
|
|
|
color: var(--text-normal);
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-wizard-advanced-section {
|
|
|
|
|
padding: 18px;
|
|
|
|
|
background: var(--background-secondary);
|
|
|
|
|
border-radius: 5px;
|
|
|
|
|
margin-bottom: 8px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* ── Grid size ── */
|
|
|
|
|
|
|
|
|
|
.duckmage-wizard-size-inputs {
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
gap: 8px;
|
|
|
|
|
flex-wrap: wrap;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-wizard-input-num {
|
|
|
|
|
width: 68px;
|
|
|
|
|
padding: 9px 8px;
|
|
|
|
|
background: var(--background-primary);
|
|
|
|
|
border: 1px solid var(--background-modifier-border);
|
|
|
|
|
border-radius: 5px;
|
|
|
|
|
color: var(--text-normal);
|
|
|
|
|
font-size: 0.95em;
|
|
|
|
|
text-align: center;
|
|
|
|
|
transition: border-color 0.15s;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-wizard-input-num:focus {
|
|
|
|
|
border-color: var(--interactive-accent);
|
|
|
|
|
outline: none;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-wizard-size-sep {
|
|
|
|
|
color: var(--text-muted);
|
|
|
|
|
font-size: 0.9em;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-wizard-note-count {
|
|
|
|
|
margin: 9px 0 0 0;
|
|
|
|
|
font-size: 0.83em;
|
|
|
|
|
color: var(--text-accent);
|
|
|
|
|
font-weight: 500;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* ── Hex orientation ── */
|
|
|
|
|
|
|
|
|
|
.duckmage-wizard-orient-row {
|
|
|
|
|
display: flex;
|
|
|
|
|
gap: 12px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-wizard-orient-btn {
|
|
|
|
|
flex: 1;
|
|
|
|
|
padding: 14px 16px;
|
|
|
|
|
background: var(--background-secondary);
|
|
|
|
|
border: 2px solid var(--background-modifier-border);
|
|
|
|
|
border-radius: 6px;
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
transition: border-color 0.15s, background 0.15s;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-wizard-orient-btn:hover {
|
|
|
|
|
border-color: var(--interactive-accent-hover);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-wizard-orient-btn.is-active {
|
|
|
|
|
border-color: var(--interactive-accent);
|
|
|
|
|
background: var(--background-modifier-hover);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-wizard-orient-label {
|
|
|
|
|
display: block;
|
|
|
|
|
font-weight: 600;
|
|
|
|
|
font-size: 0.9em;
|
|
|
|
|
margin-bottom: 5px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-wizard-orient-desc {
|
|
|
|
|
display: block;
|
|
|
|
|
font-size: 0.78em;
|
|
|
|
|
color: var(--text-muted);
|
|
|
|
|
line-height: 1.4;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* ── Palette select ── */
|
|
|
|
|
|
|
|
|
|
.duckmage-wizard-select {
|
|
|
|
|
width: 100%;
|
|
|
|
|
height: 36px;
|
|
|
|
|
padding: 0 10px;
|
|
|
|
|
background: var(--background-primary);
|
|
|
|
|
border: 1px solid var(--background-modifier-border);
|
|
|
|
|
border-radius: 5px;
|
|
|
|
|
color: var(--text-normal);
|
|
|
|
|
font-size: 0.95em;
|
|
|
|
|
box-sizing: border-box;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* ── Progress message (map generation) ── */
|
|
|
|
|
|
|
|
|
|
.duckmage-wizard-progress-msg {
|
|
|
|
|
font-size: 0.83em;
|
|
|
|
|
color: var(--text-muted);
|
|
|
|
|
min-height: 18px;
|
|
|
|
|
margin-top: 6px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* ── Nav row ── */
|
|
|
|
|
|
|
|
|
|
.duckmage-wizard-nav {
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
gap: 12px;
|
|
|
|
|
padding-top: 20px;
|
|
|
|
|
border-top: 1px solid var(--background-modifier-border);
|
|
|
|
|
margin-bottom: 16px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-wizard-back-btn {
|
|
|
|
|
padding: 8px 18px;
|
|
|
|
|
background: transparent;
|
|
|
|
|
border: 1px solid var(--background-modifier-border);
|
|
|
|
|
border-radius: 5px;
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
color: var(--text-normal);
|
|
|
|
|
font-size: 0.88em;
|
|
|
|
|
transition: border-color 0.15s;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-wizard-back-btn:hover:not(:disabled) {
|
|
|
|
|
border-color: var(--text-muted);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-wizard-back-btn:disabled {
|
|
|
|
|
opacity: 0.35;
|
|
|
|
|
cursor: default;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-wizard-nav-progress {
|
|
|
|
|
flex: 1;
|
|
|
|
|
text-align: center;
|
|
|
|
|
font-size: 0.82em;
|
|
|
|
|
color: var(--text-muted);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-wizard-next-btn {
|
|
|
|
|
padding: 8px 22px;
|
|
|
|
|
margin-left: auto;
|
|
|
|
|
font-size: 0.9em;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* ── Footer ── */
|
|
|
|
|
|
|
|
|
|
.duckmage-wizard-footer {
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
gap: 10px;
|
|
|
|
|
padding-top: 14px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-wizard-footer-sep {
|
|
|
|
|
color: var(--text-faint);
|
|
|
|
|
font-size: 0.8em;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-wizard-dismiss-btn {
|
|
|
|
|
background: none;
|
|
|
|
|
border: none;
|
|
|
|
|
padding: 2px 6px;
|
|
|
|
|
color: var(--text-faint);
|
|
|
|
|
font-size: 0.78em;
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
text-decoration: underline;
|
|
|
|
|
text-underline-offset: 2px;
|
|
|
|
|
transition: color 0.15s;
|
|
|
|
|
white-space: nowrap;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-wizard-dismiss-btn:hover {
|
|
|
|
|
color: var(--text-muted);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* ── Tips list (done step) ── */
|
|
|
|
|
|
|
|
|
|
.duckmage-wizard-tips {
|
|
|
|
|
list-style: none;
|
|
|
|
|
padding: 0;
|
|
|
|
|
margin: 0 0 20px 0;
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
gap: 8px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-wizard-tip-item {
|
|
|
|
|
font-size: 0.9em;
|
|
|
|
|
color: var(--text-normal);
|
|
|
|
|
line-height: 1.5;
|
|
|
|
|
padding-left: 22px;
|
|
|
|
|
position: relative;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-wizard-tip-item::before {
|
|
|
|
|
content: "→";
|
|
|
|
|
position: absolute;
|
|
|
|
|
left: 0;
|
|
|
|
|
color: var(--interactive-accent);
|
|
|
|
|
font-weight: 700;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* ── Done step ── */
|
|
|
|
|
|
|
|
|
|
.duckmage-wizard-summary {
|
|
|
|
|
list-style: none;
|
|
|
|
|
padding: 14px 18px;
|
|
|
|
|
margin: 0 0 22px 0;
|
|
|
|
|
background: var(--background-secondary);
|
|
|
|
|
border-radius: 5px;
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
gap: 6px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-wizard-summary li {
|
|
|
|
|
font-size: 0.9em;
|
|
|
|
|
color: var(--text-normal);
|
|
|
|
|
padding-left: 20px;
|
|
|
|
|
position: relative;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-wizard-summary li::before {
|
|
|
|
|
content: "✓";
|
|
|
|
|
position: absolute;
|
|
|
|
|
left: 0;
|
|
|
|
|
color: var(--color-green, #4caf50);
|
|
|
|
|
font-weight: 700;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-wizard-cta-row {
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
gap: 14px;
|
|
|
|
|
margin-top: 8px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-wizard-open-btn {
|
|
|
|
|
font-size: 0.95em;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-wizard-skip-btn {
|
|
|
|
|
background: none;
|
|
|
|
|
border: 1px solid var(--background-modifier-border);
|
|
|
|
|
padding: 8px 18px;
|
|
|
|
|
border-radius: 5px;
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
color: var(--text-muted);
|
|
|
|
|
font-size: 0.85em;
|
|
|
|
|
transition: border-color 0.15s;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-wizard-skip-btn:hover {
|
|
|
|
|
border-color: var(--text-muted);
|
|
|
|
|
}
|
add token layer: place, edit, and inspect tokens on the hex map
- Tokens placed per-hex link to vault notes; support circle/square shape,
sm/md/lg size, fill color, optional border color, icon, and description
- TokenInfoModal shows note content (formatted via MarkdownRenderer, images
included) with jump-to-hex, open-note, edit, and two-step remove actions
- Edit flow reopens TokenModal pre-filled; proxy notes handle multiple tokens
referencing the same source note
- Icon picker replaced with scrollable palette grid (2-row cap, drag-to-reorder,
hide/show) in both HexEditorModal and TokenModal; order persisted in settings
- Token layer toggle added to layers menu (default on)
- Fix metadata-cache race on token delete (wasToken check prevents missed updates)
- Token preview rendered inline in TokenModal title row; real-time color preview
via native input event on color pickers
- Bump version to 1.1.6
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-22 12:26:20 +00:00
|
|
|
|
|
|
|
|
/* ── Token layer ─────────────────────────────────────────────────────────── */
|
|
|
|
|
|
|
|
|
|
.duckmage-token-layer {
|
|
|
|
|
position: absolute;
|
|
|
|
|
top: 0;
|
|
|
|
|
left: 0;
|
|
|
|
|
pointer-events: none;
|
|
|
|
|
z-index: 10;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-token {
|
|
|
|
|
position: absolute;
|
|
|
|
|
transform: translate(-50%, -50%);
|
|
|
|
|
width: var(--token-size, 2em);
|
|
|
|
|
height: var(--token-size, 2em);
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
justify-content: center;
|
|
|
|
|
background-color: var(--token-color, #4a90e2);
|
|
|
|
|
/* outline renders outside the box, follows border-radius, not clipped by fill */
|
|
|
|
|
outline: 3px solid var(--token-border, transparent);
|
|
|
|
|
outline-offset: 2px;
|
|
|
|
|
cursor: grab;
|
|
|
|
|
pointer-events: auto;
|
|
|
|
|
user-select: none;
|
|
|
|
|
box-shadow: 0 1px 4px rgba(0,0,0,0.45);
|
|
|
|
|
transition: box-shadow 0.1s;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Inline preview inside the token modal title row — overrides .duckmage-token positioning */
|
|
|
|
|
.duckmage-token-modal-title-row .duckmage-token {
|
|
|
|
|
--token-size: 28px;
|
|
|
|
|
position: relative;
|
|
|
|
|
transform: none;
|
|
|
|
|
left: auto;
|
|
|
|
|
top: auto;
|
|
|
|
|
flex-shrink: 0;
|
|
|
|
|
cursor: default;
|
|
|
|
|
pointer-events: none;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-token:hover {
|
|
|
|
|
box-shadow: 0 2px 8px rgba(0,0,0,0.6);
|
2026-05-25 19:55:04 +00:00
|
|
|
z-index: 50;
|
add token layer: place, edit, and inspect tokens on the hex map
- Tokens placed per-hex link to vault notes; support circle/square shape,
sm/md/lg size, fill color, optional border color, icon, and description
- TokenInfoModal shows note content (formatted via MarkdownRenderer, images
included) with jump-to-hex, open-note, edit, and two-step remove actions
- Edit flow reopens TokenModal pre-filled; proxy notes handle multiple tokens
referencing the same source note
- Icon picker replaced with scrollable palette grid (2-row cap, drag-to-reorder,
hide/show) in both HexEditorModal and TokenModal; order persisted in settings
- Token layer toggle added to layers menu (default on)
- Fix metadata-cache race on token delete (wasToken check prevents missed updates)
- Token preview rendered inline in TokenModal title row; real-time color preview
via native input event on color pickers
- Bump version to 1.1.6
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-22 12:26:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-token-circle {
|
|
|
|
|
border-radius: 50%;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-token-square {
|
|
|
|
|
border-radius: 3px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-token-hexagon {
|
|
|
|
|
clip-path: polygon(50% 0%, 100% 25%, 100% 75%, 50% 100%, 0% 75%, 0% 25%);
|
|
|
|
|
border-radius: 0;
|
|
|
|
|
/* outline is clipped by clip-path; stack 8 zero-blur drop-shadows to form a solid ring */
|
|
|
|
|
outline: none;
|
|
|
|
|
filter:
|
|
|
|
|
drop-shadow(0 3px 0 var(--token-border, transparent))
|
|
|
|
|
drop-shadow(0 -3px 0 var(--token-border, transparent))
|
|
|
|
|
drop-shadow(3px 0 0 var(--token-border, transparent))
|
|
|
|
|
drop-shadow(-3px 0 0 var(--token-border, transparent))
|
|
|
|
|
drop-shadow(2.1px 2.1px 0 var(--token-border, transparent))
|
|
|
|
|
drop-shadow(-2.1px 2.1px 0 var(--token-border, transparent))
|
|
|
|
|
drop-shadow(2.1px -2.1px 0 var(--token-border, transparent))
|
|
|
|
|
drop-shadow(-2.1px -2.1px 0 var(--token-border, transparent));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-token-icon {
|
|
|
|
|
width: 70%;
|
|
|
|
|
height: 70%;
|
|
|
|
|
object-fit: contain;
|
|
|
|
|
pointer-events: none;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-token-label {
|
|
|
|
|
font-size: 1em;
|
|
|
|
|
font-weight: 700;
|
|
|
|
|
color: #fff;
|
|
|
|
|
pointer-events: none;
|
|
|
|
|
text-shadow: 0 1px 2px rgba(0,0,0,0.6);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Token size variants */
|
|
|
|
|
.duckmage-token-size-sm { --token-size: 1.35em; }
|
|
|
|
|
.duckmage-token-size-md { --token-size: 2em; }
|
|
|
|
|
.duckmage-token-size-lg { --token-size: 2.85em; }
|
|
|
|
|
|
|
|
|
|
.duckmage-token-dragging {
|
|
|
|
|
cursor: grabbing;
|
|
|
|
|
opacity: 0.85;
|
|
|
|
|
box-shadow: 0 4px 16px rgba(0,0,0,0.6);
|
|
|
|
|
z-index: 20;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Hex highlighted as a drop target when dragging a token */
|
|
|
|
|
.duckmage-token-drop-target::after {
|
|
|
|
|
content: "";
|
|
|
|
|
position: absolute;
|
|
|
|
|
inset: 0;
|
|
|
|
|
background: rgba(255,255,255,0.25);
|
|
|
|
|
pointer-events: none;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Inline note picker results — shown below the Note input on focus */
|
|
|
|
|
.duckmage-note-picker-results {
|
|
|
|
|
border: 1px solid var(--background-modifier-border);
|
|
|
|
|
border-radius: 4px;
|
|
|
|
|
margin: 0 0 8px 0;
|
|
|
|
|
max-height: 180px;
|
|
|
|
|
overflow-y: auto;
|
|
|
|
|
background: var(--background-primary);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-note-picker-item {
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: baseline;
|
|
|
|
|
gap: 6px;
|
|
|
|
|
padding: 5px 10px;
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
font-size: 0.9em;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-note-picker-item:hover {
|
|
|
|
|
background: var(--background-modifier-hover);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-note-picker-empty {
|
|
|
|
|
padding: 6px 10px;
|
|
|
|
|
font-size: 0.85em;
|
|
|
|
|
color: var(--text-muted);
|
|
|
|
|
font-style: italic;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Token size radio group */
|
|
|
|
|
.duckmage-token-size-group {
|
|
|
|
|
display: flex;
|
|
|
|
|
gap: 12px;
|
|
|
|
|
align-items: center;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-token-size-option {
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
gap: 4px;
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
font-size: 0.9em;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-token-desc-textarea {
|
|
|
|
|
width: 100%;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Token modal styles */
|
|
|
|
|
.duckmage-token-modal-buttons {
|
|
|
|
|
display: flex;
|
|
|
|
|
gap: 8px;
|
|
|
|
|
justify-content: flex-end;
|
2026-05-23 18:28:27 +00:00
|
|
|
margin-top: auto;
|
|
|
|
|
padding-top: 16px;
|
add token layer: place, edit, and inspect tokens on the hex map
- Tokens placed per-hex link to vault notes; support circle/square shape,
sm/md/lg size, fill color, optional border color, icon, and description
- TokenInfoModal shows note content (formatted via MarkdownRenderer, images
included) with jump-to-hex, open-note, edit, and two-step remove actions
- Edit flow reopens TokenModal pre-filled; proxy notes handle multiple tokens
referencing the same source note
- Icon picker replaced with scrollable palette grid (2-row cap, drag-to-reorder,
hide/show) in both HexEditorModal and TokenModal; order persisted in settings
- Token layer toggle added to layers menu (default on)
- Fix metadata-cache race on token delete (wasToken check prevents missed updates)
- Token preview rendered inline in TokenModal title row; real-time color preview
via native input event on color pickers
- Bump version to 1.1.6
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-22 12:26:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-token-modal-title {
|
|
|
|
|
font-weight: 600;
|
|
|
|
|
font-size: 1.05em;
|
|
|
|
|
margin: 0 0 12px 0;
|
|
|
|
|
color: var(--text-normal);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-token-info-content {
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
min-height: 200px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-token-info-note-wrap {
|
|
|
|
|
margin-top: 8px;
|
|
|
|
|
margin-bottom: 14px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-token-info-note-label {
|
|
|
|
|
display: block;
|
|
|
|
|
font-size: 0.78em;
|
|
|
|
|
font-weight: 600;
|
|
|
|
|
text-transform: uppercase;
|
|
|
|
|
letter-spacing: 0.04em;
|
|
|
|
|
color: var(--text-muted);
|
|
|
|
|
margin-bottom: 4px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-token-info-note-rendered {
|
|
|
|
|
min-height: 140px;
|
|
|
|
|
max-height: 320px;
|
|
|
|
|
overflow-y: auto;
|
|
|
|
|
font-size: 0.88em;
|
|
|
|
|
border: 1px solid var(--background-modifier-border);
|
|
|
|
|
border-radius: 4px;
|
|
|
|
|
padding: 6px 10px;
|
|
|
|
|
box-sizing: border-box;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-token-info-note-rendered img {
|
|
|
|
|
max-width: 100%;
|
|
|
|
|
height: auto;
|
|
|
|
|
border-radius: 2px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-token-info-empty {
|
|
|
|
|
color: var(--text-muted);
|
|
|
|
|
font-style: italic;
|
|
|
|
|
margin: 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-token-info-loc {
|
|
|
|
|
color: var(--text-muted);
|
|
|
|
|
font-size: 0.85em;
|
|
|
|
|
margin: 2px 0 6px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-token-info-desc {
|
|
|
|
|
font-size: 0.95em;
|
|
|
|
|
color: var(--text-normal);
|
|
|
|
|
margin-bottom: 12px;
|
|
|
|
|
white-space: pre-wrap;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-token-info-edit-btn {
|
|
|
|
|
font-size: 0.8em;
|
|
|
|
|
padding: 2px 8px;
|
|
|
|
|
flex-shrink: 0;
|
|
|
|
|
align-self: center;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-token-info-remove-btn {
|
|
|
|
|
margin-top: auto;
|
|
|
|
|
align-self: flex-start;
|
|
|
|
|
font-size: 0.8em;
|
|
|
|
|
padding: 2px 8px;
|
|
|
|
|
color: var(--text-error);
|
|
|
|
|
background: transparent;
|
|
|
|
|
border: 1px solid var(--text-error);
|
|
|
|
|
border-radius: 4px;
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
opacity: 0.7;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-token-info-remove-btn:hover {
|
|
|
|
|
opacity: 1;
|
|
|
|
|
background: color-mix(in srgb, var(--text-error) 10%, transparent);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-token-info-remove-btn--confirming {
|
|
|
|
|
opacity: 1;
|
|
|
|
|
background: color-mix(in srgb, var(--text-error) 18%, transparent);
|
|
|
|
|
border-color: var(--text-error);
|
|
|
|
|
}
|
2026-05-25 00:14:32 +00:00
|
|
|
|
|
|
|
|
/* ── Painter tool context menu ───────────────────────────────────────────── */
|
|
|
|
|
|
|
|
|
|
.duckmage-painter-ctx-menu {
|
|
|
|
|
position: fixed;
|
|
|
|
|
z-index: 1000;
|
|
|
|
|
background: var(--background-primary);
|
|
|
|
|
border: 1px solid var(--background-modifier-border);
|
|
|
|
|
border-radius: 6px;
|
|
|
|
|
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.18);
|
|
|
|
|
padding: 4px 0;
|
|
|
|
|
min-width: 130px;
|
|
|
|
|
font-size: var(--font-ui-small);
|
|
|
|
|
user-select: none;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-painter-ctx-item {
|
|
|
|
|
padding: 6px 14px;
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
color: var(--text-normal);
|
|
|
|
|
white-space: nowrap;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-painter-ctx-item:hover {
|
|
|
|
|
background: var(--background-modifier-hover);
|
|
|
|
|
color: var(--text-accent);
|
|
|
|
|
}
|
2026-05-31 13:16:19 +00:00
|
|
|
|
|
|
|
|
/* ── Stagger offset toggle button ────────────────────────────────────────── */
|
|
|
|
|
|
|
|
|
|
.duckmage-stagger-toggle {
|
|
|
|
|
font-size: 0.8em;
|
|
|
|
|
padding: 2px 14px;
|
|
|
|
|
border-radius: 999px;
|
|
|
|
|
border: 1px solid var(--background-modifier-border);
|
|
|
|
|
background: transparent;
|
|
|
|
|
color: var(--text-muted);
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
min-width: 54px;
|
|
|
|
|
transition: background 0.12s, color 0.12s, border-color 0.12s;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-stagger-toggle:hover {
|
|
|
|
|
border-color: var(--interactive-accent);
|
|
|
|
|
color: var(--interactive-accent);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.duckmage-stagger-toggle.is-even {
|
|
|
|
|
background: var(--interactive-accent);
|
|
|
|
|
color: var(--text-on-accent);
|
|
|
|
|
border-color: var(--interactive-accent);
|
|
|
|
|
}
|
feat: export module — PDF/Markdown for notes, hexes, maps, tables, workflows (1.2.0)
Adds a unified export pipeline under src/export/ with exporters for single notes,
structured hexes, maps (with reference table), random tables, and workflows
(with rolled samples). PDF rendering reuses Obsidian's MarkdownRenderer + theme
CSS; map PNG/PDF goes through a dedicated canvas renderer.
UI surface:
- "Export" tab in MapModal (PNG/PDF with overlay + size options)
- "Export" link in HexEditorModal + HexExportModal
- "Export PDF/Markdown" links in RandomTableView header
- "Export…" button in WorkflowEditorModal + WorkflowExportModal
- Commands: export current note (PDF/MD), current hex, current workflow
- File-menu items on any .md file: Export to PDF / Markdown
Other:
- New `exportFolder` setting (defaults to {worldFolder}/exports)
- hexGeometry: add hexSize / hexCenter / hexPolygonPoints / gridBoundingBox
- HexTableView + HexEditorModal: first-wins on duplicate palette names
(matches HexMapView's .find() behaviour)
- Tests: unit + integration suites for all exporters; sim-vault fixture
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-06-06 00:46:41 +00:00
|
|
|
|
|
|
|
|
/* Export — hidden render host for printToPDF */
|
|
|
|
|
.duckmage-export-webview {
|
|
|
|
|
position: fixed;
|
|
|
|
|
left: -10000px;
|
|
|
|
|
top: -10000px;
|
|
|
|
|
width: 850px;
|
|
|
|
|
height: 1100px;
|
|
|
|
|
visibility: hidden;
|
|
|
|
|
pointer-events: none;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Export — off-screen render host for MarkdownRenderer */
|
|
|
|
|
.duckmage-export-render-host {
|
|
|
|
|
position: fixed;
|
|
|
|
|
left: -10000px;
|
|
|
|
|
top: -10000px;
|
|
|
|
|
width: 850px;
|
|
|
|
|
visibility: hidden;
|
|
|
|
|
pointer-events: none;
|
|
|
|
|
}
|
2026-06-06 03:04:11 +00:00
|
|
|
|
|
|
|
|
/* ── JS-driven custom-property consumers ────────────────────────────────────
|
|
|
|
|
Dynamic colors (terrain swatches, path swatches, etc.) are set as CSS
|
|
|
|
|
custom properties via setCssProps; these rules consume them. Fallbacks
|
|
|
|
|
keep the default appearance when the variable is unset.
|
|
|
|
|
─────────────────────────────────────────────────────────────────────────── */
|
|
|
|
|
.duckmage-terrain-header-swatch { background-color: var(--duckmage-bg, transparent); }
|
|
|
|
|
.duckmage-terrain-preview { background-color: var(--duckmage-bg, transparent); }
|
|
|
|
|
.duckmage-terrain-btn-preview { background-color: var(--duckmage-bg, transparent); }
|
|
|
|
|
.duckmage-path-btn-swatch { background-color: var(--duckmage-bg, transparent); }
|
|
|
|
|
.duckmage-draw-btn-terrain.is-terrain-preview {
|
|
|
|
|
border-color: var(--duckmage-border-color, currentcolor);
|
|
|
|
|
}
|
|
|
|
|
.duckmage-draw-btn-terrain.is-eyedropper-active {
|
|
|
|
|
border-color: var(--interactive-accent);
|
|
|
|
|
color: var(--interactive-accent);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Tinted icon helper — utils.ts createIconEl applies this class and sets
|
|
|
|
|
--duckmage-mask-url + --duckmage-bg. */
|
|
|
|
|
.duckmage-masked-icon {
|
|
|
|
|
-webkit-mask-image: var(--duckmage-mask-url);
|
|
|
|
|
-webkit-mask-size: contain;
|
|
|
|
|
-webkit-mask-repeat: no-repeat;
|
|
|
|
|
-webkit-mask-position: center;
|
|
|
|
|
mask-image: var(--duckmage-mask-url);
|
|
|
|
|
mask-size: contain;
|
|
|
|
|
mask-repeat: no-repeat;
|
|
|
|
|
mask-position: center;
|
|
|
|
|
background-color: var(--duckmage-bg);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Static-layout helpers replacing inline setCssProps calls in workflows view */
|
|
|
|
|
.duckmage-rt-roll-btn-spaced { margin-right: 8px; }
|
|
|
|
|
.duckmage-rt-copy-link-btn-spaced { margin-bottom: 12px; }
|
|
|
|
|
.duckmage-wf-steps-list { margin: 0; padding-left: 18px; }
|
|
|
|
|
.duckmage-rt-new-wf-link { font-style: italic; }
|
|
|
|
|
.duckmage-wf-template-validation { margin-top: 4px; font-size: 0.85em; }
|
|
|
|
|
.duckmage-wf-editor-results-row { margin-top: 8px; }
|
2026-06-21 17:00:32 +00:00
|
|
|
|
|
|
|
|
/* Inline numeric readout shown beside a settings slider. */
|
|
|
|
|
.duckmage-slider-value {
|
|
|
|
|
display: inline-block;
|
|
|
|
|
min-width: 2.5em;
|
|
|
|
|
margin-right: 8px;
|
|
|
|
|
text-align: right;
|
|
|
|
|
font-variant-numeric: tabular-nums;
|
|
|
|
|
color: var(--text-muted);
|
|
|
|
|
}
|