github-issue-304-builtin (#309)
* takt: github-issue-304-builtin * ピース選択UIから「also in」表示を削除
This commit is contained in:
parent
99aa22d250
commit
80a79683ac
@ -61,6 +61,7 @@ vi.mock('../infra/config/global/pieceCategories.js', async (importOriginal) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const {
|
const {
|
||||||
|
BUILTIN_CATEGORY_NAME,
|
||||||
getPieceCategories,
|
getPieceCategories,
|
||||||
loadDefaultCategories,
|
loadDefaultCategories,
|
||||||
buildCategorizedPieces,
|
buildCategorizedPieces,
|
||||||
@ -129,6 +130,7 @@ piece_categories:
|
|||||||
{ name: 'Quick Start', pieces: ['default'], children: [] },
|
{ name: 'Quick Start', pieces: ['default'], children: [] },
|
||||||
]);
|
]);
|
||||||
expect(config!.userPieceCategories).toEqual([]);
|
expect(config!.userPieceCategories).toEqual([]);
|
||||||
|
expect(config!.hasUserCategories).toBe(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should use builtin categories when user overlay file is missing', () => {
|
it('should use builtin categories when user overlay file is missing', () => {
|
||||||
@ -147,11 +149,12 @@ others_category_name: Others
|
|||||||
{ name: 'Main', pieces: ['default'], children: [] },
|
{ name: 'Main', pieces: ['default'], children: [] },
|
||||||
]);
|
]);
|
||||||
expect(config!.userPieceCategories).toEqual([]);
|
expect(config!.userPieceCategories).toEqual([]);
|
||||||
|
expect(config!.hasUserCategories).toBe(false);
|
||||||
expect(config!.showOthersCategory).toBe(true);
|
expect(config!.showOthersCategory).toBe(true);
|
||||||
expect(config!.othersCategoryName).toBe('Others');
|
expect(config!.othersCategoryName).toBe('Others');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should merge user overlay categories with builtin categories', () => {
|
it('should separate user categories from builtin categories with builtin wrapper', () => {
|
||||||
writeYaml(join(resourcesDir, 'piece-categories.yaml'), `
|
writeYaml(join(resourcesDir, 'piece-categories.yaml'), `
|
||||||
piece_categories:
|
piece_categories:
|
||||||
Main:
|
Main:
|
||||||
@ -184,15 +187,22 @@ others_category_name: Unclassified
|
|||||||
const config = getPieceCategories(testDir);
|
const config = getPieceCategories(testDir);
|
||||||
expect(config).not.toBeNull();
|
expect(config).not.toBeNull();
|
||||||
expect(config!.pieceCategories).toEqual([
|
expect(config!.pieceCategories).toEqual([
|
||||||
|
{ name: 'Main', pieces: ['custom'], children: [] },
|
||||||
|
{ name: 'My Team', pieces: ['team-flow'], children: [] },
|
||||||
{
|
{
|
||||||
name: 'Main',
|
name: BUILTIN_CATEGORY_NAME,
|
||||||
pieces: ['custom'],
|
pieces: [],
|
||||||
children: [
|
children: [
|
||||||
{ name: 'Child', pieces: ['nested'], children: [] },
|
{
|
||||||
|
name: 'Main',
|
||||||
|
pieces: ['default', 'coding'],
|
||||||
|
children: [
|
||||||
|
{ name: 'Child', pieces: ['nested'], children: [] },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{ name: 'Review', pieces: ['review-only', 'e2e-test'], children: [] },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{ name: 'Review', pieces: ['review-only', 'e2e-test'], children: [] },
|
|
||||||
{ name: 'My Team', pieces: ['team-flow'], children: [] },
|
|
||||||
]);
|
]);
|
||||||
expect(config!.builtinPieceCategories).toEqual([
|
expect(config!.builtinPieceCategories).toEqual([
|
||||||
{
|
{
|
||||||
@ -208,6 +218,7 @@ others_category_name: Unclassified
|
|||||||
{ name: 'Main', pieces: ['custom'], children: [] },
|
{ name: 'Main', pieces: ['custom'], children: [] },
|
||||||
{ name: 'My Team', pieces: ['team-flow'], children: [] },
|
{ name: 'My Team', pieces: ['team-flow'], children: [] },
|
||||||
]);
|
]);
|
||||||
|
expect(config!.hasUserCategories).toBe(true);
|
||||||
expect(config!.showOthersCategory).toBe(false);
|
expect(config!.showOthersCategory).toBe(false);
|
||||||
expect(config!.othersCategoryName).toBe('Unclassified');
|
expect(config!.othersCategoryName).toBe('Unclassified');
|
||||||
});
|
});
|
||||||
@ -259,6 +270,7 @@ others_category_name: Unclassified
|
|||||||
{ name: 'Review', pieces: ['review-only'], children: [] },
|
{ name: 'Review', pieces: ['review-only'], children: [] },
|
||||||
]);
|
]);
|
||||||
expect(config!.userPieceCategories).toEqual([]);
|
expect(config!.userPieceCategories).toEqual([]);
|
||||||
|
expect(config!.hasUserCategories).toBe(false);
|
||||||
expect(config!.showOthersCategory).toBe(false);
|
expect(config!.showOthersCategory).toBe(false);
|
||||||
expect(config!.othersCategoryName).toBe('Unclassified');
|
expect(config!.othersCategoryName).toBe('Unclassified');
|
||||||
});
|
});
|
||||||
@ -290,6 +302,7 @@ describe('buildCategorizedPieces', () => {
|
|||||||
userPieceCategories: [
|
userPieceCategories: [
|
||||||
{ name: 'My Team', pieces: ['missing-user-piece'], children: [] },
|
{ name: 'My Team', pieces: ['missing-user-piece'], children: [] },
|
||||||
],
|
],
|
||||||
|
hasUserCategories: true,
|
||||||
showOthersCategory: true,
|
showOthersCategory: true,
|
||||||
othersCategoryName: 'Others',
|
othersCategoryName: 'Others',
|
||||||
};
|
};
|
||||||
@ -322,6 +335,7 @@ describe('buildCategorizedPieces', () => {
|
|||||||
{ name: 'Main', pieces: ['default'], children: [] },
|
{ name: 'Main', pieces: ['default'], children: [] },
|
||||||
],
|
],
|
||||||
userPieceCategories: [],
|
userPieceCategories: [],
|
||||||
|
hasUserCategories: false,
|
||||||
showOthersCategory: true,
|
showOthersCategory: true,
|
||||||
othersCategoryName: 'Others',
|
othersCategoryName: 'Others',
|
||||||
};
|
};
|
||||||
@ -346,6 +360,7 @@ describe('buildCategorizedPieces', () => {
|
|||||||
{ name: 'Main', pieces: ['default'], children: [] },
|
{ name: 'Main', pieces: ['default'], children: [] },
|
||||||
],
|
],
|
||||||
userPieceCategories: [],
|
userPieceCategories: [],
|
||||||
|
hasUserCategories: false,
|
||||||
showOthersCategory: false,
|
showOthersCategory: false,
|
||||||
othersCategoryName: 'Others',
|
othersCategoryName: 'Others',
|
||||||
};
|
};
|
||||||
@ -356,6 +371,52 @@ describe('buildCategorizedPieces', () => {
|
|||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should categorize pieces through builtin wrapper node', () => {
|
||||||
|
const allPieces = createPieceMap([
|
||||||
|
{ name: 'custom', source: 'user' },
|
||||||
|
{ name: 'default', source: 'builtin' },
|
||||||
|
{ name: 'review-only', source: 'builtin' },
|
||||||
|
{ name: 'extra', source: 'builtin' },
|
||||||
|
]);
|
||||||
|
const config = {
|
||||||
|
pieceCategories: [
|
||||||
|
{ name: 'My Team', pieces: ['custom'], children: [] },
|
||||||
|
{
|
||||||
|
name: BUILTIN_CATEGORY_NAME,
|
||||||
|
pieces: [],
|
||||||
|
children: [
|
||||||
|
{ name: 'Quick Start', pieces: ['default'], children: [] },
|
||||||
|
{ name: 'Review', pieces: ['review-only'], children: [] },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
builtinPieceCategories: [
|
||||||
|
{ name: 'Quick Start', pieces: ['default'], children: [] },
|
||||||
|
{ name: 'Review', pieces: ['review-only'], children: [] },
|
||||||
|
],
|
||||||
|
userPieceCategories: [
|
||||||
|
{ name: 'My Team', pieces: ['custom'], children: [] },
|
||||||
|
],
|
||||||
|
hasUserCategories: true,
|
||||||
|
showOthersCategory: true,
|
||||||
|
othersCategoryName: 'Others',
|
||||||
|
};
|
||||||
|
|
||||||
|
const categorized = buildCategorizedPieces(allPieces, config);
|
||||||
|
expect(categorized.categories).toEqual([
|
||||||
|
{ name: 'My Team', pieces: ['custom'], children: [] },
|
||||||
|
{
|
||||||
|
name: BUILTIN_CATEGORY_NAME,
|
||||||
|
pieces: [],
|
||||||
|
children: [
|
||||||
|
{ name: 'Quick Start', pieces: ['default'], children: [] },
|
||||||
|
{ name: 'Review', pieces: ['review-only'], children: [] },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{ name: 'Others', pieces: ['extra'], children: [] },
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
it('should find categories containing a piece', () => {
|
it('should find categories containing a piece', () => {
|
||||||
const categories = [
|
const categories = [
|
||||||
{ name: 'A', pieces: ['shared'], children: [] },
|
{ name: 'A', pieces: ['shared'], children: [] },
|
||||||
|
|||||||
@ -39,8 +39,8 @@ const configMock = vi.hoisted(() => ({
|
|||||||
loadAllPiecesWithSources: vi.fn(),
|
loadAllPiecesWithSources: vi.fn(),
|
||||||
getPieceCategories: vi.fn(),
|
getPieceCategories: vi.fn(),
|
||||||
buildCategorizedPieces: vi.fn(),
|
buildCategorizedPieces: vi.fn(),
|
||||||
|
getCurrentPiece: vi.fn(),
|
||||||
resolveConfigValue: vi.fn(),
|
resolveConfigValue: vi.fn(),
|
||||||
findPieceCategories: vi.fn(() => []),
|
|
||||||
}));
|
}));
|
||||||
|
|
||||||
vi.mock('../infra/config/index.js', () => configMock);
|
vi.mock('../infra/config/index.js', () => configMock);
|
||||||
@ -242,6 +242,65 @@ describe('selectPieceFromCategorizedPieces', () => {
|
|||||||
// Should NOT contain the parent category again
|
// Should NOT contain the parent category again
|
||||||
expect(labels.some((l) => l.includes('Dev'))).toBe(false);
|
expect(labels.some((l) => l.includes('Dev'))).toBe(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should navigate into builtin wrapper category and select a piece', async () => {
|
||||||
|
const categorized: CategorizedPieces = {
|
||||||
|
categories: [
|
||||||
|
{ name: 'My Team', pieces: ['custom'], children: [] },
|
||||||
|
{
|
||||||
|
name: 'builtin',
|
||||||
|
pieces: [],
|
||||||
|
children: [
|
||||||
|
{ name: 'Quick Start', pieces: ['default'], children: [] },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
allPieces: createPieceMap([
|
||||||
|
{ name: 'custom', source: 'user' },
|
||||||
|
{ name: 'default', source: 'builtin' },
|
||||||
|
]),
|
||||||
|
missingPieces: [],
|
||||||
|
};
|
||||||
|
|
||||||
|
// Select builtin category → Quick Start subcategory → piece
|
||||||
|
selectOptionMock
|
||||||
|
.mockResolvedValueOnce('__custom_category__:builtin')
|
||||||
|
.mockResolvedValueOnce('__category__:Quick Start')
|
||||||
|
.mockResolvedValueOnce('default');
|
||||||
|
|
||||||
|
const selected = await selectPieceFromCategorizedPieces(categorized, '');
|
||||||
|
expect(selected).toBe('default');
|
||||||
|
expect(selectOptionMock).toHaveBeenCalledTimes(3);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should show builtin wrapper as a folder in top-level options', async () => {
|
||||||
|
const categorized: CategorizedPieces = {
|
||||||
|
categories: [
|
||||||
|
{ name: 'My Team', pieces: ['custom'], children: [] },
|
||||||
|
{
|
||||||
|
name: 'builtin',
|
||||||
|
pieces: [],
|
||||||
|
children: [
|
||||||
|
{ name: 'Quick Start', pieces: ['default'], children: [] },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
allPieces: createPieceMap([
|
||||||
|
{ name: 'custom', source: 'user' },
|
||||||
|
{ name: 'default', source: 'builtin' },
|
||||||
|
]),
|
||||||
|
missingPieces: [],
|
||||||
|
};
|
||||||
|
|
||||||
|
selectOptionMock.mockResolvedValueOnce(null);
|
||||||
|
|
||||||
|
await selectPieceFromCategorizedPieces(categorized, '');
|
||||||
|
|
||||||
|
const firstCallOptions = selectOptionMock.mock.calls[0]![1] as { label: string; value: string }[];
|
||||||
|
const labels = firstCallOptions.map((o) => o.label);
|
||||||
|
expect(labels.some((l) => l.includes('My Team'))).toBe(true);
|
||||||
|
expect(labels.some((l) => l.includes('builtin'))).toBe(true);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('selectPiece', () => {
|
describe('selectPiece', () => {
|
||||||
|
|||||||
@ -11,7 +11,6 @@ import {
|
|||||||
removeBookmark,
|
removeBookmark,
|
||||||
} from '../../infra/config/global/index.js';
|
} from '../../infra/config/global/index.js';
|
||||||
import {
|
import {
|
||||||
findPieceCategories,
|
|
||||||
listPieces,
|
listPieces,
|
||||||
listPieceEntries,
|
listPieceEntries,
|
||||||
loadAllPiecesWithSources,
|
loadAllPiecesWithSources,
|
||||||
@ -160,8 +159,6 @@ function buildCategoryLevelOptions(
|
|||||||
categories: PieceCategoryNode[],
|
categories: PieceCategoryNode[],
|
||||||
pieces: string[],
|
pieces: string[],
|
||||||
currentPiece: string,
|
currentPiece: string,
|
||||||
rootCategories: PieceCategoryNode[],
|
|
||||||
currentPathLabel: string,
|
|
||||||
): {
|
): {
|
||||||
options: SelectionOption[];
|
options: SelectionOption[];
|
||||||
categoryMap: Map<string, PieceCategoryNode>;
|
categoryMap: Map<string, PieceCategoryNode>;
|
||||||
@ -181,19 +178,7 @@ function buildCategoryLevelOptions(
|
|||||||
|
|
||||||
for (const pieceName of pieces) {
|
for (const pieceName of pieces) {
|
||||||
const isCurrent = pieceName === currentPiece;
|
const isCurrent = pieceName === currentPiece;
|
||||||
const alsoIn = findPieceCategories(pieceName, rootCategories)
|
const label = isCurrent ? `🎼 ${pieceName} (current)` : `🎼 ${pieceName}`;
|
||||||
.filter((path) => path !== currentPathLabel);
|
|
||||||
const alsoInLabel = alsoIn.length > 0 ? `also in ${alsoIn.join(', ')}` : '';
|
|
||||||
|
|
||||||
let label = `🎼 ${pieceName}`;
|
|
||||||
if (isCurrent && alsoInLabel) {
|
|
||||||
label = `🎼 ${pieceName} (current, ${alsoInLabel})`;
|
|
||||||
} else if (isCurrent) {
|
|
||||||
label = `🎼 ${pieceName} (current)`;
|
|
||||||
} else if (alsoInLabel) {
|
|
||||||
label = `🎼 ${pieceName} (${alsoInLabel})`;
|
|
||||||
}
|
|
||||||
|
|
||||||
options.push({ label, value: pieceName });
|
options.push({ label, value: pieceName });
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -223,8 +208,6 @@ async function selectPieceFromCategoryTree(
|
|||||||
currentCategories,
|
currentCategories,
|
||||||
currentPieces,
|
currentPieces,
|
||||||
currentPiece,
|
currentPiece,
|
||||||
categories,
|
|
||||||
currentPathLabel,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
if (options.length === 0) {
|
if (options.length === 0) {
|
||||||
|
|||||||
@ -20,6 +20,7 @@ export {
|
|||||||
} from './pieceLoader.js';
|
} from './pieceLoader.js';
|
||||||
|
|
||||||
export {
|
export {
|
||||||
|
BUILTIN_CATEGORY_NAME,
|
||||||
loadDefaultCategories,
|
loadDefaultCategories,
|
||||||
getDefaultCategoriesPath,
|
getDefaultCategoriesPath,
|
||||||
getPieceCategories,
|
getPieceCategories,
|
||||||
|
|||||||
@ -22,6 +22,8 @@ const CategoryConfigSchema = z.object({
|
|||||||
others_category_name: z.string().min(1).optional(),
|
others_category_name: z.string().min(1).optional(),
|
||||||
}).passthrough();
|
}).passthrough();
|
||||||
|
|
||||||
|
export const BUILTIN_CATEGORY_NAME = 'builtin';
|
||||||
|
|
||||||
export interface PieceCategoryNode {
|
export interface PieceCategoryNode {
|
||||||
name: string;
|
name: string;
|
||||||
pieces: string[];
|
pieces: string[];
|
||||||
@ -32,6 +34,7 @@ export interface CategoryConfig {
|
|||||||
pieceCategories: PieceCategoryNode[];
|
pieceCategories: PieceCategoryNode[];
|
||||||
builtinPieceCategories: PieceCategoryNode[];
|
builtinPieceCategories: PieceCategoryNode[];
|
||||||
userPieceCategories: PieceCategoryNode[];
|
userPieceCategories: PieceCategoryNode[];
|
||||||
|
hasUserCategories: boolean;
|
||||||
showOthersCategory: boolean;
|
showOthersCategory: boolean;
|
||||||
othersCategoryName: string;
|
othersCategoryName: string;
|
||||||
}
|
}
|
||||||
@ -57,7 +60,6 @@ interface RawCategoryConfig {
|
|||||||
interface ParsedCategoryNode {
|
interface ParsedCategoryNode {
|
||||||
name: string;
|
name: string;
|
||||||
pieces: string[];
|
pieces: string[];
|
||||||
hasPieces: boolean;
|
|
||||||
children: ParsedCategoryNode[];
|
children: ParsedCategoryNode[];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -97,7 +99,6 @@ function parseCategoryNode(
|
|||||||
throw new Error(`category "${name}" must be an object in ${sourceLabel} at ${path.join(' > ')}`);
|
throw new Error(`category "${name}" must be an object in ${sourceLabel} at ${path.join(' > ')}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
const hasPieces = Object.prototype.hasOwnProperty.call(raw, 'pieces');
|
|
||||||
const pieces = parsePieces(raw.pieces, sourceLabel, path);
|
const pieces = parsePieces(raw.pieces, sourceLabel, path);
|
||||||
const children: ParsedCategoryNode[] = [];
|
const children: ParsedCategoryNode[] = [];
|
||||||
|
|
||||||
@ -109,7 +110,7 @@ function parseCategoryNode(
|
|||||||
children.push(parseCategoryNode(key, value, sourceLabel, [...path, key]));
|
children.push(parseCategoryNode(key, value, sourceLabel, [...path, key]));
|
||||||
}
|
}
|
||||||
|
|
||||||
return { name, pieces, hasPieces, children };
|
return { name, pieces, children };
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseCategoryTree(raw: unknown, sourceLabel: string): ParsedCategoryNode[] {
|
function parseCategoryTree(raw: unknown, sourceLabel: string): ParsedCategoryNode[] {
|
||||||
@ -176,38 +177,6 @@ function convertParsedNodes(nodes: ParsedCategoryNode[]): PieceCategoryNode[] {
|
|||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
function mergeCategoryNodes(baseNodes: ParsedCategoryNode[], overlayNodes: ParsedCategoryNode[]): ParsedCategoryNode[] {
|
|
||||||
const overlayByName = new Map<string, ParsedCategoryNode>();
|
|
||||||
for (const overlayNode of overlayNodes) {
|
|
||||||
overlayByName.set(overlayNode.name, overlayNode);
|
|
||||||
}
|
|
||||||
|
|
||||||
const merged: ParsedCategoryNode[] = [];
|
|
||||||
for (const baseNode of baseNodes) {
|
|
||||||
const overlayNode = overlayByName.get(baseNode.name);
|
|
||||||
if (!overlayNode) {
|
|
||||||
merged.push(baseNode);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
overlayByName.delete(baseNode.name);
|
|
||||||
|
|
||||||
const mergedNode: ParsedCategoryNode = {
|
|
||||||
name: baseNode.name,
|
|
||||||
pieces: overlayNode.hasPieces ? overlayNode.pieces : baseNode.pieces,
|
|
||||||
hasPieces: baseNode.hasPieces || overlayNode.hasPieces,
|
|
||||||
children: mergeCategoryNodes(baseNode.children, overlayNode.children),
|
|
||||||
};
|
|
||||||
merged.push(mergedNode);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const overlayNode of overlayByName.values()) {
|
|
||||||
merged.push(overlayNode);
|
|
||||||
}
|
|
||||||
|
|
||||||
return merged;
|
|
||||||
}
|
|
||||||
|
|
||||||
function resolveShowOthersCategory(defaultConfig: ParsedCategoryConfig, userConfig: ParsedCategoryConfig | null): boolean {
|
function resolveShowOthersCategory(defaultConfig: ParsedCategoryConfig, userConfig: ParsedCategoryConfig | null): boolean {
|
||||||
if (userConfig?.showOthersCategory !== undefined) {
|
if (userConfig?.showOthersCategory !== undefined) {
|
||||||
return userConfig.showOthersCategory;
|
return userConfig.showOthersCategory;
|
||||||
@ -249,6 +218,7 @@ export function loadDefaultCategories(cwd: string): CategoryConfig | null {
|
|||||||
pieceCategories: builtinPieceCategories,
|
pieceCategories: builtinPieceCategories,
|
||||||
builtinPieceCategories,
|
builtinPieceCategories,
|
||||||
userPieceCategories: [],
|
userPieceCategories: [],
|
||||||
|
hasUserCategories: false,
|
||||||
showOthersCategory,
|
showOthersCategory,
|
||||||
othersCategoryName,
|
othersCategoryName,
|
||||||
};
|
};
|
||||||
@ -260,6 +230,18 @@ export function getDefaultCategoriesPath(cwd: string): string {
|
|||||||
return join(getLanguageResourcesDir(lang), 'piece-categories.yaml');
|
return join(getLanguageResourcesDir(lang), 'piece-categories.yaml');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function buildSeparatedCategories(
|
||||||
|
userCategories: PieceCategoryNode[],
|
||||||
|
builtinCategories: PieceCategoryNode[],
|
||||||
|
): PieceCategoryNode[] {
|
||||||
|
const builtinWrapper: PieceCategoryNode = {
|
||||||
|
name: BUILTIN_CATEGORY_NAME,
|
||||||
|
pieces: [],
|
||||||
|
children: builtinCategories,
|
||||||
|
};
|
||||||
|
return [...userCategories, builtinWrapper];
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get effective piece categories configuration.
|
* Get effective piece categories configuration.
|
||||||
* Built from builtin categories and optional user overlay.
|
* Built from builtin categories and optional user overlay.
|
||||||
@ -274,17 +256,19 @@ export function getPieceCategories(cwd: string): CategoryConfig | null {
|
|||||||
const userPath = getPieceCategoriesPath(cwd);
|
const userPath = getPieceCategoriesPath(cwd);
|
||||||
const userConfig = loadCategoryConfigFromPath(userPath, userPath);
|
const userConfig = loadCategoryConfigFromPath(userPath, userPath);
|
||||||
|
|
||||||
const merged = userConfig?.pieceCategories
|
|
||||||
? mergeCategoryNodes(defaultConfig.pieceCategories, userConfig.pieceCategories)
|
|
||||||
: defaultConfig.pieceCategories;
|
|
||||||
|
|
||||||
const builtinPieceCategories = convertParsedNodes(defaultConfig.pieceCategories);
|
const builtinPieceCategories = convertParsedNodes(defaultConfig.pieceCategories);
|
||||||
const userPieceCategories = convertParsedNodes(userConfig?.pieceCategories ?? []);
|
const userPieceCategories = convertParsedNodes(userConfig?.pieceCategories ?? []);
|
||||||
|
const hasUserCategories = userPieceCategories.length > 0;
|
||||||
|
|
||||||
|
const pieceCategories = hasUserCategories
|
||||||
|
? buildSeparatedCategories(userPieceCategories, builtinPieceCategories)
|
||||||
|
: builtinPieceCategories;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
pieceCategories: convertParsedNodes(merged),
|
pieceCategories,
|
||||||
builtinPieceCategories,
|
builtinPieceCategories,
|
||||||
userPieceCategories,
|
userPieceCategories,
|
||||||
|
hasUserCategories,
|
||||||
showOthersCategory: resolveShowOthersCategory(defaultConfig, userConfig),
|
showOthersCategory: resolveShowOthersCategory(defaultConfig, userConfig),
|
||||||
othersCategoryName: resolveOthersCategoryName(defaultConfig, userConfig),
|
othersCategoryName: resolveOthersCategoryName(defaultConfig, userConfig),
|
||||||
};
|
};
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user