refactor: update names, load defaults from package.json

This commit is contained in:
2026-07-15 16:02:30 +05:00
parent 9b27e94d04
commit 4539d39576
13 changed files with 146 additions and 79 deletions
+3 -3
View File
@@ -1,4 +1,4 @@
# xboct-flow — VS Code Extension # XBOCTuK Flow — VS Code Extension
## Хранение ## Хранение
@@ -22,7 +22,7 @@ updated: 2026-07-12T10:00:00Z
**Структура папок:** **Структура папок:**
- `.vscode/tasks/` — проектные задачи - `.vscode/tasks/` — проектные задачи
- глобальная папка — настраивается (`xboctFlow.globalPath`) - глобальная папка — настраивается (`xboctukFlow.globalPath`)
Оба пути настраиваются через VS Code settings. Оба пути настраиваются через VS Code settings.
@@ -30,7 +30,7 @@ updated: 2026-07-12T10:00:00Z
Через **Custom Editor** + **WebView**: Через **Custom Editor** + **WebView**:
1. **Activity Bar** — вкладка XBOCT flow 1. **Activity Bar** — вкладка XBOCTuK Flow
2. **Task Dashboard** — WebView в editor area со split layout: 2. **Task Dashboard** — WebView в editor area со split layout:
- Левая панель — список задач с фильтрацией/группировкой - Левая панель — список задач с фильтрацией/группировкой
- Правая панель — содержимое выбранной задачи (редактируемый markdown) - Правая панель — содержимое выбранной задачи (редактируемый markdown)
+2 -2
View File
@@ -1,11 +1,11 @@
{ {
"name": "xboct-flow", "name": "xboctuk-flow",
"version": "0.0.1", "version": "0.0.1",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "xboct-flow", "name": "xboctuk-flow",
"version": "0.0.1", "version": "0.0.1",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
+46 -46
View File
@@ -1,6 +1,6 @@
{ {
"name": "xboct-flow", "name": "xboctuk-flow",
"displayName": "XBOCT flow", "displayName": "XBOCTuK Flow",
"description": "Local-first task management for solo developers and small teams", "description": "Local-first task management for solo developers and small teams",
"version": "0.0.1", "version": "0.0.1",
"publisher": "", "publisher": "",
@@ -25,55 +25,55 @@
"contributes": { "contributes": {
"commands": [ "commands": [
{ {
"command": "xboctFlow.createTask", "command": "xboctukFlow.createTask",
"title": "XBOCT flow: Create Task", "title": "XBOCTuK Flow: Create Task",
"icon": "$(add)" "icon": "$(add)"
}, },
{ {
"command": "xboctFlow.openTask", "command": "xboctukFlow.openTask",
"title": "XBOCT flow: Open Task", "title": "XBOCTuK Flow: Open Task",
"icon": "$(go-to-file)" "icon": "$(go-to-file)"
}, },
{ {
"command": "xboctFlow.deleteTask", "command": "xboctukFlow.deleteTask",
"title": "XBOCT flow: Delete Task", "title": "XBOCTuK Flow: Delete Task",
"icon": "$(trash)" "icon": "$(trash)"
}, },
{ {
"command": "xboctFlow.changeStatus", "command": "xboctukFlow.changeStatus",
"title": "XBOCT flow: Change Status", "title": "XBOCTuK Flow: Change Status",
"icon": "$(sync)" "icon": "$(sync)"
}, },
{ {
"command": "xboctFlow.openDashboard", "command": "xboctukFlow.openDashboard",
"title": "XBOCT flow: Open Dashboard", "title": "XBOCTuK Flow: Open Dashboard",
"icon": "$(dashboard)" "icon": "$(dashboard)"
}, },
{ {
"command": "xboctFlow.openInDashboard", "command": "xboctukFlow.openInDashboard",
"title": "XBOCT flow: Edit in Dashboard", "title": "XBOCTuK Flow: Edit in Dashboard",
"icon": "$(edit)" "icon": "$(edit)"
}, },
{ {
"command": "xboctFlow.openKanban", "command": "xboctukFlow.openKanban",
"title": "XBOCT flow: Open Kanban", "title": "XBOCTuK Flow: Open Kanban",
"icon": "$(layout)" "icon": "$(layout)"
} }
], ],
"configuration": { "configuration": {
"title": "XBOCT flow", "title": "XBOCTuK Flow",
"properties": { "properties": {
"xboctFlow.projectPath": { "xboctukFlow.projectPath": {
"type": "string", "type": "string",
"default": ".vscode/tasks", "default": ".vscode/tasks",
"description": "Path to project-level tasks folder (relative to workspace root)" "description": "Path to project-level tasks folder (relative to workspace root)"
}, },
"xboctFlow.globalPath": { "xboctukFlow.globalPath": {
"type": "string", "type": "string",
"default": "", "default": "",
"description": "Path to global tasks folder (absolute path, shared across projects)" "description": "Path to global tasks folder (absolute path, shared across projects)"
}, },
"xboctFlow.fileExtension": { "xboctukFlow.fileExtension": {
"type": "string", "type": "string",
"default": ".task.md", "default": ".task.md",
"enum": [ "enum": [
@@ -88,17 +88,17 @@
"viewsContainers": { "viewsContainers": {
"activitybar": [ "activitybar": [
{ {
"id": "xboctFlow", "id": "xboctukFlow",
"title": "XBOCT flow", "title": "XBOCTuK Flow",
"icon": "media/icon.svg" "icon": "media/icon.svg"
} }
] ]
}, },
"views": { "views": {
"xboctFlow": [ "xboctukFlow": [
{ {
"type": "tree", "type": "tree",
"id": "xboctFlow.tasks", "id": "xboctukFlow.tasks",
"name": "Tasks", "name": "Tasks",
"icon": "media/icon.svg" "icon": "media/icon.svg"
} }
@@ -107,60 +107,60 @@
"menus": { "menus": {
"view/title": [ "view/title": [
{ {
"command": "xboctFlow.createTask", "command": "xboctukFlow.createTask",
"when": "view == xboctFlow.tasks", "when": "view == xboctukFlow.tasks",
"group": "navigation@1" "group": "navigation@1"
}, },
{ {
"command": "xboctFlow.openDashboard", "command": "xboctukFlow.openDashboard",
"when": "view == xboctFlow.tasks", "when": "view == xboctukFlow.tasks",
"group": "navigation@2" "group": "navigation@2"
}, },
{ {
"command": "xboctFlow.openKanban", "command": "xboctukFlow.openKanban",
"when": "view == xboctFlow.tasks", "when": "view == xboctukFlow.tasks",
"group": "navigation@3" "group": "navigation@3"
} }
], ],
"view/item/context": [ "view/item/context": [
{ {
"command": "xboctFlow.openTask", "command": "xboctukFlow.openTask",
"when": "view == xboctFlow.tasks && viewItem == task", "when": "view == xboctukFlow.tasks && viewItem == task",
"group": "inline@1" "group": "inline@1"
}, },
{ {
"command": "xboctFlow.openInDashboard", "command": "xboctukFlow.openInDashboard",
"when": "view == xboctFlow.tasks && viewItem == task", "when": "view == xboctukFlow.tasks && viewItem == task",
"group": "inline@2" "group": "inline@2"
}, },
{ {
"command": "xboctFlow.changeStatus", "command": "xboctukFlow.changeStatus",
"when": "view == xboctFlow.tasks && viewItem == task", "when": "view == xboctukFlow.tasks && viewItem == task",
"group": "inline@3" "group": "inline@3"
}, },
{ {
"command": "xboctFlow.deleteTask", "command": "xboctukFlow.deleteTask",
"when": "view == xboctFlow.tasks && viewItem == task", "when": "view == xboctukFlow.tasks && viewItem == task",
"group": "inline@4" "group": "inline@4"
}, },
{ {
"command": "xboctFlow.openTask", "command": "xboctukFlow.openTask",
"when": "view == xboctFlow.tasks && viewItem == task", "when": "view == xboctukFlow.tasks && viewItem == task",
"group": "1_task@1" "group": "1_task@1"
}, },
{ {
"command": "xboctFlow.openInDashboard", "command": "xboctukFlow.openInDashboard",
"when": "view == xboctFlow.tasks && viewItem == task", "when": "view == xboctukFlow.tasks && viewItem == task",
"group": "1_task@2" "group": "1_task@2"
}, },
{ {
"command": "xboctFlow.changeStatus", "command": "xboctukFlow.changeStatus",
"when": "view == xboctFlow.tasks && viewItem == task", "when": "view == xboctukFlow.tasks && viewItem == task",
"group": "1_task@3" "group": "1_task@3"
}, },
{ {
"command": "xboctFlow.deleteTask", "command": "xboctukFlow.deleteTask",
"when": "view == xboctFlow.tasks && viewItem == task", "when": "view == xboctukFlow.tasks && viewItem == task",
"group": "1_task@4" "group": "1_task@4"
} }
] ]
+60
View File
@@ -0,0 +1,60 @@
/**
* Reads extension settings metadata from package.json (single source of truth).
* Defaults / enum for Settings UI live in package.json `contributes.configuration`.
*/
import packageJson from "../package.json";
type ConfigProperty = {
type?: string;
default?: string | number | boolean | string[];
enum?: string[];
description?: string;
};
type PackageJsonShape = {
contributes: {
configuration: {
title: string;
properties: Record<string, ConfigProperty>;
};
};
};
const pkg = packageJson as PackageJsonShape;
const properties = pkg.contributes.configuration.properties;
/** VS Code configuration section (`xboctukFlow` from keys like `xboctukFlow.projectPath`). */
export function getConfigSection(): string {
const firstKey = Object.keys(properties)[0];
if (!firstKey) {
throw new Error("package.json has no contributes.configuration.properties");
}
const dot = firstKey.indexOf(".");
if (dot <= 0) {
throw new Error(`Invalid configuration key: ${firstKey}`);
}
return firstKey.slice(0, dot);
}
export type SettingKey = "projectPath" | "globalPath" | "fileExtension";
function property(key: SettingKey): ConfigProperty {
const fullKey = `${getConfigSection()}.${key}`;
const prop = properties[fullKey];
if (!prop) {
throw new Error(`Missing package.json setting: ${fullKey}`);
}
return prop;
}
/** Default from package.json (same as Settings UI default). */
export function getSettingDefault(key: SettingKey): string {
const value = property(key).default;
if (typeof value === "string") {
return value;
}
if (value === undefined || value === null) {
return "";
}
return String(value);
}
+3 -3
View File
@@ -22,7 +22,7 @@ export function activate(context: vscode.ExtensionContext) {
}; };
context.subscriptions.push( context.subscriptions.push(
vscode.window.registerTreeDataProvider("xboctFlow.tasks", tree), vscode.window.registerTreeDataProvider("xboctukFlow.tasks", tree),
); );
const ext = config.getFileExtension(); const ext = config.getFileExtension();
@@ -41,8 +41,8 @@ export function activate(context: vscode.ExtensionContext) {
vscode.StatusBarAlignment.Left, vscode.StatusBarAlignment.Left,
); );
statusBarItem.text = "$(checklist) Tasks"; statusBarItem.text = "$(checklist) Tasks";
statusBarItem.command = "xboctFlow.openDashboard"; statusBarItem.command = "xboctukFlow.openDashboard";
statusBarItem.tooltip = "XBOCT flow — Open Dashboard"; statusBarItem.tooltip = "XBOCTuK Flow — Open Dashboard";
statusBarItem.show(); statusBarItem.show();
context.subscriptions.push(statusBarItem); context.subscriptions.push(statusBarItem);
+13 -6
View File
@@ -1,5 +1,6 @@
import * as path from "path"; import * as path from "path";
import * as vscode from "vscode"; import * as vscode from "vscode";
import { getConfigSection, getSettingDefault } from "@/config";
import { IConfigProvider } from "@/ports"; import { IConfigProvider } from "@/ports";
export class VscodeConfigProvider implements IConfigProvider { export class VscodeConfigProvider implements IConfigProvider {
@@ -7,18 +8,24 @@ export class VscodeConfigProvider implements IConfigProvider {
const workspaceRoot = vscode.workspace.workspaceFolders?.[0]?.uri.fsPath; const workspaceRoot = vscode.workspace.workspaceFolders?.[0]?.uri.fsPath;
if (!workspaceRoot) return undefined; if (!workspaceRoot) return undefined;
const config = vscode.workspace.getConfiguration("xboctFlow"); const config = vscode.workspace.getConfiguration(getConfigSection());
const relativePath = config.get<string>("projectPath") ?? ".vscode/tasks"; // get() already merges package.json defaults; fallback for safety.
const relativePath =
config.get<string>("projectPath") ?? getSettingDefault("projectPath");
return path.join(workspaceRoot, relativePath); return path.join(workspaceRoot, relativePath);
} }
getGlobalTaskPath(): string | undefined { getGlobalTaskPath(): string | undefined {
const config = vscode.workspace.getConfiguration("xboctFlow"); const config = vscode.workspace.getConfiguration(getConfigSection());
return config.get<string>("globalPath") || undefined; const globalPath =
config.get<string>("globalPath") ?? getSettingDefault("globalPath");
return globalPath || undefined;
} }
getFileExtension(): string { getFileExtension(): string {
const config = vscode.workspace.getConfiguration("xboctFlow"); const config = vscode.workspace.getConfiguration(getConfigSection());
return config.get<string>("fileExtension") ?? ".task.md"; return (
config.get<string>("fileExtension") ?? getSettingDefault("fileExtension")
);
} }
} }
+7 -7
View File
@@ -226,27 +226,27 @@ export function registerTaskCommands(
deps: TaskCommandDeps, deps: TaskCommandDeps,
): void { ): void {
context.subscriptions.push( context.subscriptions.push(
vscode.commands.registerCommand("xboctFlow.createTask", () => vscode.commands.registerCommand("xboctukFlow.createTask", () =>
runCreateTask(deps), runCreateTask(deps),
), ),
vscode.commands.registerCommand("xboctFlow.openTask", (item?: unknown) => vscode.commands.registerCommand("xboctukFlow.openTask", (item?: unknown) =>
runOpenTask(deps, item), runOpenTask(deps, item),
), ),
vscode.commands.registerCommand("xboctFlow.deleteTask", (item?: unknown) => vscode.commands.registerCommand("xboctukFlow.deleteTask", (item?: unknown) =>
runDeleteTask(deps, item), runDeleteTask(deps, item),
), ),
vscode.commands.registerCommand( vscode.commands.registerCommand(
"xboctFlow.changeStatus", "xboctukFlow.changeStatus",
(item?: unknown) => runChangeStatus(deps, item), (item?: unknown) => runChangeStatus(deps, item),
), ),
vscode.commands.registerCommand("xboctFlow.openDashboard", () => vscode.commands.registerCommand("xboctukFlow.openDashboard", () =>
runOpenDashboard(deps), runOpenDashboard(deps),
), ),
vscode.commands.registerCommand( vscode.commands.registerCommand(
"xboctFlow.openInDashboard", "xboctukFlow.openInDashboard",
(item?: unknown) => runOpenInDashboard(deps, item), (item?: unknown) => runOpenInDashboard(deps, item),
), ),
vscode.commands.registerCommand("xboctFlow.openKanban", () => vscode.commands.registerCommand("xboctukFlow.openKanban", () =>
runOpenKanban(deps), runOpenKanban(deps),
), ),
); );
+3 -3
View File
@@ -56,8 +56,8 @@ export class TaskDashboardPanel {
} }
const panel = vscode.window.createWebviewPanel( const panel = vscode.window.createWebviewPanel(
"xboctFlow.dashboard", "xboctukFlow.dashboard",
"XBOCT flow", "XBOCTuK Flow",
vscode.ViewColumn.One, vscode.ViewColumn.One,
{ {
enableScripts: true, enableScripts: true,
@@ -181,7 +181,7 @@ export class TaskDashboardPanel {
return; return;
case "createTask": case "createTask":
await vscode.commands.executeCommand("xboctFlow.createTask"); await vscode.commands.executeCommand("xboctukFlow.createTask");
await this.reloadTasks(); await this.reloadTasks();
return; return;
} }
+2 -2
View File
@@ -39,8 +39,8 @@ export class TaskKanbanPanel {
} }
const panel = vscode.window.createWebviewPanel( const panel = vscode.window.createWebviewPanel(
"xboctFlow.kanban", "xboctukFlow.kanban",
"XBOCT flow — Kanban", "XBOCTuK Flow — Kanban",
vscode.ViewColumn.One, vscode.ViewColumn.One,
{ {
enableScripts: true, enableScripts: true,
+1 -1
View File
@@ -21,7 +21,7 @@ export class TaskTreeItem extends vscode.TreeItem {
}`, }`,
); );
this.command = { this.command = {
command: "xboctFlow.openTask", command: "xboctukFlow.openTask",
title: "Open Task", title: "Open Task",
arguments: [this], arguments: [this],
}; };
+3 -3
View File
@@ -47,15 +47,15 @@ headless/тестов можно оставить; editor-save — optional path
[x] **Боковая панель** [x] **Боковая панель**
- кнопка открытия dashboard (view title → `xboctFlow.openDashboard`) - кнопка открытия dashboard (view title → `xboctukFlow.openDashboard`)
- кнопки на задаче (inline): open file, edit in dashboard - кнопки на задаче (inline): open file, edit in dashboard
(`xboctFlow.openInDashboard` + preselect), delete (`xboctukFlow.openInDashboard` + preselect), delete
- change status остаётся в context menu - change status остаётся в context menu
[x] **Dashboard: кнопка создания задачи** [x] **Dashboard: кнопка создания задачи**
- `+ Task` в toolbar → inbound `createTask` → host - `+ Task` в toolbar → inbound `createTask` → host
`executeCommand("xboctFlow.createTask")` → reload list `executeCommand("xboctukFlow.createTask")` → reload list
[x] **Kanban** [x] **Kanban**
+2 -2
View File
@@ -3,7 +3,7 @@
"module": "commonjs", "module": "commonjs",
"target": "ES2022", "target": "ES2022",
"outDir": "out", "outDir": "out",
"rootDir": "src", "rootDir": ".",
"lib": ["ES2022"], "lib": ["ES2022"],
"sourceMap": true, "sourceMap": true,
"strict": true, "strict": true,
@@ -17,6 +17,6 @@
"@/*": ["./src/*"] "@/*": ["./src/*"]
} }
}, },
"include": ["src/**/*"], "include": ["src/**/*", "package.json"],
"exclude": ["node_modules", "out", ".vscode-test", "src/**/*.test.ts"] "exclude": ["node_modules", "out", ".vscode-test", "src/**/*.test.ts"]
} }
+1 -1
View File
@@ -6,6 +6,6 @@
"declaration": false, "declaration": false,
"types": ["node"] "types": ["node"]
}, },
"include": ["src/**/*", "tests/**/*"], "include": ["src/**/*", "tests/**/*", "package.json"],
"exclude": ["node_modules", "out", ".vscode-test"] "exclude": ["node_modules", "out", ".vscode-test"]
} }