diff --git a/api/kanban_bridge.py b/api/kanban_bridge.py
index 0ba9ff32..bd8dfaa9 100644
--- a/api/kanban_bridge.py
+++ b/api/kanban_bridge.py
@@ -20,6 +20,7 @@ from dataclasses import asdict, is_dataclass
from urllib.parse import parse_qs, unquote
from api.helpers import bad, j
+from api.workspace import resolve_trusted_workspace
BOARD_COLUMNS = ["triage", "todo", "ready", "running", "blocked", "done"]
_TASK_PREFIX = "/api/kanban/tasks/"
@@ -855,6 +856,13 @@ def _create_board_payload(body):
slug = str(body.get("slug") or "").strip()
if not slug:
raise ValueError("slug is required")
+ board_kwargs = {}
+ if "default_workdir" in body:
+ raw_workdir = str(body.get("default_workdir") or "").strip()
+ if raw_workdir:
+ board_kwargs["default_workdir"] = str(resolve_trusted_workspace(raw_workdir))
+ else:
+ board_kwargs["default_workdir"] = ""
try:
meta = kb.create_board(
slug,
@@ -862,6 +870,7 @@ def _create_board_payload(body):
description=body.get("description") or None,
icon=body.get("icon") or None,
color=body.get("color") or None,
+ **board_kwargs,
)
except (ValueError, AttributeError) as exc:
raise ValueError(str(exc)) from exc
@@ -894,6 +903,10 @@ def _update_board_payload(slug, body):
raise ValueError(f"invalid board slug: {slug!r}") from exc
if not normed or not kb.board_exists(normed):
raise LookupError(f"board {slug!r} does not exist")
+ board_kwargs = {}
+ if "default_workdir" in body:
+ raw_workdir = str(body.get("default_workdir") or "").strip()
+ board_kwargs["default_workdir"] = str(resolve_trusted_workspace(raw_workdir)) if raw_workdir else ""
archived = body.get("archived")
if isinstance(archived, str):
archived = archived.strip().lower() in {"1", "true", "yes", "on"}
@@ -904,6 +917,7 @@ def _update_board_payload(slug, body):
icon=body.get("icon"),
color=body.get("color"),
archived=archived if isinstance(archived, bool) else None,
+ **board_kwargs,
)
return {"board": _board_meta_dict(meta), "read_only": False}
diff --git a/static/i18n.js b/static/i18n.js
index cca91772..120e62f4 100644
--- a/static/i18n.js
+++ b/static/i18n.js
@@ -1052,6 +1052,8 @@ const LOCALES = {
kanban_board_description: 'Description (optional)',
kanban_board_icon: 'Icon (emoji, optional)',
kanban_board_color: 'Color (optional)',
+ kanban_board_default_workdir: 'Default workdir',
+ kanban_board_default_workdir_placeholder: 'Workspace path (optional)',
kanban_board_name_required: 'Name is required',
kanban_board_slug_required: 'Slug is required',
kanban_card_complete: 'complete',
@@ -2814,6 +2816,8 @@ const LOCALES = {
kanban_board_description: 'Descrizione (opzionale)',
kanban_board_icon: 'Icona (emoji, opzionale)',
kanban_board_color: 'Colore (opzionale)',
+ kanban_board_default_workdir: 'Workspace predefinito',
+ kanban_board_default_workdir_placeholder: 'Percorso workspace (opzionale)',
kanban_board_name_required: 'Il nome è obbligatorio',
kanban_board_slug_required: 'Lo slug è obbligatorio',
kanban_card_complete: 'completato',
@@ -4559,6 +4563,8 @@ const LOCALES = {
kanban_board_description: '説明 (任意)',
kanban_board_icon: 'アイコン (絵文字、任意)',
kanban_board_color: '色 (任意)',
+ kanban_board_default_workdir: '既定の作業ディレクトリ',
+ kanban_board_default_workdir_placeholder: 'ワークスペースのパス(任意)',
kanban_board_name_required: '名前は必須です',
kanban_board_slug_required: 'スラッグは必須です',
kanban_card_complete: '完了',
@@ -5998,6 +6004,8 @@ const LOCALES = {
kanban_board_description: 'Описание (необязательно)',
kanban_board_icon: 'Иконка (emoji, необязательно)',
kanban_board_color: 'Цвет (необязательно)',
+ kanban_board_default_workdir: 'Рабочая папка по умолчанию',
+ kanban_board_default_workdir_placeholder: 'Путь к workspace (необязательно)',
kanban_board_name_required: 'Укажите имя',
kanban_board_slug_required: 'Укажите slug',
kanban_card_complete: 'завершить',
@@ -7706,6 +7714,8 @@ const LOCALES = {
kanban_board_description: 'Description (optional)',
kanban_board_icon: 'Icon (emoji, optional)',
kanban_board_color: 'Color (optional)',
+ kanban_board_default_workdir: 'Directorio de trabajo predeterminado',
+ kanban_board_default_workdir_placeholder: 'Ruta del workspace (opcional)',
kanban_board_name_required: 'Name is required',
kanban_board_slug_required: 'Slug is required',
kanban_card_complete: 'complete',
@@ -9373,6 +9383,8 @@ const LOCALES = {
kanban_board_description: 'Description (optional)',
kanban_board_icon: 'Icon (emoji, optional)',
kanban_board_color: 'Color (optional)',
+ kanban_board_default_workdir: 'Standard-Arbeitsverzeichnis',
+ kanban_board_default_workdir_placeholder: 'Workspace-Pfad (optional)',
kanban_board_name_required: 'Name is required',
kanban_board_slug_required: 'Slug is required',
kanban_card_complete: 'complete',
@@ -11091,6 +11103,8 @@ const LOCALES = {
kanban_board_description: '描述(可选)',
kanban_board_icon: '图标(emoji,可选)',
kanban_board_color: '颜色(可选)',
+ kanban_board_default_workdir: '默认工作目录',
+ kanban_board_default_workdir_placeholder: '工作区路径(可选)',
kanban_board_name_required: '名称为必填',
kanban_board_slug_required: '标识为必填',
kanban_card_complete: '完成',
@@ -13082,6 +13096,8 @@ const LOCALES = {
kanban_board_description: '描述(選填)',
kanban_board_icon: '圖示(emoji,選填)',
kanban_board_color: '顏色(選填)',
+ kanban_board_default_workdir: '預設工作目錄',
+ kanban_board_default_workdir_placeholder: '工作區路徑(選填)',
kanban_board_name_required: '名稱為必填',
kanban_board_slug_required: 'Slug 為必填',
kanban_card_complete: '完成',
@@ -14713,6 +14729,8 @@ const LOCALES = {
kanban_board_description: 'Description (optional)',
kanban_board_icon: 'Icon (emoji, optional)',
kanban_board_color: 'Color (optional)',
+ kanban_board_default_workdir: 'Diretório de trabalho padrão',
+ kanban_board_default_workdir_placeholder: 'Caminho do workspace (opcional)',
kanban_board_name_required: 'Name is required',
kanban_board_slug_required: 'Slug is required',
kanban_card_complete: 'complete',
@@ -16355,6 +16373,8 @@ const LOCALES = {
kanban_board_description: 'Description (optional)',
kanban_board_icon: 'Icon (emoji, optional)',
kanban_board_color: 'Color (optional)',
+ kanban_board_default_workdir: '기본 작업 디렉터리',
+ kanban_board_default_workdir_placeholder: '워크스페이스 경로(선택 사항)',
kanban_board_name_required: 'Name is required',
kanban_board_slug_required: 'Slug is required',
kanban_card_complete: 'complete',
@@ -18120,6 +18140,8 @@ const LOCALES = {
kanban_board_description: 'Description (facultatif)',
kanban_board_icon: 'Icône (emoji, facultatif)',
kanban_board_color: 'Couleur (facultatif)',
+ kanban_board_default_workdir: 'Répertoire par défaut',
+ kanban_board_default_workdir_placeholder: 'Chemin du workspace (facultatif)',
kanban_board_name_required: 'Le nom est requis',
kanban_board_slug_required: 'Le slug est requis',
kanban_card_complete: 'terminer',
@@ -19155,6 +19177,8 @@ const LOCALES = {
kanban_board: 'Deska',
kanban_board_archived: 'Deska archivována',
kanban_board_color: 'Barva (volitelná)',
+ kanban_board_default_workdir: 'Výchozí pracovní adresář',
+ kanban_board_default_workdir_placeholder: 'Cesta workspace (volitelná)',
kanban_board_description: 'Popis (volitelný)',
kanban_board_icon: 'Ikona (emoji, volitelná)',
kanban_board_name: 'Název',
@@ -21530,6 +21554,8 @@ const LOCALES = {
kanban_board_description: 'Açıklama (isteğe bağlı)',
kanban_board_icon: 'Simge (emoji, isteğe bağlı)',
kanban_board_color: 'Renk (isteğe bağlı)',
+ kanban_board_default_workdir: 'Varsayılan çalışma dizini',
+ kanban_board_default_workdir_placeholder: 'Workspace yolu (isteğe bağlı)',
kanban_board_name_required: 'Ad gerekli',
kanban_board_slug_required: 'Slug gerekli',
kanban_card_complete: 'tamamlamak',
@@ -23290,6 +23316,8 @@ const LOCALES = {
kanban_board_description: 'Opis (opcjonalnie)',
kanban_board_icon: 'Ikona (emoji, opcjonalnie)',
kanban_board_color: 'Kolor (opcjonalnie)',
+ kanban_board_default_workdir: 'Domyślny katalog roboczy',
+ kanban_board_default_workdir_placeholder: 'Ścieżka workspace (opcjonalna)',
kanban_board_name_required: 'Nazwa jest wymagana',
kanban_board_slug_required: 'Identyfikator (slug) jest wymagany',
kanban_card_complete: 'ukończone',
@@ -24871,6 +24899,8 @@ const LOCALES = {
kanban_board_description: 'Mô tả (tùy chọn)',
kanban_board_icon: 'Biểu tượng (emoji, tùy chọn)',
kanban_board_color: 'Màu sắc (tùy chọn)',
+ kanban_board_default_workdir: 'Workspace mặc định',
+ kanban_board_default_workdir_placeholder: 'Đường dẫn workspace (tùy chọn)',
kanban_board_name_required: 'Tên là bắt buộc',
kanban_board_slug_required: 'Slug là bắt buộc',
kanban_card_complete: 'hoàn tất',
diff --git a/static/index.html b/static/index.html
index 8e9568de..8912f0dd 100644
--- a/static/index.html
+++ b/static/index.html
@@ -1795,6 +1795,11 @@
+
+
+
+
+
diff --git a/static/panels.js b/static/panels.js
index 8ab5cc14..880ce1e0 100644
--- a/static/panels.js
+++ b/static/panels.js
@@ -3898,12 +3898,8 @@ async function loadKanbanBoards(){
_kanbanSetSavedBoard('default');
}
_kanbanCurrentBoard = (active === 'default') ? null : active;
- // The switcher is visible whenever ≥1 non-default board exists OR the
- // current board is non-default. (If you only have 'default', a switcher
- // adds clutter without value.)
- const hasMultiple = boards.length > 1 || (active !== 'default');
- switcher.hidden = !hasMultiple;
- if (!hasMultiple) return;
+ // Keep the switcher visible because it is also the default-board settings path.
+ switcher.hidden = false;
// Update the toggle label/icon
const activeMeta = boards.find(b => b.slug === active) || {slug: active, name: active, icon: '', color: ''};
const nameEl = document.getElementById('kanbanBoardSwitcherName');
@@ -3947,10 +3943,7 @@ function _renderKanbanBoardMenu(boards, current){
${esc(String(total))}
`;
}).join('');
- // Actions row — disable rename/archive when the only option is `default`
- // (the default board's display metadata is editable but the slug isn't,
- // and `default` cannot be archived).
- const renameDisabled = current === 'default';
+ // The default board is editable but cannot be archived.
const archiveDisabled = current === 'default';
const actions = `
@@ -3958,7 +3951,7 @@ function _renderKanbanBoardMenu(boards, current){
${esc(t('kanban_new_board') || 'New board…')}
-