Merge dev into main (v0.31.0)
MindSift CI & Docker Release / Build, Test in Container & Push (push) Successful in 28s
MindSift CI & Docker Release / Build, Test in Container & Push (push) Successful in 28s
This commit is contained in:
commit
f2c92c4bec
11 files changed
+219
-77
No files matched your search
+7
-1
@@ -6,6 +6,11 @@ Das Format folgt [Keep a Changelog](https://keepachangelog.com/de/1.1.0/), die V
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
## [0.31.0] - 2026-11-01
|
||||
|
||||
### Added
|
||||
- Kanban: Die Farbe jeder Spalte lässt sich einstellen. In „Spalten anpassen“ öffnet der Farbpunkt vor dem Namen die Auswahl: Grau, Anthrazit, Grün, Gelb, Blau, Rot, Lila oder die Akzentfarbe des Themes. Die Farbe erscheint im Spaltenkopf, als Statuspunkt in der Listenansicht und im Detail-Fenster. Blau und Lila passen sich hellen und dunklen Themes an.
|
||||
|
||||
## [0.30.0] - 2026-11-01
|
||||
|
||||
### Added
|
||||
@@ -577,7 +582,8 @@ Das Format folgt [Keep a Changelog](https://keepachangelog.com/de/1.1.0/), die V
|
||||
- Geteilte Listen (Einkaufs-, Pack- und Checklisten) mit `#Kategorie`-Gruppierung, Mengenangaben und Echtzeit-Abhaken.
|
||||
- Benutzer-, Gruppen- und Admin-Verwaltung (Ersteinrichtungs-Assistent, Einladungscodes, Profilbilder, persönliche API-Keys & Webhook-Ingest `/api/v1/webhooks/ingest`, JSON-Datenexport) sowie CSS-Theme-Engine mit 4 Themes (`Dark`, `Light`, `High Contrast`, `Soft`) und PWA-Unterstützung.
|
||||
|
||||
[Unreleased]: https://git.dsit.me/sandrodach/MindSift/compare/v0.30.0...dev
|
||||
[Unreleased]: https://git.dsit.me/sandrodach/MindSift/compare/v0.31.0...dev
|
||||
[0.31.0]: https://git.dsit.me/sandrodach/MindSift/compare/v0.30.0...v0.31.0
|
||||
[0.30.0]: https://git.dsit.me/sandrodach/MindSift/compare/v0.29.0...v0.30.0
|
||||
[0.29.0]: https://git.dsit.me/sandrodach/MindSift/compare/v0.28.0...v0.29.0
|
||||
[0.28.0]: https://git.dsit.me/sandrodach/MindSift/compare/v0.27.1...v0.28.0
|
||||
|
||||
@@ -9,7 +9,7 @@ Eine Aufgabe ist ein `thought` ([database.md](../database.md)) mit `kanbanStatus
|
||||
## Ansichten
|
||||
|
||||
- **Liste** mit Filtern (offen, erledigt, Papierkorb), Gruppierung (z. B. nach Status oder Fälligkeit), Sortierung und Hashtag-Filter.
|
||||
- **Kanban:** Spalten pro Liste frei konfigurierbar (Name, Farbe, „Erledigt“-Spalte). Standard: *Zu erledigen*, *In Bearbeitung*, *Wartet auf andere*, *Erledigt*. Eine Karte in eine „Erledigt“-Spalte zu ziehen setzt `isCompleted`.
|
||||
- **Kanban:** Spalten pro Liste frei konfigurierbar (Name, Farbe, „Erledigt“-Spalte). Farben in „Spalten anpassen“ über den Punkt vor dem Namen: Grau, Anthrazit (Textfarbe des Themes), Grün, Gelb, Blau, Rot, Lila, Akzentfarbe (`columns[].color` = `gray`/`slate`/`green`/`yellow`/`blue`/`red`/`purple`/`accent`; ältere Werte `emerald`/`amber` gelten als Grün/Gelb, Spalten ohne Farbe behalten die ihrer Rolle). Der Punkt erscheint im Spaltenkopf, vor dem Status in der Listenansicht und im Detail-Fenster ([ManageColumnsModal.tsx](../../frontend/src/features/triage/ManageColumnsModal.tsx), `columnDotClass`). Blau und Lila kommen aus `--color-info` / `--color-purple`; Themes können sie setzen, sonst gelten Standardwerte aus `index.css`. Standard: *Zu erledigen*, *In Bearbeitung*, *Wartet auf andere*, *Erledigt*. Eine Karte in eine „Erledigt“-Spalte zu ziehen setzt `isCompleted`.
|
||||
- Drag & Drop sortiert in der Liste und zwischen Spalten; ein Klick öffnet das Detail-Fenster (Titel, Status, Priorität, Fälligkeit mit optionaler Uhrzeit und Schnellwahl, Liste, Hashtags, Unteraufgaben mit Fortschrittsbalken, Notizen).
|
||||
- Aufgaben lassen sich in eine Notiz umwandeln („Als Notiz vertiefen“). Aufgaben aus Notiz-Checkboxen (`enhanced-task`) bleiben verknüpft: Abhaken wirkt in beide Richtungen (`SyncDocBlockAndTask`), „In Notiz öffnen“ springt zum Block.
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
[](#)
|
||||
[](#-schnellstart)
|
||||
[](#-deine-daten-gehören-dir)
|
||||
[](CHANGELOG.md)
|
||||
[](CHANGELOG.md)
|
||||
|
||||
**MindSift** ist eine selbst gehostete Schaltstelle für spontane Gedanken, Aufgaben mit Kanban-Board, hierarchische Outliner-Notizen, die mehrere Personen gleichzeitig bearbeiten können, und gemeinsame Listen. Alles läuft als ein einzelner Container mit eingebetteter SQLite-Datenbank, auf deinem eigenen Server.
|
||||
|
||||
|
||||
Generated
+2
-2
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "mindsift-frontend",
|
||||
"version": "0.30.0",
|
||||
"version": "0.31.0",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "mindsift-frontend",
|
||||
"version": "0.30.0",
|
||||
"version": "0.31.0",
|
||||
"license": "Unlicense",
|
||||
"dependencies": {
|
||||
"@fontsource-variable/inter": "5.3.0",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "mindsift-frontend",
|
||||
"private": true,
|
||||
"version": "0.30.0",
|
||||
"version": "0.31.0",
|
||||
"license": "Unlicense",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import React, { useState, useEffect, useRef } from 'react';
|
||||
import {
|
||||
X,
|
||||
Plus,
|
||||
@@ -28,17 +28,32 @@ export const DEFAULT_KANBAN_COLUMNS: KanbanColumn[] = [
|
||||
];
|
||||
|
||||
// Wrapper and content split so the hooks always run in the same number (see TaskDetailModal)
|
||||
// Dot colour of a status column (Kanban header, list view); fixed by column role for now
|
||||
// Colours a column can get; all come from the theme (blue and purple via --color-info / --color-purple)
|
||||
export const COLUMN_COLORS: { id: string; label: string; cls: string }[] = [
|
||||
{ id: 'gray', label: 'Grau', cls: 'bg-faint' },
|
||||
{ id: 'slate', label: 'Anthrazit', cls: 'bg-main' },
|
||||
{ id: 'green', label: 'Grün', cls: 'bg-status-ok' },
|
||||
{ id: 'yellow', label: 'Gelb', cls: 'bg-status-warn' },
|
||||
{ id: 'blue', label: 'Blau', cls: 'bg-tone-blue' },
|
||||
{ id: 'red', label: 'Rot', cls: 'bg-status-err' },
|
||||
{ id: 'purple', label: 'Lila', cls: 'bg-tone-purple' },
|
||||
{ id: 'accent', label: 'Akzentfarbe', cls: 'bg-accent' },
|
||||
];
|
||||
// Names the server's default columns use
|
||||
const COLOR_ALIASES: Record<string, string> = { emerald: 'green', amber: 'yellow' };
|
||||
|
||||
const columnColorId = (col: KanbanColumn): string => {
|
||||
if (col.color) return COLOR_ALIASES[col.color] || col.color;
|
||||
// Columns without a colour (older boards) keep the colour of their role
|
||||
if (col.isDone) return 'green';
|
||||
if (col.id === 'in_progress') return 'accent';
|
||||
if (col.id === 'waiting') return 'yellow';
|
||||
return 'gray';
|
||||
};
|
||||
|
||||
// Dot colour of a status column (Kanban header, list view, task detail)
|
||||
export const columnDotClass = (col: KanbanColumn | undefined): string =>
|
||||
!col
|
||||
? 'bg-faint'
|
||||
: col.isDone
|
||||
? 'bg-status-ok'
|
||||
: col.id === 'in_progress'
|
||||
? 'bg-accent'
|
||||
: col.id === 'waiting'
|
||||
? 'bg-status-warn'
|
||||
: 'bg-faint';
|
||||
(col && COLUMN_COLORS.find((c) => c.id === columnColorId(col))?.cls) || 'bg-faint';
|
||||
|
||||
export const ManageColumnsModal: React.FC<ManageColumnsModalProps> = (props) =>
|
||||
props.isOpen ? <ManageColumnsModalContent {...props} /> : null;
|
||||
@@ -56,6 +71,44 @@ const ManageColumnsModalContent: React.FC<ManageColumnsModalProps> = ({
|
||||
const [newColName, setNewColName] = useState('');
|
||||
const [newColIsDone, setNewColIsDone] = useState(false);
|
||||
const [saving, setSaving] = useState(false);
|
||||
// Colour menu: opens at the column's dot like a context menu (fixed, so the scrolling list cannot clip it)
|
||||
const [colorMenu, setColorMenu] = useState<{ id: string; left: number; top?: number; bottom?: number } | null>(null);
|
||||
const colorMenuRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (!colorMenu) return;
|
||||
const close = (e: Event) => {
|
||||
const target = e.target as HTMLElement;
|
||||
if (colorMenuRef.current?.contains(target) || target.closest?.('[data-color-trigger]')) return;
|
||||
setColorMenu(null);
|
||||
};
|
||||
const onKey = (e: KeyboardEvent) => e.key === 'Escape' && setColorMenu(null);
|
||||
const dismiss = () => setColorMenu(null);
|
||||
document.addEventListener('pointerdown', close);
|
||||
document.addEventListener('keydown', onKey);
|
||||
window.addEventListener('resize', dismiss);
|
||||
return () => {
|
||||
document.removeEventListener('pointerdown', close);
|
||||
document.removeEventListener('keydown', onKey);
|
||||
window.removeEventListener('resize', dismiss);
|
||||
};
|
||||
}, [colorMenu]);
|
||||
|
||||
const openColorMenu = (id: string, trigger: HTMLElement) => {
|
||||
if (colorMenu?.id === id) {
|
||||
setColorMenu(null);
|
||||
return;
|
||||
}
|
||||
const rect = trigger.getBoundingClientRect();
|
||||
const menuHeight = COLUMN_COLORS.length * 36 + 12;
|
||||
const left = Math.min(rect.left, window.innerWidth - 200);
|
||||
// Below the dot if there is room, else above it
|
||||
setColorMenu(
|
||||
rect.bottom + menuHeight + 8 < window.innerHeight
|
||||
? { id, left, top: rect.bottom + 4 }
|
||||
: { id, left, bottom: window.innerHeight - rect.top + 4 }
|
||||
);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
setColumns(initialColumns && initialColumns.length > 0 ? initialColumns : DEFAULT_KANBAN_COLUMNS);
|
||||
@@ -78,6 +131,12 @@ const ManageColumnsModalContent: React.FC<ManageColumnsModalProps> = ({
|
||||
);
|
||||
};
|
||||
|
||||
const handleColor = (id: string, color: string) => {
|
||||
triggerHaptic(10);
|
||||
setColumns((prev) => prev.map((c) => (c.id === id ? { ...c, color } : c)));
|
||||
setColorMenu(null);
|
||||
};
|
||||
|
||||
const handleToggleDone = (id: string) => {
|
||||
triggerHaptic(10);
|
||||
setColumns((prev) =>
|
||||
@@ -172,65 +231,77 @@ const ManageColumnsModalContent: React.FC<ManageColumnsModalProps> = ({
|
||||
{/* Columns List */}
|
||||
<div className="space-y-2">
|
||||
<label className="block text-xs font-semibold text-main">Aktive Spalten ({columns.length}):</label>
|
||||
<div className="space-y-1.5 max-h-60 overflow-y-auto pr-1">
|
||||
<div className="space-y-1.5 max-h-[45vh] overflow-y-auto pr-1" onScroll={() => setColorMenu(null)}>
|
||||
{columns.map((col, idx) => (
|
||||
<div
|
||||
key={col.id}
|
||||
className="p-2 bg-canvas border border-border rounded-card flex items-center gap-2"
|
||||
>
|
||||
<div className="flex flex-col gap-0.5">
|
||||
<div key={col.id} className="p-2 bg-canvas border border-border rounded-card space-y-2">
|
||||
<div className="flex items-center gap-2">
|
||||
<div className="flex flex-col gap-0.5">
|
||||
<button
|
||||
type="button"
|
||||
disabled={idx === 0}
|
||||
onClick={() => handleMove(idx, 'up')}
|
||||
className="text-muted hover:text-main disabled:opacity-20 p-0.5"
|
||||
title="Nach oben"
|
||||
>
|
||||
<ArrowUp className="w-3 h-3" />
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
disabled={idx === columns.length - 1}
|
||||
onClick={() => handleMove(idx, 'down')}
|
||||
className="text-muted hover:text-main disabled:opacity-20 p-0.5"
|
||||
title="Nach unten"
|
||||
>
|
||||
<ArrowDown className="w-3 h-3" />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<button
|
||||
type="button"
|
||||
disabled={idx === 0}
|
||||
onClick={() => handleMove(idx, 'up')}
|
||||
className="text-muted hover:text-main disabled:opacity-20 p-0.5"
|
||||
title="Nach oben"
|
||||
data-color-trigger
|
||||
onClick={(e) => openColorMenu(col.id, e.currentTarget)}
|
||||
className="w-7 h-7 flex items-center justify-center rounded-btn border border-border bg-panel hover:border-strong flex-shrink-0"
|
||||
title="Farbe der Spalte"
|
||||
aria-label={`Farbe der Spalte ${col.name}`}
|
||||
aria-haspopup="menu"
|
||||
aria-expanded={colorMenu?.id === col.id}
|
||||
>
|
||||
<ArrowUp className="w-3 h-3" />
|
||||
<span className={`w-3 h-3 rounded-full ${columnDotClass(col)}`} />
|
||||
</button>
|
||||
|
||||
<input
|
||||
type="text"
|
||||
value={col.name}
|
||||
onChange={(e) => handleRename(col.id, e.target.value)}
|
||||
className="flex-1 h-7 px-2 text-xs input-field"
|
||||
placeholder="Spaltenname"
|
||||
/>
|
||||
|
||||
{/* IsDone Toggle */}
|
||||
<button
|
||||
type="button"
|
||||
disabled={idx === columns.length - 1}
|
||||
onClick={() => handleMove(idx, 'down')}
|
||||
className="text-muted hover:text-main disabled:opacity-20 p-0.5"
|
||||
title="Nach unten"
|
||||
onClick={() => handleToggleDone(col.id)}
|
||||
className={`h-7 px-2 text-[10px] rounded-btn border transition-colors flex items-center gap-1 ${
|
||||
col.isDone
|
||||
? 'bg-status-ok/15 border-status-ok text-status-ok font-semibold'
|
||||
: 'bg-panel border-border text-faint hover:text-muted'
|
||||
}`}
|
||||
title={col.isDone ? 'Gilt als Erledigt' : 'Gilt als Offen'}
|
||||
>
|
||||
<ArrowDown className="w-3 h-3" />
|
||||
<Check className={`w-2.5 h-2.5 ${col.isDone ? 'stroke-[3]' : 'opacity-40'}`} />
|
||||
<span>Erledigt</span>
|
||||
</button>
|
||||
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => handleDelete(col.id)}
|
||||
disabled={columns.length <= 1}
|
||||
className="text-muted hover:text-status-err disabled:opacity-20 p-1 transition-colors"
|
||||
title="Spalte löschen"
|
||||
>
|
||||
<Trash2 className="w-3.5 h-3.5" />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<input
|
||||
type="text"
|
||||
value={col.name}
|
||||
onChange={(e) => handleRename(col.id, e.target.value)}
|
||||
className="flex-1 h-7 px-2 text-xs input-field"
|
||||
placeholder="Spaltenname"
|
||||
/>
|
||||
|
||||
{/* IsDone Toggle */}
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => handleToggleDone(col.id)}
|
||||
className={`h-7 px-2 text-[10px] rounded-btn border transition-colors flex items-center gap-1 ${
|
||||
col.isDone
|
||||
? 'bg-status-ok/15 border-status-ok text-status-ok font-semibold'
|
||||
: 'bg-panel border-border text-faint hover:text-muted'
|
||||
}`}
|
||||
title={col.isDone ? 'Gilt als Erledigt' : 'Gilt als Offen'}
|
||||
>
|
||||
<Check className={`w-2.5 h-2.5 ${col.isDone ? 'stroke-[3]' : 'opacity-40'}`} />
|
||||
<span>Erledigt</span>
|
||||
</button>
|
||||
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => handleDelete(col.id)}
|
||||
disabled={columns.length <= 1}
|
||||
className="text-muted hover:text-status-err disabled:opacity-20 p-1 transition-colors"
|
||||
title="Spalte löschen"
|
||||
>
|
||||
<Trash2 className="w-3.5 h-3.5" />
|
||||
</button>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
@@ -303,6 +374,42 @@ const ManageColumnsModalContent: React.FC<ManageColumnsModalProps> = ({
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{colorMenu &&
|
||||
(() => {
|
||||
const col = columns.find((c) => c.id === colorMenu.id);
|
||||
if (!col) return null;
|
||||
return (
|
||||
<div
|
||||
ref={colorMenuRef}
|
||||
role="menu"
|
||||
aria-label={`Farbe der Spalte ${col.name}`}
|
||||
style={{ left: colorMenu.left, top: colorMenu.top, bottom: colorMenu.bottom }}
|
||||
className="fixed z-[60] w-44 p-1 bg-panel border border-border rounded-card shadow-panel"
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
>
|
||||
{COLUMN_COLORS.map((c) => {
|
||||
const active = columnColorId(col) === c.id;
|
||||
return (
|
||||
<button
|
||||
key={c.id}
|
||||
type="button"
|
||||
role="menuitemradio"
|
||||
aria-checked={active}
|
||||
onClick={() => handleColor(col.id, c.id)}
|
||||
className={`w-full h-11 sm:h-8 px-2 rounded-btn text-xs flex items-center gap-2 text-left transition-colors ${
|
||||
active ? 'bg-accent/10 text-main' : 'text-muted hover:text-main hover:bg-panel-hover'
|
||||
}`}
|
||||
>
|
||||
<span className={`w-3 h-3 rounded-full flex-shrink-0 ${c.cls}`} />
|
||||
<span className="flex-1">{c.label}</span>
|
||||
{active && <Check className="w-3 h-3 text-accent stroke-[3]" />}
|
||||
</button>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
})()}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -20,6 +20,7 @@ import {
|
||||
import { Thought, TaskList, TaskListMember, KanbanColumn, User, Recurrence } from '../../lib/types';
|
||||
import { UserAvatar } from '../../components/UserAvatar';
|
||||
import { OriginBadge, originDetails } from '../../components/OriginBadge';
|
||||
import { columnDotClass } from './ManageColumnsModal';
|
||||
import { UserAutocomplete } from '../UserAutocomplete';
|
||||
import { api, triggerHaptic } from '../../lib/api';
|
||||
import { MarkdownViewer } from '../../lib/markdown';
|
||||
@@ -409,17 +410,7 @@ const TaskDetailModalContent: React.FC<TaskDetailModalProps & { task: Thought }>
|
||||
: 'bg-canvas border-border text-muted hover:text-main hover:border-strong'
|
||||
}`}
|
||||
>
|
||||
<span
|
||||
className={`w-2 h-2 rounded-full ${
|
||||
col.isDone
|
||||
? 'bg-status-ok'
|
||||
: col.id === 'in_progress'
|
||||
? 'bg-accent'
|
||||
: col.id === 'waiting'
|
||||
? 'bg-status-warn'
|
||||
: 'bg-faint'
|
||||
}`}
|
||||
/>
|
||||
<span className={`w-2 h-2 rounded-full ${columnDotClass(col)}`} />
|
||||
<span>{col.name}</span>
|
||||
{isActive && <Check className="w-3 h-3 text-accent stroke-[3] ml-0.5" />}
|
||||
</button>
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import { columnDotClass } from '../ManageColumnsModal';
|
||||
|
||||
describe('columnDotClass', () => {
|
||||
it('uses the chosen colour, also the names of the server defaults', () => {
|
||||
expect(columnDotClass({ id: 'x', name: 'Review', color: 'purple' })).toBe('bg-tone-purple');
|
||||
expect(columnDotClass({ id: 'x', name: 'Blockiert', color: 'red' })).toBe('bg-status-err');
|
||||
expect(columnDotClass({ id: 'waiting', name: 'Wartet', color: 'amber' })).toBe('bg-status-warn');
|
||||
expect(columnDotClass({ id: 'done', name: 'Erledigt', color: 'emerald', isDone: true })).toBe('bg-status-ok');
|
||||
});
|
||||
|
||||
it('keeps the colour of the role for columns without one', () => {
|
||||
expect(columnDotClass({ id: 'in_progress', name: 'In Arbeit' })).toBe('bg-accent');
|
||||
expect(columnDotClass({ id: 'x', name: 'Fertig', isDone: true })).toBe('bg-status-ok');
|
||||
expect(columnDotClass({ id: 'x', name: 'Neu' })).toBe('bg-faint');
|
||||
expect(columnDotClass(undefined)).toBe('bg-faint');
|
||||
});
|
||||
});
|
||||
@@ -431,6 +431,17 @@ body {
|
||||
--code-variable: #9cdcfe;
|
||||
}
|
||||
|
||||
/* Extra tones (Kanban column colours). Fallbacks like the code tokens: a theme's own values win. */
|
||||
:where(:root) {
|
||||
--color-info: #60a5fa;
|
||||
--color-purple: #a78bfa;
|
||||
}
|
||||
|
||||
:where(:root:not(.dark)) {
|
||||
--color-info: #2563eb;
|
||||
--color-purple: #7c3aed;
|
||||
}
|
||||
|
||||
:where(:root:not(.dark)) {
|
||||
--code-keyword: #0000ff;
|
||||
--code-string: #a31515;
|
||||
|
||||
@@ -49,6 +49,10 @@
|
||||
--bg-warning: rgba(245, 158, 11, 0.12);
|
||||
--border-warning: rgba(245, 158, 11, 0.38);
|
||||
|
||||
/* Optional: Blau und Lila für Kanban-Spalten (ohne Angabe gelten Standardwerte) */
|
||||
--color-info: #60a5fa;
|
||||
--color-purple: #a78bfa;
|
||||
|
||||
/* --- 6. RUNDUNGEN (Geometrie) --- */
|
||||
--radius-card: 0px; /* Karten, Panels, Container (0px = eckig) */
|
||||
--radius-btn: 2px; /* Buttons & Badges */
|
||||
|
||||
@@ -59,6 +59,11 @@ export default {
|
||||
info: v('--accent-hover'),
|
||||
danger: v('--color-danger'),
|
||||
},
|
||||
// Extra tones for Kanban columns; fallbacks in index.css, themes may set their own
|
||||
tone: {
|
||||
blue: v('--color-info'),
|
||||
purple: v('--color-purple'),
|
||||
},
|
||||
border: v('--border-color'),
|
||||
// Aliases for names used in components (e.g. avatar initials), mapped to theme colours
|
||||
primary: v('--accent-primary'),
|
||||
|
||||
Reference in new issue
Block a user