{columns.map((col, colIndex) => {
diff --git a/shared/components/board/BoardSwimlanes.tsx b/shared/components/board/BoardSwimlanes.tsx
new file mode 100644
index 0000000..2071825
--- /dev/null
+++ b/shared/components/board/BoardSwimlanes.tsx
@@ -0,0 +1,125 @@
+import { Trans } from "@lingui/react/macro";
+import { CheckCircleIcon, CalendarDaysIcon } from "@heroicons/react/24/outline";
+import {
+ PRIORITY_STYLE,
+ groupValueOf,
+ partitionSwimlanes,
+ sortCards,
+ type BoardGroupKey,
+ type BoardViewCard,
+ type BoardViewColumn,
+} from "../../lib/board";
+
+/**
+ * Two-dimensional board view: the primary grouping as columns and a second
+ * dimension as horizontal swimlanes (rows). A read-friendly overview built from
+ * the same cards as the kanban — clicking a card opens the same peek (where its
+ * status / swimlane attribute can be changed). Drag stays in the 1-D board view.
+ */
+export function BoardSwimlanes({
+ cards,
+ columns,
+ lanes,
+ groupKey,
+ swimlaneKey,
+ today,
+ doneKey,
+ selectedId,
+ onSelect,
+}: {
+ cards: BoardViewCard[];
+ columns: BoardViewColumn[];
+ lanes: BoardViewColumn[];
+ groupKey: BoardGroupKey;
+ swimlaneKey: BoardGroupKey;
+ today: string;
+ doneKey: string;
+ selectedId?: string;
+ onSelect: (card: BoardViewCard) => void;
+}) {
+ const grid = partitionSwimlanes(cards, groupKey, swimlaneKey);
+ const gridCols = { gridTemplateColumns: `repeat(${Math.max(columns.length, 1)}, minmax(11rem, 1fr))` };
+
+ const chip = (card: BoardViewCard) => {
+ const overdue = card.due && card.due < today && card.columnKey !== doneKey;
+ return (
+
+ );
+ };
+
+ return (
+
+
+ {/* Column header row */}
+
+ {columns.map((col) => (
+
+ {col.name}
+
+ ))}
+
+
+ {lanes.map((lane) => {
+ const row = grid.get(lane.key);
+ const laneTotal = row ? [...row.values()].reduce((n, cell) => n + cell.length, 0) : 0;
+ return (
+
+
+ {lane.name}
+ {laneTotal}
+
+
+ {columns.map((col) => {
+ const cell = sortCards((row?.get(col.key) ?? []).filter((c) => groupValueOf(c, groupKey) === col.key), "manual");
+ return (
+
+ {cell.map((card) => chip(card))}
+
+ );
+ })}
+
+
+ );
+ })}
+
+ {lanes.length === 0 && (
+
+ No cards
+
+ )}
+
+
+ );
+}
diff --git a/shared/components/board/index.ts b/shared/components/board/index.ts
index c018294..0726746 100644
--- a/shared/components/board/index.ts
+++ b/shared/components/board/index.ts
@@ -1,5 +1,6 @@
export { BoardSurface } from "./BoardSurface";
export { BoardPeek } from "./BoardPeek";
export { BoardTable } from "./BoardTable";
+export { BoardSwimlanes } from "./BoardSwimlanes";
export { EmojiField, ListboxSelect, TagMultiSelect, fieldCls } from "./controls";
export type { BoardActions, BoardSurfaceProps, BoardOption } from "./types";
diff --git a/shared/i18n/locales/en/messages.mjs b/shared/i18n/locales/en/messages.mjs
index 218da68..61065a5 100644
--- a/shared/i18n/locales/en/messages.mjs
+++ b/shared/i18n/locales/en/messages.mjs
@@ -1 +1 @@
-/*eslint-disable*/export const messages=JSON.parse("{\"--lIxB\":[\"Blocked by\"],\"-b7T3G\":[\"Updated\"],\"1DBGsz\":[\"Notes\"],\"1YABGm\":[\"Link (Ctrl+K)\"],\"1hKEom\":[\"Priority\"],\"2wxgft\":[\"Rename\"],\"3qkggm\":[\"Fullscreen\"],\"4gdyen\":[\"Local (yours)\"],\"4hJhzz\":[\"Table\"],\"54sFiP\":[\"flowchart TD\\n A[Start] --> B[End]\"],\"5Q_DQ6\":[\"Inline Code\"],\"7VpPHA\":[\"Confirm\"],\"7s3WlU\":[\"Blocks\"],\"8PifYj\":[\"Mermaid diagram\"],\"8hSn0h\":[\"Result (editable)\"],\"8lE269\":[\"Sort: Manual\"],\"9gxam6\":[\"Could not render this Draw.io diagram.\"],\"AC9Gkf\":[\"Expand column\"],\"AS5WO9\":[\"Could not render this PDF.\"],\"AVreQ5\":[\"Drag to resize\"],\"AgvHni\":[\"Add column\"],\"AxAubu\":[\"Group: Assignee\"],\"BfMZ7w\":[\"Accept cloud\"],\"BnmEvM\":[\"Save as template\"],\"C6-ZRl\":[\"Someone\"],\"EWPtMO\":[\"Code\"],\"EbMPZJ\":[\"Unassigned\"],\"G4qrLy\":[\"Unset done column\"],\"GKu3m4\":[\"No labels\"],\"Gpfctt\":[\"Due\"],\"H_SQFv\":[\"No color\"],\"I6SWEy\":[\"Split\"],\"ICip_B\":[\"Cloud (remote)\"],\"Ik60OC\":[\"Open in editor\"],\"Iw6WJa\":[\"Set WIP limit\"],\"JTYvAw\":[\"Search cards\"],\"K_F6pa\":[\"Saving…\"],\"KmydK6\":[\"Bold\"],\"KvW1VO\":[\"Draw.io diagram\"],\"LQn6-8\":[\"Accept local\"],\"MHrjPM\":[\"Title\"],\"Mm72la\":[\"No comments yet\"],\"NBdIgR\":[\"Comment\"],\"OYHzN1\":[\"Tags\"],\"OepdfE\":[\"Group: Status\"],\"Q2mGA7\":[\"Clear filter\"],\"QD8opX\":[\"Board\"],\"QlsPZy\":[\"Write Mermaid syntax to see the diagram.\"],\"S5Qbb1\":[\"comma, separated\"],\"TdfEV7\":[\"Archived\"],\"UQOvxZ\":[\"Blank card\"],\"VNa_N2\":[\"This file type can not be previewed yet.\"],\"VbyRUy\":[\"Comments\"],\"WSP6v1\":[\"Sort: Priority\"],\"X03-eC\":[\"Please enter a value.\"],\"XJOV1Y\":[\"Activity\"],\"Ya7bZl\":[\"Diagram error\"],\"Zot9XS\":[\"No cards\"],\"_5CsXX\":[\"Done column\"],\"_EsjyQ\":[\"Use this\"],\"a6uhHr\":[\"Bold (Ctrl+B)\"],\"aDvLhk\":[\"Add a comment…\"],\"abUZlY\":[\"Add details...\"],\"agOeRN\":[\"Could not render this API specification.\"],\"b4hVKD\":[\"Color columns\"],\"cfaWH-\":[\"Add labels\"],\"cnGeoo\":[\"Delete\"],\"d-F6q9\":[\"Created\"],\"d5z6xQ\":[\"WIP limit \",[\"0\"]],\"dEgA5A\":[\"Cancel\"],\"euc6Ns\":[\"Duplicate\"],\"fYcKtB\":[\"Sort: Due\"],\"gLDJuJ\":[\"Untitled card\"],\"hh4sEG\":[\"Relates\"],\"hnK1gR\":[\"PDF document\"],\"i4_LY_\":[\"Write\"],\"iTylMl\":[\"Templates\"],\"iYVqZq\":[\"Column name\"],\"jZlrte\":[\"Color\"],\"kZlRKE\":[\"Mermaid source\"],\"kryGs-\":[\"Card\"],\"lCF0wC\":[\"Refresh\"],\"ltF1xa\":[\"Save merged result\"],\"nabda1\":[\"Delete card\"],\"njJFtc\":[\"Delete comment\"],\"o7J4JM\":[\"Filter\"],\"o8va6N\":[\"Restored\"],\"ojKCLU\":[\"Assignee\"],\"p9yTeb\":[\"Sort: Title\"],\"pKztsX\":[\"Open in full editor\"],\"pnrmSP\":[\"New card\"],\"pwN6Ae\":[\"Collapse column\"],\"pzutoc\":[\"Italic\"],\"rdUucN\":[\"Preview\"],\"sCzmvQ\":[\"cards\"],\"sQpDn6\":[\"Exit fullscreen\"],\"tK2x9T\":[\"⚠ \",[\"0\"],\" Conflict\",[\"1\"],\" to Resolve\"],\"u2IprG\":[\"Card title (Enter to add, Esc to cancel)\"],\"uAQUqI\":[\"Status\"],\"wf6Djn\":[\"Italic (Ctrl+I)\"],\"wtw-au\":[\"Set as done column\"],\"wwu18a\":[\"Icon\"],\"x52RAh\":[\"Blocked by \",[\"blockedCount\"],\" unfinished card(s)\"],\"y1eoq1\":[\"Copy link\"],\"y9cj46\":[\"Group: Priority\"],\"ybGQtY\":[\"← Back to list\"],\"yz7wBu\":[\"Close\"],\"yzF66j\":[\"Link\"],\"zOc0vf\":[\"No icon\"],\"zga9sT\":[\"OK\"]}");
\ No newline at end of file
+/*eslint-disable*/export const messages=JSON.parse("{\"--lIxB\":[\"Blocked by\"],\"-b7T3G\":[\"Updated\"],\"1DBGsz\":[\"Notes\"],\"1YABGm\":[\"Link (Ctrl+K)\"],\"1hKEom\":[\"Priority\"],\"2wxgft\":[\"Rename\"],\"3qkggm\":[\"Fullscreen\"],\"4gdyen\":[\"Local (yours)\"],\"4hJhzz\":[\"Table\"],\"54sFiP\":[\"flowchart TD\\n A[Start] --> B[End]\"],\"5Q_DQ6\":[\"Inline Code\"],\"7VpPHA\":[\"Confirm\"],\"7s3WlU\":[\"Blocks\"],\"8PifYj\":[\"Mermaid diagram\"],\"8hSn0h\":[\"Result (editable)\"],\"8lE269\":[\"Sort: Manual\"],\"9gxam6\":[\"Could not render this Draw.io diagram.\"],\"AC9Gkf\":[\"Expand column\"],\"AS5WO9\":[\"Could not render this PDF.\"],\"AVreQ5\":[\"Drag to resize\"],\"AgvHni\":[\"Add column\"],\"AxAubu\":[\"Group: Assignee\"],\"BfMZ7w\":[\"Accept cloud\"],\"BnmEvM\":[\"Save as template\"],\"C6-ZRl\":[\"Someone\"],\"EWPtMO\":[\"Code\"],\"EbMPZJ\":[\"Unassigned\"],\"G4qrLy\":[\"Unset done column\"],\"GKu3m4\":[\"No labels\"],\"Gpfctt\":[\"Due\"],\"H_SQFv\":[\"No color\"],\"I6SWEy\":[\"Split\"],\"ICip_B\":[\"Cloud (remote)\"],\"Ik60OC\":[\"Open in editor\"],\"Iw6WJa\":[\"Set WIP limit\"],\"JTYvAw\":[\"Search cards\"],\"K_F6pa\":[\"Saving…\"],\"KjXDqG\":[\"Swimlane: None\"],\"KmydK6\":[\"Bold\"],\"KvW1VO\":[\"Draw.io diagram\"],\"LQn6-8\":[\"Accept local\"],\"MHrjPM\":[\"Title\"],\"Mm72la\":[\"No comments yet\"],\"NBdIgR\":[\"Comment\"],\"OYHzN1\":[\"Tags\"],\"OepdfE\":[\"Group: Status\"],\"Q2mGA7\":[\"Clear filter\"],\"QD8opX\":[\"Board\"],\"QlsPZy\":[\"Write Mermaid syntax to see the diagram.\"],\"S5Qbb1\":[\"comma, separated\"],\"TdfEV7\":[\"Archived\"],\"UQOvxZ\":[\"Blank card\"],\"VNa_N2\":[\"This file type can not be previewed yet.\"],\"VbyRUy\":[\"Comments\"],\"WSP6v1\":[\"Sort: Priority\"],\"X03-eC\":[\"Please enter a value.\"],\"XJOV1Y\":[\"Activity\"],\"Ya7bZl\":[\"Diagram error\"],\"Zot9XS\":[\"No cards\"],\"_5CsXX\":[\"Done column\"],\"_EsjyQ\":[\"Use this\"],\"a6uhHr\":[\"Bold (Ctrl+B)\"],\"aDvLhk\":[\"Add a comment…\"],\"abUZlY\":[\"Add details...\"],\"agOeRN\":[\"Could not render this API specification.\"],\"b4hVKD\":[\"Color columns\"],\"cfaWH-\":[\"Add labels\"],\"cnGeoo\":[\"Delete\"],\"d-F6q9\":[\"Created\"],\"d5z6xQ\":[\"WIP limit \",[\"0\"]],\"dEgA5A\":[\"Cancel\"],\"euc6Ns\":[\"Duplicate\"],\"fYcKtB\":[\"Sort: Due\"],\"gLDJuJ\":[\"Untitled card\"],\"hh4sEG\":[\"Relates\"],\"hnK1gR\":[\"PDF document\"],\"i4_LY_\":[\"Write\"],\"iTylMl\":[\"Templates\"],\"iYVqZq\":[\"Column name\"],\"jUbC3Z\":[\"Swimlane: Priority\"],\"jZlrte\":[\"Color\"],\"kZlRKE\":[\"Mermaid source\"],\"kryGs-\":[\"Card\"],\"lCF0wC\":[\"Refresh\"],\"lHxVTh\":[\"Swimlane: Assignee\"],\"ltF1xa\":[\"Save merged result\"],\"nabda1\":[\"Delete card\"],\"njJFtc\":[\"Delete comment\"],\"o7J4JM\":[\"Filter\"],\"o8va6N\":[\"Restored\"],\"ojKCLU\":[\"Assignee\"],\"p9yTeb\":[\"Sort: Title\"],\"pKztsX\":[\"Open in full editor\"],\"pnrmSP\":[\"New card\"],\"pwN6Ae\":[\"Collapse column\"],\"pzutoc\":[\"Italic\"],\"rdUucN\":[\"Preview\"],\"sCzmvQ\":[\"cards\"],\"sQpDn6\":[\"Exit fullscreen\"],\"tK2x9T\":[\"⚠ \",[\"0\"],\" Conflict\",[\"1\"],\" to Resolve\"],\"u2IprG\":[\"Card title (Enter to add, Esc to cancel)\"],\"uAQUqI\":[\"Status\"],\"ucJg3u\":[\"Swimlane: Status\"],\"wf6Djn\":[\"Italic (Ctrl+I)\"],\"wtw-au\":[\"Set as done column\"],\"wwu18a\":[\"Icon\"],\"x52RAh\":[\"Blocked by \",[\"blockedCount\"],\" unfinished card(s)\"],\"y1eoq1\":[\"Copy link\"],\"y9cj46\":[\"Group: Priority\"],\"ybGQtY\":[\"← Back to list\"],\"yz7wBu\":[\"Close\"],\"yzF66j\":[\"Link\"],\"zOc0vf\":[\"No icon\"],\"zga9sT\":[\"OK\"]}");
\ No newline at end of file
diff --git a/shared/i18n/locales/en/messages.po b/shared/i18n/locales/en/messages.po
index df8523e..3fb7745 100644
--- a/shared/i18n/locales/en/messages.po
+++ b/shared/i18n/locales/en/messages.po
@@ -290,6 +290,7 @@ msgstr "Mermaid source"
msgid "New card"
msgstr "New card"
+#: shared/components/board/BoardSwimlanes.tsx
#: shared/components/board/BoardTable.tsx
msgid "No cards"
msgstr "No cards"
@@ -419,6 +420,22 @@ msgstr "Split"
msgid "Status"
msgstr "Status"
+#: shared/components/board/BoardSurface.tsx
+msgid "Swimlane: Assignee"
+msgstr "Swimlane: Assignee"
+
+#: shared/components/board/BoardSurface.tsx
+msgid "Swimlane: None"
+msgstr "Swimlane: None"
+
+#: shared/components/board/BoardSurface.tsx
+msgid "Swimlane: Priority"
+msgstr "Swimlane: Priority"
+
+#: shared/components/board/BoardSurface.tsx
+msgid "Swimlane: Status"
+msgstr "Swimlane: Status"
+
#: shared/components/board/BoardSurface.tsx
#: shared/components/EditorToolbar.tsx
msgid "Table"
diff --git a/shared/i18n/locales/ja/messages.mjs b/shared/i18n/locales/ja/messages.mjs
index 3e4add7..dfbac1b 100644
--- a/shared/i18n/locales/ja/messages.mjs
+++ b/shared/i18n/locales/ja/messages.mjs
@@ -1 +1 @@
-/*eslint-disable*/export const messages=JSON.parse("{\"--lIxB\":[\"Blocked by\"],\"-b7T3G\":[\"Updated\"],\"1DBGsz\":[\"ノート\"],\"1YABGm\":[\"リンク (Ctrl+K)\"],\"1hKEom\":[\"優先度\"],\"2wxgft\":[\"名前を変更\"],\"3qkggm\":[\"全画面表示\"],\"4gdyen\":[\"ローカル(自分の)\"],\"4hJhzz\":[\"表\"],\"54sFiP\":[\"flowchart TD\\n A[開始] --> B[終了]\"],\"5Q_DQ6\":[\"インラインコード\"],\"7VpPHA\":[\"確認\"],\"7s3WlU\":[\"Blocks\"],\"8PifYj\":[\"Mermaid 図\"],\"8hSn0h\":[\"結果(編集可能)\"],\"8lE269\":[\"並べ替え:手動\"],\"9gxam6\":[\"この Draw.io 図をレンダリングできませんでした。\"],\"AC9Gkf\":[\"列を展開\"],\"AS5WO9\":[\"この PDF をレンダリングできませんでした。\"],\"AVreQ5\":[\"ドラッグしてサイズ変更\"],\"AgvHni\":[\"列を追加\"],\"AxAubu\":[\"グループ:担当者\"],\"BfMZ7w\":[\"クラウドを採用\"],\"BnmEvM\":[\"テンプレートとして保存\"],\"C6-ZRl\":[\"Someone\"],\"EWPtMO\":[\"コード\"],\"EbMPZJ\":[\"未割り当て\"],\"G4qrLy\":[\"完了列を解除\"],\"GKu3m4\":[\"ラベルなし\"],\"Gpfctt\":[\"期限\"],\"H_SQFv\":[\"色なし\"],\"I6SWEy\":[\"分割\"],\"ICip_B\":[\"クラウド(リモート)\"],\"Ik60OC\":[\"エディターで開く\"],\"Iw6WJa\":[\"WIP 制限を設定\"],\"JTYvAw\":[\"カードを検索\"],\"K_F6pa\":[\"保存中…\"],\"KmydK6\":[\"太字\"],\"KvW1VO\":[\"Draw.io 図\"],\"LQn6-8\":[\"ローカルを採用\"],\"MHrjPM\":[\"タイトル\"],\"Mm72la\":[\"No comments yet\"],\"NBdIgR\":[\"Comment\"],\"OYHzN1\":[\"タグ\"],\"OepdfE\":[\"グループ:ステータス\"],\"Q2mGA7\":[\"フィルターをクリア\"],\"QD8opX\":[\"ボード\"],\"QlsPZy\":[\"Mermaid 構文を書くと図が表示されます。\"],\"S5Qbb1\":[\"カンマ区切り\"],\"TdfEV7\":[\"Archived\"],\"UQOvxZ\":[\"空のカード\"],\"VNa_N2\":[\"このファイル形式はまだプレビューできません。\"],\"VbyRUy\":[\"Comments\"],\"WSP6v1\":[\"並べ替え:優先度\"],\"X03-eC\":[\"値を入力してください。\"],\"XJOV1Y\":[\"Activity\"],\"Ya7bZl\":[\"図のエラー\"],\"Zot9XS\":[\"カードなし\"],\"_5CsXX\":[\"完了列\"],\"_EsjyQ\":[\"これを使用\"],\"a6uhHr\":[\"太字 (Ctrl+B)\"],\"aDvLhk\":[\"Add a comment…\"],\"abUZlY\":[\"詳細を追加...\"],\"agOeRN\":[\"この API 仕様をレンダリングできませんでした。\"],\"b4hVKD\":[\"色付き列\"],\"cfaWH-\":[\"ラベルを追加\"],\"cnGeoo\":[\"削除\"],\"d-F6q9\":[\"Created\"],\"d5z6xQ\":[\"WIP 制限 \",[\"0\"]],\"dEgA5A\":[\"キャンセル\"],\"euc6Ns\":[\"複製\"],\"fYcKtB\":[\"並べ替え:期限\"],\"gLDJuJ\":[\"無題のカード\"],\"hh4sEG\":[\"Relates\"],\"hnK1gR\":[\"PDF ドキュメント\"],\"i4_LY_\":[\"記述\"],\"iTylMl\":[\"テンプレート\"],\"iYVqZq\":[\"列名\"],\"jZlrte\":[\"カラー\"],\"kZlRKE\":[\"Mermaid ソース\"],\"kryGs-\":[\"カード\"],\"lCF0wC\":[\"更新\"],\"ltF1xa\":[\"マージ結果を保存\"],\"nabda1\":[\"カードを削除\"],\"njJFtc\":[\"Delete comment\"],\"o7J4JM\":[\"フィルター\"],\"o8va6N\":[\"Restored\"],\"ojKCLU\":[\"担当者\"],\"p9yTeb\":[\"並べ替え:タイトル\"],\"pKztsX\":[\"フルエディターで開く\"],\"pnrmSP\":[\"新規カード\"],\"pwN6Ae\":[\"列を折りたたむ\"],\"pzutoc\":[\"イタリック\"],\"rdUucN\":[\"プレビュー\"],\"sCzmvQ\":[\"枚のカード\"],\"sQpDn6\":[\"全画面表示を終了\"],\"tK2x9T\":[\"⚠ \",[\"0\"],\" 件の競合\",[\"1\"],\"を解決中\"],\"u2IprG\":[\"カードのタイトル(Enter で追加、Esc でキャンセル)\"],\"uAQUqI\":[\"ステータス\"],\"wf6Djn\":[\"イタリック (Ctrl+I)\"],\"wtw-au\":[\"完了列に設定\"],\"wwu18a\":[\"アイコン\"],\"x52RAh\":[\"Blocked by \",[\"blockedCount\"],\" unfinished card(s)\"],\"y1eoq1\":[\"リンクをコピー\"],\"y9cj46\":[\"グループ:優先度\"],\"ybGQtY\":[\"← リストに戻る\"],\"yz7wBu\":[\"閉じる\"],\"yzF66j\":[\"リンク\"],\"zOc0vf\":[\"アイコンなし\"],\"zga9sT\":[\"OK\"]}");
\ No newline at end of file
+/*eslint-disable*/export const messages=JSON.parse("{\"--lIxB\":[\"Blocked by\"],\"-b7T3G\":[\"Updated\"],\"1DBGsz\":[\"ノート\"],\"1YABGm\":[\"リンク (Ctrl+K)\"],\"1hKEom\":[\"優先度\"],\"2wxgft\":[\"名前を変更\"],\"3qkggm\":[\"全画面表示\"],\"4gdyen\":[\"ローカル(自分の)\"],\"4hJhzz\":[\"表\"],\"54sFiP\":[\"flowchart TD\\n A[開始] --> B[終了]\"],\"5Q_DQ6\":[\"インラインコード\"],\"7VpPHA\":[\"確認\"],\"7s3WlU\":[\"Blocks\"],\"8PifYj\":[\"Mermaid 図\"],\"8hSn0h\":[\"結果(編集可能)\"],\"8lE269\":[\"並べ替え:手動\"],\"9gxam6\":[\"この Draw.io 図をレンダリングできませんでした。\"],\"AC9Gkf\":[\"列を展開\"],\"AS5WO9\":[\"この PDF をレンダリングできませんでした。\"],\"AVreQ5\":[\"ドラッグしてサイズ変更\"],\"AgvHni\":[\"列を追加\"],\"AxAubu\":[\"グループ:担当者\"],\"BfMZ7w\":[\"クラウドを採用\"],\"BnmEvM\":[\"テンプレートとして保存\"],\"C6-ZRl\":[\"Someone\"],\"EWPtMO\":[\"コード\"],\"EbMPZJ\":[\"未割り当て\"],\"G4qrLy\":[\"完了列を解除\"],\"GKu3m4\":[\"ラベルなし\"],\"Gpfctt\":[\"期限\"],\"H_SQFv\":[\"色なし\"],\"I6SWEy\":[\"分割\"],\"ICip_B\":[\"クラウド(リモート)\"],\"Ik60OC\":[\"エディターで開く\"],\"Iw6WJa\":[\"WIP 制限を設定\"],\"JTYvAw\":[\"カードを検索\"],\"K_F6pa\":[\"保存中…\"],\"KjXDqG\":[\"Swimlane: None\"],\"KmydK6\":[\"太字\"],\"KvW1VO\":[\"Draw.io 図\"],\"LQn6-8\":[\"ローカルを採用\"],\"MHrjPM\":[\"タイトル\"],\"Mm72la\":[\"No comments yet\"],\"NBdIgR\":[\"Comment\"],\"OYHzN1\":[\"タグ\"],\"OepdfE\":[\"グループ:ステータス\"],\"Q2mGA7\":[\"フィルターをクリア\"],\"QD8opX\":[\"ボード\"],\"QlsPZy\":[\"Mermaid 構文を書くと図が表示されます。\"],\"S5Qbb1\":[\"カンマ区切り\"],\"TdfEV7\":[\"Archived\"],\"UQOvxZ\":[\"空のカード\"],\"VNa_N2\":[\"このファイル形式はまだプレビューできません。\"],\"VbyRUy\":[\"Comments\"],\"WSP6v1\":[\"並べ替え:優先度\"],\"X03-eC\":[\"値を入力してください。\"],\"XJOV1Y\":[\"Activity\"],\"Ya7bZl\":[\"図のエラー\"],\"Zot9XS\":[\"カードなし\"],\"_5CsXX\":[\"完了列\"],\"_EsjyQ\":[\"これを使用\"],\"a6uhHr\":[\"太字 (Ctrl+B)\"],\"aDvLhk\":[\"Add a comment…\"],\"abUZlY\":[\"詳細を追加...\"],\"agOeRN\":[\"この API 仕様をレンダリングできませんでした。\"],\"b4hVKD\":[\"色付き列\"],\"cfaWH-\":[\"ラベルを追加\"],\"cnGeoo\":[\"削除\"],\"d-F6q9\":[\"Created\"],\"d5z6xQ\":[\"WIP 制限 \",[\"0\"]],\"dEgA5A\":[\"キャンセル\"],\"euc6Ns\":[\"複製\"],\"fYcKtB\":[\"並べ替え:期限\"],\"gLDJuJ\":[\"無題のカード\"],\"hh4sEG\":[\"Relates\"],\"hnK1gR\":[\"PDF ドキュメント\"],\"i4_LY_\":[\"記述\"],\"iTylMl\":[\"テンプレート\"],\"iYVqZq\":[\"列名\"],\"jUbC3Z\":[\"Swimlane: Priority\"],\"jZlrte\":[\"カラー\"],\"kZlRKE\":[\"Mermaid ソース\"],\"kryGs-\":[\"カード\"],\"lCF0wC\":[\"更新\"],\"lHxVTh\":[\"Swimlane: Assignee\"],\"ltF1xa\":[\"マージ結果を保存\"],\"nabda1\":[\"カードを削除\"],\"njJFtc\":[\"Delete comment\"],\"o7J4JM\":[\"フィルター\"],\"o8va6N\":[\"Restored\"],\"ojKCLU\":[\"担当者\"],\"p9yTeb\":[\"並べ替え:タイトル\"],\"pKztsX\":[\"フルエディターで開く\"],\"pnrmSP\":[\"新規カード\"],\"pwN6Ae\":[\"列を折りたたむ\"],\"pzutoc\":[\"イタリック\"],\"rdUucN\":[\"プレビュー\"],\"sCzmvQ\":[\"枚のカード\"],\"sQpDn6\":[\"全画面表示を終了\"],\"tK2x9T\":[\"⚠ \",[\"0\"],\" 件の競合\",[\"1\"],\"を解決中\"],\"u2IprG\":[\"カードのタイトル(Enter で追加、Esc でキャンセル)\"],\"uAQUqI\":[\"ステータス\"],\"ucJg3u\":[\"Swimlane: Status\"],\"wf6Djn\":[\"イタリック (Ctrl+I)\"],\"wtw-au\":[\"完了列に設定\"],\"wwu18a\":[\"アイコン\"],\"x52RAh\":[\"Blocked by \",[\"blockedCount\"],\" unfinished card(s)\"],\"y1eoq1\":[\"リンクをコピー\"],\"y9cj46\":[\"グループ:優先度\"],\"ybGQtY\":[\"← リストに戻る\"],\"yz7wBu\":[\"閉じる\"],\"yzF66j\":[\"リンク\"],\"zOc0vf\":[\"アイコンなし\"],\"zga9sT\":[\"OK\"]}");
\ No newline at end of file
diff --git a/shared/i18n/locales/ja/messages.po b/shared/i18n/locales/ja/messages.po
index ca378ca..3b288ea 100644
--- a/shared/i18n/locales/ja/messages.po
+++ b/shared/i18n/locales/ja/messages.po
@@ -290,6 +290,7 @@ msgstr "Mermaid ソース"
msgid "New card"
msgstr "新規カード"
+#: shared/components/board/BoardSwimlanes.tsx
#: shared/components/board/BoardTable.tsx
msgid "No cards"
msgstr "カードなし"
@@ -419,6 +420,22 @@ msgstr "分割"
msgid "Status"
msgstr "ステータス"
+#: shared/components/board/BoardSurface.tsx
+msgid "Swimlane: Assignee"
+msgstr ""
+
+#: shared/components/board/BoardSurface.tsx
+msgid "Swimlane: None"
+msgstr ""
+
+#: shared/components/board/BoardSurface.tsx
+msgid "Swimlane: Priority"
+msgstr ""
+
+#: shared/components/board/BoardSurface.tsx
+msgid "Swimlane: Status"
+msgstr ""
+
#: shared/components/board/BoardSurface.tsx
#: shared/components/EditorToolbar.tsx
msgid "Table"
diff --git a/shared/i18n/locales/ko/messages.mjs b/shared/i18n/locales/ko/messages.mjs
index 1fea663..87a7662 100644
--- a/shared/i18n/locales/ko/messages.mjs
+++ b/shared/i18n/locales/ko/messages.mjs
@@ -1 +1 @@
-/*eslint-disable*/export const messages=JSON.parse("{\"--lIxB\":[\"Blocked by\"],\"-b7T3G\":[\"Updated\"],\"1DBGsz\":[\"노트\"],\"1YABGm\":[\"링크 (Ctrl+K)\"],\"1hKEom\":[\"우선순위\"],\"2wxgft\":[\"이름 변경\"],\"3qkggm\":[\"전체 화면\"],\"4gdyen\":[\"로컈 (내 것)\"],\"4hJhzz\":[\"테이블\"],\"54sFiP\":[\"flowchart TD\\n A[시작] --> B[끝]\"],\"5Q_DQ6\":[\"인라인 코드\"],\"7VpPHA\":[\"확인\"],\"7s3WlU\":[\"Blocks\"],\"8PifYj\":[\"Mermaid 다이어그램\"],\"8hSn0h\":[\"결과 (편집 가능)\"],\"8lE269\":[\"정렬: 수동\"],\"9gxam6\":[\"이 Draw.io 다이어그램을 렌더링할 수 없습니다.\"],\"AC9Gkf\":[\"열 펼치기\"],\"AS5WO9\":[\"이 PDF를 렌더링할 수 없습니다.\"],\"AVreQ5\":[\"드래그하여 크기 조정\"],\"AgvHni\":[\"열 추가\"],\"AxAubu\":[\"그룹: 담당자\"],\"BfMZ7w\":[\"클라우드 수낙\"],\"BnmEvM\":[\"템플릿으로 저장\"],\"C6-ZRl\":[\"Someone\"],\"EWPtMO\":[\"코드\"],\"EbMPZJ\":[\"미할당\"],\"G4qrLy\":[\"완료 열 해제\"],\"GKu3m4\":[\"라벨 없음\"],\"Gpfctt\":[\"마감\"],\"H_SQFv\":[\"색상 없음\"],\"I6SWEy\":[\"스플릿\"],\"ICip_B\":[\"클라우드 (원격)\"],\"Ik60OC\":[\"에디터에서 열기\"],\"Iw6WJa\":[\"WIP 한도 설정\"],\"JTYvAw\":[\"카드 검색\"],\"K_F6pa\":[\"저장 중…\"],\"KmydK6\":[\"굵게\"],\"KvW1VO\":[\"Draw.io 다이어그램\"],\"LQn6-8\":[\"로컈 수낙\"],\"MHrjPM\":[\"제목\"],\"Mm72la\":[\"No comments yet\"],\"NBdIgR\":[\"Comment\"],\"OYHzN1\":[\"태그\"],\"OepdfE\":[\"그룹: 상태\"],\"Q2mGA7\":[\"필터 지우기\"],\"QD8opX\":[\"보드\"],\"QlsPZy\":[\"Mermaid 구문을 작성하면 다이어그램이 표시됩니다.\"],\"S5Qbb1\":[\"쉼표로 구분\"],\"TdfEV7\":[\"Archived\"],\"UQOvxZ\":[\"빈 카드\"],\"VNa_N2\":[\"이 파일 형식은 아직 미리볼 수 없습니다.\"],\"VbyRUy\":[\"Comments\"],\"WSP6v1\":[\"정렬: 우선순위\"],\"X03-eC\":[\"값을 입력해 주세요.\"],\"XJOV1Y\":[\"Activity\"],\"Ya7bZl\":[\"다이어그램 오류\"],\"Zot9XS\":[\"카드 없음\"],\"_5CsXX\":[\"완료 열\"],\"_EsjyQ\":[\"이것 사용\"],\"a6uhHr\":[\"굵게 (Ctrl+B)\"],\"aDvLhk\":[\"Add a comment…\"],\"abUZlY\":[\"세부정보 추가...\"],\"agOeRN\":[\"이 API 명세를 렌더링할 수 없습니다.\"],\"b4hVKD\":[\"색상 열\"],\"cfaWH-\":[\"라벨 추가\"],\"cnGeoo\":[\"삭제\"],\"d-F6q9\":[\"Created\"],\"d5z6xQ\":[\"WIP 한도 \",[\"0\"]],\"dEgA5A\":[\"취소\"],\"euc6Ns\":[\"복제\"],\"fYcKtB\":[\"정렬: 마감\"],\"gLDJuJ\":[\"제목 없는 카드\"],\"hh4sEG\":[\"Relates\"],\"hnK1gR\":[\"PDF 문서\"],\"i4_LY_\":[\"작성\"],\"iTylMl\":[\"템플릿\"],\"iYVqZq\":[\"열 이름\"],\"jZlrte\":[\"색상\"],\"kZlRKE\":[\"Mermaid 소스\"],\"kryGs-\":[\"카드\"],\"lCF0wC\":[\"새로고침\"],\"ltF1xa\":[\"병합 결과 저장\"],\"nabda1\":[\"카드 삭제\"],\"njJFtc\":[\"Delete comment\"],\"o7J4JM\":[\"필터\"],\"o8va6N\":[\"Restored\"],\"ojKCLU\":[\"담당자\"],\"p9yTeb\":[\"정렬: 제목\"],\"pKztsX\":[\"전체 에디터에서 열기\"],\"pnrmSP\":[\"새 카드\"],\"pwN6Ae\":[\"열 접기\"],\"pzutoc\":[\"기울임꼴\"],\"rdUucN\":[\"미리보기\"],\"sCzmvQ\":[\"개 카드\"],\"sQpDn6\":[\"전체 화면 종료\"],\"tK2x9T\":[\"⚠ 해결할 충돌 \",[\"0\"],\"건\",[\"1\"]],\"u2IprG\":[\"카드 제목 (Enter로 추가, Esc로 취소)\"],\"uAQUqI\":[\"상태\"],\"wf6Djn\":[\"기울임꼴 (Ctrl+I)\"],\"wtw-au\":[\"완료 열로 설정\"],\"wwu18a\":[\"아이콘\"],\"x52RAh\":[\"Blocked by \",[\"blockedCount\"],\" unfinished card(s)\"],\"y1eoq1\":[\"링크 복사\"],\"y9cj46\":[\"그룹: 우선순위\"],\"ybGQtY\":[\"← 목록으로\"],\"yz7wBu\":[\"닫기\"],\"yzF66j\":[\"링크\"],\"zOc0vf\":[\"아이콘 없음\"],\"zga9sT\":[\"확인\"]}");
\ No newline at end of file
+/*eslint-disable*/export const messages=JSON.parse("{\"--lIxB\":[\"Blocked by\"],\"-b7T3G\":[\"Updated\"],\"1DBGsz\":[\"노트\"],\"1YABGm\":[\"링크 (Ctrl+K)\"],\"1hKEom\":[\"우선순위\"],\"2wxgft\":[\"이름 변경\"],\"3qkggm\":[\"전체 화면\"],\"4gdyen\":[\"로컈 (내 것)\"],\"4hJhzz\":[\"테이블\"],\"54sFiP\":[\"flowchart TD\\n A[시작] --> B[끝]\"],\"5Q_DQ6\":[\"인라인 코드\"],\"7VpPHA\":[\"확인\"],\"7s3WlU\":[\"Blocks\"],\"8PifYj\":[\"Mermaid 다이어그램\"],\"8hSn0h\":[\"결과 (편집 가능)\"],\"8lE269\":[\"정렬: 수동\"],\"9gxam6\":[\"이 Draw.io 다이어그램을 렌더링할 수 없습니다.\"],\"AC9Gkf\":[\"열 펼치기\"],\"AS5WO9\":[\"이 PDF를 렌더링할 수 없습니다.\"],\"AVreQ5\":[\"드래그하여 크기 조정\"],\"AgvHni\":[\"열 추가\"],\"AxAubu\":[\"그룹: 담당자\"],\"BfMZ7w\":[\"클라우드 수낙\"],\"BnmEvM\":[\"템플릿으로 저장\"],\"C6-ZRl\":[\"Someone\"],\"EWPtMO\":[\"코드\"],\"EbMPZJ\":[\"미할당\"],\"G4qrLy\":[\"완료 열 해제\"],\"GKu3m4\":[\"라벨 없음\"],\"Gpfctt\":[\"마감\"],\"H_SQFv\":[\"색상 없음\"],\"I6SWEy\":[\"스플릿\"],\"ICip_B\":[\"클라우드 (원격)\"],\"Ik60OC\":[\"에디터에서 열기\"],\"Iw6WJa\":[\"WIP 한도 설정\"],\"JTYvAw\":[\"카드 검색\"],\"K_F6pa\":[\"저장 중…\"],\"KjXDqG\":[\"Swimlane: None\"],\"KmydK6\":[\"굵게\"],\"KvW1VO\":[\"Draw.io 다이어그램\"],\"LQn6-8\":[\"로컈 수낙\"],\"MHrjPM\":[\"제목\"],\"Mm72la\":[\"No comments yet\"],\"NBdIgR\":[\"Comment\"],\"OYHzN1\":[\"태그\"],\"OepdfE\":[\"그룹: 상태\"],\"Q2mGA7\":[\"필터 지우기\"],\"QD8opX\":[\"보드\"],\"QlsPZy\":[\"Mermaid 구문을 작성하면 다이어그램이 표시됩니다.\"],\"S5Qbb1\":[\"쉼표로 구분\"],\"TdfEV7\":[\"Archived\"],\"UQOvxZ\":[\"빈 카드\"],\"VNa_N2\":[\"이 파일 형식은 아직 미리볼 수 없습니다.\"],\"VbyRUy\":[\"Comments\"],\"WSP6v1\":[\"정렬: 우선순위\"],\"X03-eC\":[\"값을 입력해 주세요.\"],\"XJOV1Y\":[\"Activity\"],\"Ya7bZl\":[\"다이어그램 오류\"],\"Zot9XS\":[\"카드 없음\"],\"_5CsXX\":[\"완료 열\"],\"_EsjyQ\":[\"이것 사용\"],\"a6uhHr\":[\"굵게 (Ctrl+B)\"],\"aDvLhk\":[\"Add a comment…\"],\"abUZlY\":[\"세부정보 추가...\"],\"agOeRN\":[\"이 API 명세를 렌더링할 수 없습니다.\"],\"b4hVKD\":[\"색상 열\"],\"cfaWH-\":[\"라벨 추가\"],\"cnGeoo\":[\"삭제\"],\"d-F6q9\":[\"Created\"],\"d5z6xQ\":[\"WIP 한도 \",[\"0\"]],\"dEgA5A\":[\"취소\"],\"euc6Ns\":[\"복제\"],\"fYcKtB\":[\"정렬: 마감\"],\"gLDJuJ\":[\"제목 없는 카드\"],\"hh4sEG\":[\"Relates\"],\"hnK1gR\":[\"PDF 문서\"],\"i4_LY_\":[\"작성\"],\"iTylMl\":[\"템플릿\"],\"iYVqZq\":[\"열 이름\"],\"jUbC3Z\":[\"Swimlane: Priority\"],\"jZlrte\":[\"색상\"],\"kZlRKE\":[\"Mermaid 소스\"],\"kryGs-\":[\"카드\"],\"lCF0wC\":[\"새로고침\"],\"lHxVTh\":[\"Swimlane: Assignee\"],\"ltF1xa\":[\"병합 결과 저장\"],\"nabda1\":[\"카드 삭제\"],\"njJFtc\":[\"Delete comment\"],\"o7J4JM\":[\"필터\"],\"o8va6N\":[\"Restored\"],\"ojKCLU\":[\"담당자\"],\"p9yTeb\":[\"정렬: 제목\"],\"pKztsX\":[\"전체 에디터에서 열기\"],\"pnrmSP\":[\"새 카드\"],\"pwN6Ae\":[\"열 접기\"],\"pzutoc\":[\"기울임꼴\"],\"rdUucN\":[\"미리보기\"],\"sCzmvQ\":[\"개 카드\"],\"sQpDn6\":[\"전체 화면 종료\"],\"tK2x9T\":[\"⚠ 해결할 충돌 \",[\"0\"],\"건\",[\"1\"]],\"u2IprG\":[\"카드 제목 (Enter로 추가, Esc로 취소)\"],\"uAQUqI\":[\"상태\"],\"ucJg3u\":[\"Swimlane: Status\"],\"wf6Djn\":[\"기울임꼴 (Ctrl+I)\"],\"wtw-au\":[\"완료 열로 설정\"],\"wwu18a\":[\"아이콘\"],\"x52RAh\":[\"Blocked by \",[\"blockedCount\"],\" unfinished card(s)\"],\"y1eoq1\":[\"링크 복사\"],\"y9cj46\":[\"그룹: 우선순위\"],\"ybGQtY\":[\"← 목록으로\"],\"yz7wBu\":[\"닫기\"],\"yzF66j\":[\"링크\"],\"zOc0vf\":[\"아이콘 없음\"],\"zga9sT\":[\"확인\"]}");
\ No newline at end of file
diff --git a/shared/i18n/locales/ko/messages.po b/shared/i18n/locales/ko/messages.po
index 0d4ca4b..6c852e0 100644
--- a/shared/i18n/locales/ko/messages.po
+++ b/shared/i18n/locales/ko/messages.po
@@ -290,6 +290,7 @@ msgstr "Mermaid 소스"
msgid "New card"
msgstr "새 카드"
+#: shared/components/board/BoardSwimlanes.tsx
#: shared/components/board/BoardTable.tsx
msgid "No cards"
msgstr "카드 없음"
@@ -419,6 +420,22 @@ msgstr "스플릿"
msgid "Status"
msgstr "상태"
+#: shared/components/board/BoardSurface.tsx
+msgid "Swimlane: Assignee"
+msgstr ""
+
+#: shared/components/board/BoardSurface.tsx
+msgid "Swimlane: None"
+msgstr ""
+
+#: shared/components/board/BoardSurface.tsx
+msgid "Swimlane: Priority"
+msgstr ""
+
+#: shared/components/board/BoardSurface.tsx
+msgid "Swimlane: Status"
+msgstr ""
+
#: shared/components/board/BoardSurface.tsx
#: shared/components/EditorToolbar.tsx
msgid "Table"
diff --git a/shared/i18n/locales/zh/messages.mjs b/shared/i18n/locales/zh/messages.mjs
index 56c2ffc..1039f41 100644
--- a/shared/i18n/locales/zh/messages.mjs
+++ b/shared/i18n/locales/zh/messages.mjs
@@ -1 +1 @@
-/*eslint-disable*/export const messages=JSON.parse("{\"--lIxB\":[\"被阻塞于\"],\"-b7T3G\":[\"更新\"],\"1DBGsz\":[\"备注\"],\"1YABGm\":[\"链接 (Ctrl+K)\"],\"1hKEom\":[\"优先级\"],\"2wxgft\":[\"重命名\"],\"3qkggm\":[\"全屏\"],\"4gdyen\":[\"本地(我的)\"],\"4hJhzz\":[\"表格\"],\"54sFiP\":[\"flowchart TD\\n A[开始] --> B[结束]\"],\"5Q_DQ6\":[\"行内代码\"],\"7VpPHA\":[\"确认\"],\"7s3WlU\":[\"阻塞\"],\"8PifYj\":[\"Mermaid 图表\"],\"8hSn0h\":[\"结果(可编辑)\"],\"8lE269\":[\"排序:手动\"],\"9gxam6\":[\"无法渲染此 Draw.io 图表。\"],\"AC9Gkf\":[\"展开列\"],\"AS5WO9\":[\"无法渲染此 PDF。\"],\"AVreQ5\":[\"拖动调整宽度\"],\"AgvHni\":[\"添加列\"],\"AxAubu\":[\"分组:负责人\"],\"BfMZ7w\":[\"接受云端\"],\"BnmEvM\":[\"存为模板\"],\"C6-ZRl\":[\"某人\"],\"EWPtMO\":[\"代码\"],\"EbMPZJ\":[\"未分配\"],\"G4qrLy\":[\"取消完成列\"],\"GKu3m4\":[\"暂无标签\"],\"Gpfctt\":[\"截止日期\"],\"H_SQFv\":[\"无颜色\"],\"I6SWEy\":[\"分栏\"],\"ICip_B\":[\"云端(远程)\"],\"Ik60OC\":[\"在编辑器中打开\"],\"Iw6WJa\":[\"设置 WIP 限制\"],\"JTYvAw\":[\"搜索卡片\"],\"K_F6pa\":[\"保存中…\"],\"KmydK6\":[\"粗体\"],\"KvW1VO\":[\"Draw.io 图表\"],\"LQn6-8\":[\"接受本地\"],\"MHrjPM\":[\"标题\"],\"Mm72la\":[\"暂无评论\"],\"NBdIgR\":[\"评论\"],\"OYHzN1\":[\"标签\"],\"OepdfE\":[\"分组:状态\"],\"Q2mGA7\":[\"清除筛选\"],\"QD8opX\":[\"看板\"],\"QlsPZy\":[\"输入 Mermaid 语法以查看图表。\"],\"S5Qbb1\":[\"用逗号分隔\"],\"TdfEV7\":[\"归档\"],\"UQOvxZ\":[\"空白卡片\"],\"VNa_N2\":[\"暂不支持预览此文件类型。\"],\"VbyRUy\":[\"评论\"],\"WSP6v1\":[\"排序:优先级\"],\"X03-eC\":[\"请输入内容。\"],\"XJOV1Y\":[\"活动\"],\"Ya7bZl\":[\"图表错误\"],\"Zot9XS\":[\"暂无卡片\"],\"_5CsXX\":[\"完成列\"],\"_EsjyQ\":[\"使用此版本\"],\"a6uhHr\":[\"粗体 (Ctrl+B)\"],\"aDvLhk\":[\"添加评论…\"],\"abUZlY\":[\"添加详情...\"],\"agOeRN\":[\"无法渲染此 API 规范。\"],\"b4hVKD\":[\"彩色列\"],\"cfaWH-\":[\"添加标签\"],\"cnGeoo\":[\"删除\"],\"d-F6q9\":[\"创建\"],\"d5z6xQ\":[\"WIP 限制 \",[\"0\"]],\"dEgA5A\":[\"取消\"],\"euc6Ns\":[\"复制卡片\"],\"fYcKtB\":[\"排序:截止\"],\"gLDJuJ\":[\"未命名卡片\"],\"hh4sEG\":[\"相关\"],\"hnK1gR\":[\"PDF 文档\"],\"i4_LY_\":[\"写作\"],\"iTylMl\":[\"模板\"],\"iYVqZq\":[\"列名称\"],\"jZlrte\":[\"颜色\"],\"kZlRKE\":[\"Mermaid 源码\"],\"kryGs-\":[\"卡片\"],\"lCF0wC\":[\"刷新\"],\"ltF1xa\":[\"保存合并结果\"],\"nabda1\":[\"删除卡片\"],\"njJFtc\":[\"删除评论\"],\"o7J4JM\":[\"筛选\"],\"o8va6N\":[\"恢复\"],\"ojKCLU\":[\"负责人\"],\"p9yTeb\":[\"排序:标题\"],\"pKztsX\":[\"在完整编辑器中打开\"],\"pnrmSP\":[\"新建卡片\"],\"pwN6Ae\":[\"折叠列\"],\"pzutoc\":[\"斜体\"],\"rdUucN\":[\"预览\"],\"sCzmvQ\":[\"张卡片\"],\"sQpDn6\":[\"退出全屏\"],\"tK2x9T\":[\"⚠ \",[\"0\"],\" 个冲突\",[\"1\"],\"待解决\"],\"u2IprG\":[\"卡片标题(回车添加,Esc 取消)\"],\"uAQUqI\":[\"状态\"],\"wf6Djn\":[\"斜体 (Ctrl+I)\"],\"wtw-au\":[\"设为完成列\"],\"wwu18a\":[\"图标\"],\"x52RAh\":[\"被 \",[\"blockedCount\"],\" 张未完成卡片阻塞\"],\"y1eoq1\":[\"复制链接\"],\"y9cj46\":[\"分组:优先级\"],\"ybGQtY\":[\"← 返回列表\"],\"yz7wBu\":[\"关闭\"],\"yzF66j\":[\"链接\"],\"zOc0vf\":[\"无图标\"],\"zga9sT\":[\"确定\"]}");
\ No newline at end of file
+/*eslint-disable*/export const messages=JSON.parse("{\"--lIxB\":[\"被阻塞于\"],\"-b7T3G\":[\"更新\"],\"1DBGsz\":[\"备注\"],\"1YABGm\":[\"链接 (Ctrl+K)\"],\"1hKEom\":[\"优先级\"],\"2wxgft\":[\"重命名\"],\"3qkggm\":[\"全屏\"],\"4gdyen\":[\"本地(我的)\"],\"4hJhzz\":[\"表格\"],\"54sFiP\":[\"flowchart TD\\n A[开始] --> B[结束]\"],\"5Q_DQ6\":[\"行内代码\"],\"7VpPHA\":[\"确认\"],\"7s3WlU\":[\"阻塞\"],\"8PifYj\":[\"Mermaid 图表\"],\"8hSn0h\":[\"结果(可编辑)\"],\"8lE269\":[\"排序:手动\"],\"9gxam6\":[\"无法渲染此 Draw.io 图表。\"],\"AC9Gkf\":[\"展开列\"],\"AS5WO9\":[\"无法渲染此 PDF。\"],\"AVreQ5\":[\"拖动调整宽度\"],\"AgvHni\":[\"添加列\"],\"AxAubu\":[\"分组:负责人\"],\"BfMZ7w\":[\"接受云端\"],\"BnmEvM\":[\"存为模板\"],\"C6-ZRl\":[\"某人\"],\"EWPtMO\":[\"代码\"],\"EbMPZJ\":[\"未分配\"],\"G4qrLy\":[\"取消完成列\"],\"GKu3m4\":[\"暂无标签\"],\"Gpfctt\":[\"截止日期\"],\"H_SQFv\":[\"无颜色\"],\"I6SWEy\":[\"分栏\"],\"ICip_B\":[\"云端(远程)\"],\"Ik60OC\":[\"在编辑器中打开\"],\"Iw6WJa\":[\"设置 WIP 限制\"],\"JTYvAw\":[\"搜索卡片\"],\"K_F6pa\":[\"保存中…\"],\"KjXDqG\":[\"泳道:无\"],\"KmydK6\":[\"粗体\"],\"KvW1VO\":[\"Draw.io 图表\"],\"LQn6-8\":[\"接受本地\"],\"MHrjPM\":[\"标题\"],\"Mm72la\":[\"暂无评论\"],\"NBdIgR\":[\"评论\"],\"OYHzN1\":[\"标签\"],\"OepdfE\":[\"分组:状态\"],\"Q2mGA7\":[\"清除筛选\"],\"QD8opX\":[\"看板\"],\"QlsPZy\":[\"输入 Mermaid 语法以查看图表。\"],\"S5Qbb1\":[\"用逗号分隔\"],\"TdfEV7\":[\"归档\"],\"UQOvxZ\":[\"空白卡片\"],\"VNa_N2\":[\"暂不支持预览此文件类型。\"],\"VbyRUy\":[\"评论\"],\"WSP6v1\":[\"排序:优先级\"],\"X03-eC\":[\"请输入内容。\"],\"XJOV1Y\":[\"活动\"],\"Ya7bZl\":[\"图表错误\"],\"Zot9XS\":[\"暂无卡片\"],\"_5CsXX\":[\"完成列\"],\"_EsjyQ\":[\"使用此版本\"],\"a6uhHr\":[\"粗体 (Ctrl+B)\"],\"aDvLhk\":[\"添加评论…\"],\"abUZlY\":[\"添加详情...\"],\"agOeRN\":[\"无法渲染此 API 规范。\"],\"b4hVKD\":[\"彩色列\"],\"cfaWH-\":[\"添加标签\"],\"cnGeoo\":[\"删除\"],\"d-F6q9\":[\"创建\"],\"d5z6xQ\":[\"WIP 限制 \",[\"0\"]],\"dEgA5A\":[\"取消\"],\"euc6Ns\":[\"复制卡片\"],\"fYcKtB\":[\"排序:截止\"],\"gLDJuJ\":[\"未命名卡片\"],\"hh4sEG\":[\"相关\"],\"hnK1gR\":[\"PDF 文档\"],\"i4_LY_\":[\"写作\"],\"iTylMl\":[\"模板\"],\"iYVqZq\":[\"列名称\"],\"jUbC3Z\":[\"泳道:优先级\"],\"jZlrte\":[\"颜色\"],\"kZlRKE\":[\"Mermaid 源码\"],\"kryGs-\":[\"卡片\"],\"lCF0wC\":[\"刷新\"],\"lHxVTh\":[\"泳道:负责人\"],\"ltF1xa\":[\"保存合并结果\"],\"nabda1\":[\"删除卡片\"],\"njJFtc\":[\"删除评论\"],\"o7J4JM\":[\"筛选\"],\"o8va6N\":[\"恢复\"],\"ojKCLU\":[\"负责人\"],\"p9yTeb\":[\"排序:标题\"],\"pKztsX\":[\"在完整编辑器中打开\"],\"pnrmSP\":[\"新建卡片\"],\"pwN6Ae\":[\"折叠列\"],\"pzutoc\":[\"斜体\"],\"rdUucN\":[\"预览\"],\"sCzmvQ\":[\"张卡片\"],\"sQpDn6\":[\"退出全屏\"],\"tK2x9T\":[\"⚠ \",[\"0\"],\" 个冲突\",[\"1\"],\"待解决\"],\"u2IprG\":[\"卡片标题(回车添加,Esc 取消)\"],\"uAQUqI\":[\"状态\"],\"ucJg3u\":[\"泳道:状态\"],\"wf6Djn\":[\"斜体 (Ctrl+I)\"],\"wtw-au\":[\"设为完成列\"],\"wwu18a\":[\"图标\"],\"x52RAh\":[\"被 \",[\"blockedCount\"],\" 张未完成卡片阻塞\"],\"y1eoq1\":[\"复制链接\"],\"y9cj46\":[\"分组:优先级\"],\"ybGQtY\":[\"← 返回列表\"],\"yz7wBu\":[\"关闭\"],\"yzF66j\":[\"链接\"],\"zOc0vf\":[\"无图标\"],\"zga9sT\":[\"确定\"]}");
\ No newline at end of file
diff --git a/shared/i18n/locales/zh/messages.po b/shared/i18n/locales/zh/messages.po
index 98fdcf7..626e528 100644
--- a/shared/i18n/locales/zh/messages.po
+++ b/shared/i18n/locales/zh/messages.po
@@ -290,6 +290,7 @@ msgstr "Mermaid 源码"
msgid "New card"
msgstr "新建卡片"
+#: shared/components/board/BoardSwimlanes.tsx
#: shared/components/board/BoardTable.tsx
msgid "No cards"
msgstr "暂无卡片"
@@ -419,6 +420,22 @@ msgstr "分栏"
msgid "Status"
msgstr "状态"
+#: shared/components/board/BoardSurface.tsx
+msgid "Swimlane: Assignee"
+msgstr "泳道:负责人"
+
+#: shared/components/board/BoardSurface.tsx
+msgid "Swimlane: None"
+msgstr "泳道:无"
+
+#: shared/components/board/BoardSurface.tsx
+msgid "Swimlane: Priority"
+msgstr "泳道:优先级"
+
+#: shared/components/board/BoardSurface.tsx
+msgid "Swimlane: Status"
+msgstr "泳道:状态"
+
#: shared/components/board/BoardSurface.tsx
#: shared/components/EditorToolbar.tsx
msgid "Table"
diff --git a/shared/lib/board.ts b/shared/lib/board.ts
index dac9f6b..110e034 100644
--- a/shared/lib/board.ts
+++ b/shared/lib/board.ts
@@ -24,6 +24,11 @@ export type BoardViewConfig = {
colorColumns?: boolean;
viewType?: BoardViewType;
groupBy?: BoardGroupKey;
+ /**
+ * Second grouping dimension rendered as horizontal swimlanes (rows) in the
+ * board view. Must differ from `groupBy`; unset = no swimlanes.
+ */
+ swimlaneBy?: BoardGroupKey;
};
export type BoardTag = { id?: string; label: string; color?: string | null };
@@ -204,6 +209,30 @@ export function effectiveColumns(
.map((v) => ({ key: v, name: v || unassignedLabel }));
}
+/**
+ * Bucket cards into a swimlane grid: laneValue → columnValue → cards. Lane and
+ * column values come from `groupValueOf` under the two grouping dimensions. The
+ * board view renders rows (lanes) × columns from this. Order within a cell is the
+ * caller's responsibility (pre-sort, e.g. with sortCards).
+ */
+export function partitionSwimlanes(
+ cards: BoardViewCard[],
+ groupBy: BoardGroupKey,
+ swimlaneBy: BoardGroupKey,
+): Map
> {
+ const grid = new Map>();
+ for (const c of cards) {
+ const lane = groupValueOf(c, swimlaneBy);
+ const col = groupValueOf(c, groupBy);
+ let row = grid.get(lane);
+ if (!row) grid.set(lane, (row = new Map()));
+ let cell = row.get(col);
+ if (!cell) row.set(col, (cell = []));
+ cell.push(c);
+ }
+ return grid;
+}
+
export function cardMatchesFilter(card: BoardViewCard, filter: CardFilter | null): boolean {
if (!filter) return true;
if (filter.prop === "priority") return (card.priority || "none") === filter.value;
diff --git a/src/components/BoardView.tsx b/src/components/BoardView.tsx
index 054af5f..87b87dc 100644
--- a/src/components/BoardView.tsx
+++ b/src/components/BoardView.tsx
@@ -103,6 +103,7 @@ export function BoardView({ boardPath, boardRelativePath }: { boardPath: string;
doneColumn: config.doneColumn,
colorColumns: config.colorColumns,
viewType: config.viewType,
+ swimlaneBy: config.swimlaneBy as BoardViewConfig["swimlaneBy"],
groupBy: (config.groupBy as BoardViewConfig["groupBy"]) || "status",
}
: { title: boardName, columns: [] },
diff --git a/src/lib/types.ts b/src/lib/types.ts
index 72de955..921f855 100644
--- a/src/lib/types.ts
+++ b/src/lib/types.ts
@@ -21,6 +21,8 @@ export type BoardConfig = {
colorColumns?: boolean;
/** Which renderer this board shows: kanban columns or a flat table. Defaults to "board". */
viewType?: "board" | "table";
+ /** Optional second grouping dimension rendered as swimlane rows in the board view. */
+ swimlaneBy?: "status" | "priority" | "assignee";
};
/** A card = a real `.md` note that belongs to a board (frontmatter `board == id`). */
diff --git a/tests/unit/boardSwimlanes.spec.ts b/tests/unit/boardSwimlanes.spec.ts
new file mode 100644
index 0000000..38ad722
--- /dev/null
+++ b/tests/unit/boardSwimlanes.spec.ts
@@ -0,0 +1,36 @@
+import { test, expect } from "@playwright/test";
+import { partitionSwimlanes, type BoardViewCard } from "../../shared/lib/board";
+
+// Pure-logic acceptance for C4 swimlanes: the 2-D board view buckets cards into
+// laneValue → columnValue → cards, which partitionSwimlanes computes.
+
+function c(id: string, columnKey: string, extra: Partial = {}): BoardViewCard {
+ return { id, columnKey, position: 0, title: id, tags: [], ...extra };
+}
+
+test("partitionSwimlanes buckets by lane (swimlane dim) then column (group dim)", () => {
+ const cards = [
+ c("a", "todo", { priority: "high" }),
+ c("b", "todo", { priority: "low" }),
+ c("c", "done", { priority: "high" }),
+ c("d", "todo", { priority: "high" }),
+ ];
+ // columns = status, lanes = priority
+ const grid = partitionSwimlanes(cards, "status", "priority");
+ expect(grid.get("high")!.get("todo")!.map((x) => x.id).sort()).toEqual(["a", "d"]);
+ expect(grid.get("high")!.get("done")!.map((x) => x.id)).toEqual(["c"]);
+ expect(grid.get("low")!.get("todo")!.map((x) => x.id)).toEqual(["b"]);
+ expect(grid.get("low")!.has("done")).toBe(false);
+});
+
+test("partitionSwimlanes uses 'none'/'' fallbacks for missing dimension values", () => {
+ const cards = [c("a", "todo"), c("b", "todo", { assignee: "kim" })];
+ // lanes = assignee → missing assignee buckets under "" (unassigned)
+ const grid = partitionSwimlanes(cards, "status", "assignee");
+ expect(grid.get("")!.get("todo")!.map((x) => x.id)).toEqual(["a"]);
+ expect(grid.get("kim")!.get("todo")!.map((x) => x.id)).toEqual(["b"]);
+
+ // lanes = priority → missing priority buckets under "none"
+ const grid2 = partitionSwimlanes(cards, "status", "priority");
+ expect(grid2.get("none")!.get("todo")!.map((x) => x.id).sort()).toEqual(["a", "b"]);
+});