From a02d368abb0863b5a667fe4e4ec559a37c0b8101 Mon Sep 17 00:00:00 2001 From: "Yuito Akatsuki (Tani Yutaka)" Date: Wed, 19 Nov 2025 12:01:36 +0900 Subject: [PATCH 01/11] =?UTF-8?q?=E9=96=8B=E7=99=BA=E7=94=A8=E3=81=ABwatch?= =?UTF-8?q?=E3=81=8B=E3=82=89node=5Fmodules=E3=81=A8=E3=81=8B=E3=82=92igno?= =?UTF-8?q?re=E3=81=97=E3=81=A6=E3=81=8A=E3=81=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/server/app.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/server/app.ts b/src/server/app.ts index c1ea01e..81f4d98 100644 --- a/src/server/app.ts +++ b/src/server/app.ts @@ -66,7 +66,8 @@ export function startLocalChangeWatcher({ }) { const wsServer = new WebSocketServer({ server }); const watcher = chokidar.watch(watchPath, { - ignored: ["**/.remote/**"], + ignored: [/node_modules|\.git/, "**/.remote/**"], + persistent: true, }); watcher.on("change", () => { wsServer.clients.forEach((client) => { From b163ad3449d1f52c72988461a523c102aacbff1d Mon Sep 17 00:00:00 2001 From: "Yuito Akatsuki (Tani Yutaka)" Date: Wed, 19 Nov 2025 12:13:07 +0900 Subject: [PATCH 02/11] =?UTF-8?q?recursive=E3=81=AB=E3=83=95=E3=82=A1?= =?UTF-8?q?=E3=82=A4=E3=83=AB=E5=90=8D=E3=82=92=E5=8F=96=E5=BE=97=E3=81=99?= =?UTF-8?q?=E3=82=8B=E3=82=88=E3=81=86=E3=81=AB=E5=A4=89=E6=9B=B4=E3=81=99?= =?UTF-8?q?=E3=82=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/lib/file-system-repo.ts | 41 +++++++++++++++++++++---------------- 1 file changed, 23 insertions(+), 18 deletions(-) diff --git a/src/lib/file-system-repo.ts b/src/lib/file-system-repo.ts index 1fb2742..2b036e9 100644 --- a/src/lib/file-system-repo.ts +++ b/src/lib/file-system-repo.ts @@ -214,9 +214,14 @@ export class FileSystemRepo { } private async getItemFilenames(remote: boolean = false) { - return await fs.readdir( - this.getRootOrRemotePath(remote), - FileSystemRepo.fileSystemOptions(), + return ( + await fs.readdir( + this.getRootOrRemotePath(remote), + FileSystemRepo.fileSystemOptions() + ) + ).filter( + (itemFilename) => + /\.md$/.test(itemFilename) && !itemFilename.startsWith(".remote/") ); } @@ -234,7 +239,7 @@ export class FileSystemRepo { const basename = `${prefix}${suffix}`; const filenameCandidate = this.getFilename(basename); const found = itemFilenames.find( - (filename) => filename === filenameCandidate, + (filename) => filename === filenameCandidate ); if (!found) { return basename; @@ -246,20 +251,22 @@ export class FileSystemRepo { private static fileSystemOptions() { return { encoding: "utf8", + withFileTypes: false, + recursive: true, } as const; } private async setItemData( fileContent: FileContent, remote: boolean = false, - basename: string | null = null, + basename: string | null = null ) { if (!fileContent.id) { return; } const filepath = this.getFilePath( basename || this.defaultBasename(fileContent), - remote, + remote ); const data = fileContent.toSaveFormat(); await fs.writeFile(filepath, data, FileSystemRepo.fileSystemOptions()); @@ -267,12 +274,12 @@ export class FileSystemRepo { private async getItemData( itemFilename: string, - remote: boolean = false, + remote: boolean = false ): Promise { try { const fileContent = await fs.readFile( path.join(this.getRootOrRemotePath(remote), itemFilename), - FileSystemRepo.fileSystemOptions(), + FileSystemRepo.fileSystemOptions() ); return FileContent.read(fileContent); } catch (err: any) { @@ -283,7 +290,7 @@ export class FileSystemRepo { private async syncItem( item: Item, beforeSync: boolean = false, - forceUpdate: boolean = false, + forceUpdate: boolean = false ) { const fileContent = FileContent.fromItem(item); @@ -295,7 +302,7 @@ export class FileSystemRepo { const basename = localResult?.name || null; const remoteFileContent = await this.getItemData( this.getFilename(item.id), - true, + true ); if (data === null || remoteFileContent?.equals(data) || forceUpdate) { @@ -317,7 +324,7 @@ export class FileSystemRepo { async saveItem( item: Item, beforeSync: boolean = false, - forceUpdate: boolean = false, + forceUpdate: boolean = false ) { await this.syncItem(item, beforeSync, forceUpdate); } @@ -325,12 +332,10 @@ export class FileSystemRepo { async loadItems(): Promise { const itemFilenames = await this.getItemFilenames(); - const promises = itemFilenames - .filter((itemFilename) => /\.md$/.test(itemFilename)) - .map(async (itemFilename) => { - const basename = this.parseFilename(itemFilename); - return await this.loadItemByBasename(basename); - }); + const promises = itemFilenames.map(async (itemFilename) => { + const basename = this.parseFilename(itemFilename); + return await this.loadItemByBasename(basename); + }); const items = excludeNull(await Promise.all(promises)); return items; @@ -447,7 +452,7 @@ export class FileSystemRepo { await fs.writeFile( newFilePath, newData, - FileSystemRepo.fileSystemOptions(), + FileSystemRepo.fileSystemOptions() ); } From cfebd05d8179aad598d09ca97fc0db2e58032b56 Mon Sep 17 00:00:00 2001 From: "Yuito Akatsuki (Tani Yutaka)" Date: Wed, 19 Nov 2025 12:28:03 +0900 Subject: [PATCH 03/11] =?UTF-8?q?=E3=83=87=E3=82=A3=E3=83=AC=E3=82=AF?= =?UTF-8?q?=E3=83=88=E3=83=AA=E3=82=82=E5=90=AB=E3=82=81=E3=82=8B=E3=82=88?= =?UTF-8?q?=E3=81=86=E3=81=AB=E5=A4=89=E6=9B=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/lib/file-system-repo.ts | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/lib/file-system-repo.ts b/src/lib/file-system-repo.ts index 2b036e9..0f55a35 100644 --- a/src/lib/file-system-repo.ts +++ b/src/lib/file-system-repo.ts @@ -206,7 +206,7 @@ export class FileSystemRepo { } private parseFilename(filename: string) { - return path.basename(filename, ".md"); + return filename.split(".md")[0]; } private getFilePath(uuid: string, remote: boolean = false) { @@ -217,11 +217,11 @@ export class FileSystemRepo { return ( await fs.readdir( this.getRootOrRemotePath(remote), - FileSystemRepo.fileSystemOptions() + FileSystemRepo.fileSystemOptions(), ) ).filter( (itemFilename) => - /\.md$/.test(itemFilename) && !itemFilename.startsWith(".remote/") + /\.md$/.test(itemFilename) && !itemFilename.startsWith(".remote/"), ); } @@ -239,7 +239,7 @@ export class FileSystemRepo { const basename = `${prefix}${suffix}`; const filenameCandidate = this.getFilename(basename); const found = itemFilenames.find( - (filename) => filename === filenameCandidate + (filename) => filename === filenameCandidate, ); if (!found) { return basename; @@ -259,14 +259,14 @@ export class FileSystemRepo { private async setItemData( fileContent: FileContent, remote: boolean = false, - basename: string | null = null + basename: string | null = null, ) { if (!fileContent.id) { return; } const filepath = this.getFilePath( basename || this.defaultBasename(fileContent), - remote + remote, ); const data = fileContent.toSaveFormat(); await fs.writeFile(filepath, data, FileSystemRepo.fileSystemOptions()); @@ -274,12 +274,12 @@ export class FileSystemRepo { private async getItemData( itemFilename: string, - remote: boolean = false + remote: boolean = false, ): Promise { try { const fileContent = await fs.readFile( path.join(this.getRootOrRemotePath(remote), itemFilename), - FileSystemRepo.fileSystemOptions() + FileSystemRepo.fileSystemOptions(), ); return FileContent.read(fileContent); } catch (err: any) { @@ -290,7 +290,7 @@ export class FileSystemRepo { private async syncItem( item: Item, beforeSync: boolean = false, - forceUpdate: boolean = false + forceUpdate: boolean = false, ) { const fileContent = FileContent.fromItem(item); @@ -302,7 +302,7 @@ export class FileSystemRepo { const basename = localResult?.name || null; const remoteFileContent = await this.getItemData( this.getFilename(item.id), - true + true, ); if (data === null || remoteFileContent?.equals(data) || forceUpdate) { @@ -324,7 +324,7 @@ export class FileSystemRepo { async saveItem( item: Item, beforeSync: boolean = false, - forceUpdate: boolean = false + forceUpdate: boolean = false, ) { await this.syncItem(item, beforeSync, forceUpdate); } @@ -452,7 +452,7 @@ export class FileSystemRepo { await fs.writeFile( newFilePath, newData, - FileSystemRepo.fileSystemOptions() + FileSystemRepo.fileSystemOptions(), ); } From f657b38378765295cb1cdc3efc36d462137b1911 Mon Sep 17 00:00:00 2001 From: "Yuito Akatsuki (Tani Yutaka)" Date: Wed, 19 Nov 2025 14:14:53 +0900 Subject: [PATCH 04/11] =?UTF-8?q?=E3=83=87=E3=82=A3=E3=83=AC=E3=82=AF?= =?UTF-8?q?=E3=83=88=E3=83=AA=E9=9A=8E=E5=B1=A4=E3=82=92=E8=A1=A8=E7=A4=BA?= =?UTF-8?q?=E3=81=99=E3=82=8B=E3=82=88=E3=81=86=E3=81=AB=E3=81=99=E3=82=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/client/components/SidebarArticles.tsx | 149 ++++++++++++++++++++-- src/lib/view-models/items.ts | 1 + src/server/api/items.ts | 1 + 3 files changed, 137 insertions(+), 14 deletions(-) diff --git a/src/client/components/SidebarArticles.tsx b/src/client/components/SidebarArticles.tsx index eacabb4..f79eed7 100644 --- a/src/client/components/SidebarArticles.tsx +++ b/src/client/components/SidebarArticles.tsx @@ -59,6 +59,33 @@ export const SidebarArticles = ({ items, sortType, articleState }: Props) => { localStorage.setItem(StorageName[articleState], isDetailsOpen.toString()); }, [isDetailsOpen]); + console.log(items); + + const rootGrouped: { + [root: string]: { + direct: ItemViewModel[]; + children: { [child: string]: ItemViewModel[] }; + }; + } = {}; + const topLevelItems: ItemViewModel[] = []; + items.forEach((item) => { + if (!item.parent || item.parent.length === 0) { + topLevelItems.push(item); + } else { + const root = item.parent[0]; + const rest = item.parent.slice(1); + const child = rest.length === 0 ? null : rest.join("/"); + if (!rootGrouped[root]) rootGrouped[root] = { direct: [], children: {} }; + if (child === null) { + rootGrouped[root].direct.push(item); + } else { + if (!rootGrouped[root].children[child]) + rootGrouped[root].children[child] = []; + rootGrouped[root].children[child].push(item); + } + } + }); + return (
@@ -66,17 +93,99 @@ export const SidebarArticles = ({ items, sortType, articleState }: Props) => { {items.length}
    - {items.sort(compare[sortType]).map((item) => ( -
  • - - - note - - - {item.modified && articleState !== "Draft" && "(差分あり) "} - {item.title} - - + {topLevelItems.length > 0 && + topLevelItems.sort(compare[sortType]).map((item) => ( +
  • + + + note + + + {item.modified && articleState !== "Draft" && "(差分あり) "} + {item.title} + + +
  • + ))} + + {Object.entries(rootGrouped).map(([root, group]) => ( +
  • +
    + + {root} + + {group.direct.length + + Object.values(group.children).reduce( + (s, arr) => s + arr.length, + 0, + )} + + +
      + {group.direct.length > 0 && + group.direct.sort(compare[sortType]).map((item) => ( +
    • + + + note + + + {item.modified && + articleState !== "Draft" && + "(差分あり) "} + {item.title} + + +
    • + ))} + + {Object.entries(group.children).map( + ([childPath, groupedItems]) => ( +
    • +
      + + {childPath} + + {groupedItems.length} + + +
        + {groupedItems.sort(compare[sortType]).map((item) => ( +
      • + + + note + + + {item.modified && + articleState !== "Draft" && + "(差分あり) "} + {item.title} + + +
      • + ))} +
      +
      +
    • + ), + )} +
    +
  • ))}
@@ -93,6 +202,18 @@ const articleDetailsStyle = css({ "&[open] > summary::before": { content: "'expand_more'", }, + // nested lists: give visual indentation for hierarchy + "& ul": { + listStyle: "none", + margin: 0, + paddingLeft: 0, + }, + "& ul ul": { + paddingLeft: getSpace(4), + }, + "& ul ul ul": { + paddingLeft: getSpace(4), + }, }); const articleSummaryStyle = css({ @@ -137,9 +258,9 @@ const articlesListItemStyle = css({ fontSize: Typography.body2, gap: getSpace(1), lineHeight: LineHeight.bodyDense, - padding: `${getSpace(3 / 4)}px ${getSpace(5 / 2)}px ${getSpace( - 3 / 4, - )}px ${getSpace(3 / 2)}px`, + padding: `${getSpace(3 / 4)}px ${getSpace(5 / 2)}px ${getSpace(3 / 4)}px ${getSpace( + 3, + )}px`, whiteSpace: "nowrap", textOverflow: "ellipsis", diff --git a/src/lib/view-models/items.ts b/src/lib/view-models/items.ts index 215d15d..e049c85 100644 --- a/src/lib/view-models/items.ts +++ b/src/lib/view-models/items.ts @@ -5,6 +5,7 @@ export type ItemViewModel = { title: string; updated_at: string; modified: boolean; + parent: string[]; }; export type ItemsIndexViewModel = { diff --git a/src/server/api/items.ts b/src/server/api/items.ts index d35699b..b126b98 100644 --- a/src/server/api/items.ts +++ b/src/server/api/items.ts @@ -27,6 +27,7 @@ const itemsIndex = async (req: Express.Request, res: Express.Response) => { title: item.title, updated_at: item.updatedAt, modified: item.modified, + parent: item.name.split("/").slice(0, -1) || [], }; if (item.id) { From 86cd3960099a2856d02a4470cdf38edc16a2c735 Mon Sep 17 00:00:00 2001 From: "Yuito Akatsuki (Tani Yutaka)" Date: Wed, 19 Nov 2025 14:41:59 +0900 Subject: [PATCH 05/11] =?UTF-8?q?=E5=86=8D=E8=B5=B7=E7=9A=84=E3=81=AB?= =?UTF-8?q?=E9=9A=8E=E5=B1=A4=E6=A7=8B=E9=80=A0=E3=81=A8=E3=81=97=E3=81=A6?= =?UTF-8?q?=E8=A1=A8=E7=A4=BA=E3=81=99=E3=82=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/client/components/SidebarArticles.tsx | 170 ++++++++++------------ 1 file changed, 73 insertions(+), 97 deletions(-) diff --git a/src/client/components/SidebarArticles.tsx b/src/client/components/SidebarArticles.tsx index f79eed7..e141444 100644 --- a/src/client/components/SidebarArticles.tsx +++ b/src/client/components/SidebarArticles.tsx @@ -59,33 +59,84 @@ export const SidebarArticles = ({ items, sortType, articleState }: Props) => { localStorage.setItem(StorageName[articleState], isDetailsOpen.toString()); }, [isDetailsOpen]); - console.log(items); - - const rootGrouped: { - [root: string]: { - direct: ItemViewModel[]; - children: { [child: string]: ItemViewModel[] }; - }; - } = {}; + // build recursive tree from item.parent (segments array) const topLevelItems: ItemViewModel[] = []; + + type TreeNode = { + name: string; + items: ItemViewModel[]; + children: { [name: string]: TreeNode }; + }; + + const roots: { [name: string]: TreeNode } = {}; + + const addToTree = (segments: string[], item: ItemViewModel) => { + const rootName = segments[0]; + if (!roots[rootName]) + roots[rootName] = { name: rootName, items: [], children: {} }; + let node = roots[rootName]; + const rest = segments.slice(1); + if (rest.length === 0) { + node.items.push(item); + return; + } + for (const seg of rest) { + if (!node.children[seg]) + node.children[seg] = { name: seg, items: [], children: {} }; + node = node.children[seg]; + } + node.items.push(item); + }; + items.forEach((item) => { if (!item.parent || item.parent.length === 0) { topLevelItems.push(item); } else { - const root = item.parent[0]; - const rest = item.parent.slice(1); - const child = rest.length === 0 ? null : rest.join("/"); - if (!rootGrouped[root]) rootGrouped[root] = { direct: [], children: {} }; - if (child === null) { - rootGrouped[root].direct.push(item); - } else { - if (!rootGrouped[root].children[child]) - rootGrouped[root].children[child] = []; - rootGrouped[root].children[child].push(item); - } + addToTree(item.parent, item); } }); + const countSubtreeItems = (node: TreeNode): number => + node.items.length + + Object.values(node.children).reduce((s, c) => s + countSubtreeItems(c), 0); + + const renderNode = (node: TreeNode, path: string) => { + const cmp = compare[sortType]; + return ( +
  • +
    + + {node.name} + + {countSubtreeItems(node)} + + +
      + {node.items.sort(cmp).map((item) => ( +
    • + + + note + + + {item.modified && articleState !== "Draft" && "(差分あり) "} + {item.title} + + +
    • + ))} + + {Object.values(node.children) + .sort((a, b) => a.name.localeCompare(b.name)) + .map((child) => renderNode(child, `${path}/${child.name}`))} +
    +
    +
  • + ); + }; + return (
    @@ -110,84 +161,9 @@ export const SidebarArticles = ({ items, sortType, articleState }: Props) => { ))} - {Object.entries(rootGrouped).map(([root, group]) => ( -
  • -
    - - {root} - - {group.direct.length + - Object.values(group.children).reduce( - (s, arr) => s + arr.length, - 0, - )} - - -
      - {group.direct.length > 0 && - group.direct.sort(compare[sortType]).map((item) => ( -
    • - - - note - - - {item.modified && - articleState !== "Draft" && - "(差分あり) "} - {item.title} - - -
    • - ))} - - {Object.entries(group.children).map( - ([childPath, groupedItems]) => ( -
    • -
      - - {childPath} - - {groupedItems.length} - - -
        - {groupedItems.sort(compare[sortType]).map((item) => ( -
      • - - - note - - - {item.modified && - articleState !== "Draft" && - "(差分あり) "} - {item.title} - - -
      • - ))} -
      -
      -
    • - ), - )} -
    -
    -
  • - ))} + {Object.values(roots) + .sort((a, b) => a.name.localeCompare(b.name)) + .map((r) => renderNode(r, r.name))}
    ); From d3694a2ad0260f5777fc578ef025004399b2a58c Mon Sep 17 00:00:00 2001 From: "Yuito Akatsuki (Tani Yutaka)" Date: Wed, 19 Nov 2025 14:45:41 +0900 Subject: [PATCH 06/11] =?UTF-8?q?=E3=83=87=E3=82=A3=E3=83=AC=E3=82=AF?= =?UTF-8?q?=E3=83=88=E3=83=AA=E3=82=92=E4=B8=80=E7=95=AA=E6=9C=80=E5=88=9D?= =?UTF-8?q?=E3=81=AB=E5=87=BA=E3=81=99=E3=82=88=E3=81=86=E3=81=AB=E3=81=99?= =?UTF-8?q?=E3=82=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/client/components/SidebarArticles.tsx | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/client/components/SidebarArticles.tsx b/src/client/components/SidebarArticles.tsx index e141444..3ebe081 100644 --- a/src/client/components/SidebarArticles.tsx +++ b/src/client/components/SidebarArticles.tsx @@ -112,6 +112,10 @@ export const SidebarArticles = ({ items, sortType, articleState }: Props) => {
      + {Object.values(node.children) + .sort((a, b) => a.name.localeCompare(b.name)) + .map((child) => renderNode(child, `${path}/${child.name}`))} + {node.items.sort(cmp).map((item) => (
    • @@ -127,10 +131,6 @@ export const SidebarArticles = ({ items, sortType, articleState }: Props) => {
    • ))} - - {Object.values(node.children) - .sort((a, b) => a.name.localeCompare(b.name)) - .map((child) => renderNode(child, `${path}/${child.name}`))}
    @@ -144,6 +144,10 @@ export const SidebarArticles = ({ items, sortType, articleState }: Props) => { {items.length}
      + {Object.values(roots) + .sort((a, b) => a.name.localeCompare(b.name)) + .map((r) => renderNode(r, r.name))} + {topLevelItems.length > 0 && topLevelItems.sort(compare[sortType]).map((item) => (
    • @@ -160,10 +164,6 @@ export const SidebarArticles = ({ items, sortType, articleState }: Props) => {
    • ))} - - {Object.values(roots) - .sort((a, b) => a.name.localeCompare(b.name)) - .map((r) => renderNode(r, r.name))}
    ); From b84fdd4192cce0312c4c919f99d75d1cb54e1de9 Mon Sep 17 00:00:00 2001 From: "Yuito Akatsuki (Tani Yutaka)" Date: Wed, 19 Nov 2025 14:57:09 +0900 Subject: [PATCH 07/11] =?UTF-8?q?=E3=82=B9=E3=82=BF=E3=82=A4=E3=83=AB?= =?UTF-8?q?=E3=82=92=E8=AA=BF=E6=95=B4=E3=81=97=E5=90=8C=E9=9A=8E=E5=B1=A4?= =?UTF-8?q?=E3=82=92=E8=A1=A8=E3=81=99=E7=B8=A6=E7=B7=9A=E3=82=92=E8=BF=BD?= =?UTF-8?q?=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/client/components/SidebarArticles.tsx | 32 ++++++++++++++++++++--- 1 file changed, 29 insertions(+), 3 deletions(-) diff --git a/src/client/components/SidebarArticles.tsx b/src/client/components/SidebarArticles.tsx index 3ebe081..461fbcf 100644 --- a/src/client/components/SidebarArticles.tsx +++ b/src/client/components/SidebarArticles.tsx @@ -178,18 +178,44 @@ const articleDetailsStyle = css({ "&[open] > summary::before": { content: "'expand_more'", }, - // nested lists: give visual indentation for hierarchy + // nested lists: draw vertical guide lines inside the padded area "& ul": { listStyle: "none", margin: 0, - paddingLeft: 0, + paddingLeft: getSpace(1), }, "& ul ul": { - paddingLeft: getSpace(4), + position: "relative", + paddingLeft: getSpace(3), + }, + "& ul ul::before": { + content: "''", + position: "absolute", + left: getSpace(3), + top: 0, + bottom: 0, + width: 1, + backgroundColor: Colors.gray20, + }, + "& ul ul > li": { + paddingLeft: getSpace(1.5), }, "& ul ul ul": { + position: "relative", paddingLeft: getSpace(4), }, + "& ul ul ul::before": { + content: "''", + position: "absolute", + left: getSpace(3), + top: 0, + bottom: 0, + width: 1, + backgroundColor: Colors.gray20, + }, + "& ul ul ul > li": { + paddingLeft: getSpace(1.5), + }, }); const articleSummaryStyle = css({ From b732de99cac77a4761feb25cc87934a22c97387a Mon Sep 17 00:00:00 2001 From: "Yuito Akatsuki (Tani Yutaka)" Date: Wed, 19 Nov 2025 16:09:18 +0900 Subject: [PATCH 08/11] Update src/lib/file-system-repo.ts Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- src/lib/file-system-repo.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/file-system-repo.ts b/src/lib/file-system-repo.ts index 0f55a35..194368d 100644 --- a/src/lib/file-system-repo.ts +++ b/src/lib/file-system-repo.ts @@ -206,7 +206,7 @@ export class FileSystemRepo { } private parseFilename(filename: string) { - return filename.split(".md")[0]; + return filename.replace(/\.md$/, ""); } private getFilePath(uuid: string, remote: boolean = false) { From 5be9fe2c29933a85c6f807b5b1ee7b9d0a77369f Mon Sep 17 00:00:00 2001 From: "Yuito Akatsuki (Tani Yutaka)" Date: Wed, 19 Nov 2025 16:09:41 +0900 Subject: [PATCH 09/11] Update src/client/components/SidebarArticles.tsx Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- src/client/components/SidebarArticles.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/client/components/SidebarArticles.tsx b/src/client/components/SidebarArticles.tsx index 461fbcf..5722535 100644 --- a/src/client/components/SidebarArticles.tsx +++ b/src/client/components/SidebarArticles.tsx @@ -149,7 +149,7 @@ export const SidebarArticles = ({ items, sortType, articleState }: Props) => { .map((r) => renderNode(r, r.name))} {topLevelItems.length > 0 && - topLevelItems.sort(compare[sortType]).map((item) => ( + [...topLevelItems].sort(compare[sortType]).map((item) => (
  • Date: Wed, 19 Nov 2025 16:10:31 +0900 Subject: [PATCH 10/11] Update src/client/components/SidebarArticles.tsx Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- src/client/components/SidebarArticles.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/client/components/SidebarArticles.tsx b/src/client/components/SidebarArticles.tsx index 5722535..69585e0 100644 --- a/src/client/components/SidebarArticles.tsx +++ b/src/client/components/SidebarArticles.tsx @@ -116,7 +116,7 @@ export const SidebarArticles = ({ items, sortType, articleState }: Props) => { .sort((a, b) => a.name.localeCompare(b.name)) .map((child) => renderNode(child, `${path}/${child.name}`))} - {node.items.sort(cmp).map((item) => ( + {[...node.items].sort(cmp).map((item) => (
  • Date: Wed, 19 Nov 2025 16:10:49 +0900 Subject: [PATCH 11/11] Update src/server/api/items.ts Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- src/server/api/items.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/server/api/items.ts b/src/server/api/items.ts index b126b98..99836bb 100644 --- a/src/server/api/items.ts +++ b/src/server/api/items.ts @@ -27,7 +27,7 @@ const itemsIndex = async (req: Express.Request, res: Express.Response) => { title: item.title, updated_at: item.updatedAt, modified: item.modified, - parent: item.name.split("/").slice(0, -1) || [], + parent: item.name.split("/").slice(0, -1), }; if (item.id) {