Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 0 additions & 9 deletions .github/CODEOWNERS

This file was deleted.

22 changes: 9 additions & 13 deletions .github/workflows/publish-package.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,14 @@ name: Publish package

on:
workflow_dispatch:
push:
tags:
- "v*.*.*"

permissions:
contents: write
contents: read
packages: write
id-token: write

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
Expand All @@ -19,25 +23,17 @@ jobs:
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
- uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6.0.0
with:
node-version: "20.16.0"
node-version: "24"
registry-url: "https://registry.npmjs.org"
- name: Set version number to environment variable
run: |
node -p -e '`RELEASED_PACKAGE_VERSION=v${require("./package.json").version}`' >> $GITHUB_ENV
node -p -e '`RELEASED_MAJOR_VERSION=v${require("./package.json").version}`' | awk -F. '{print $1}' >> $GITHUB_ENV
- run: yarn install
- run: yarn run build
env:
NODE_ENV: "production"
- name: Upgrade npm to latest version
run: npm install -g npm@latest
- name: Publish package
run: yarn publish --access public
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
- name: Create version tag
run: |
git config --global user.name 'github-actions[bot]'
git config --global user.email '41898282+github-actions[bot]@users.noreply.github.com'
git tag -a ${{ env.RELEASED_PACKAGE_VERSION }} -m 'Release ${{ env.RELEASED_PACKAGE_VERSION }}'
git tag -f ${{ env.RELEASED_MAJOR_VERSION }} ${{ env.RELEASED_PACKAGE_VERSION }}
git push origin ${{ env.RELEASED_PACKAGE_VERSION }}
git push -f origin ${{ env.RELEASED_MAJOR_VERSION }}
npm publish --access public --tag fork
1 change: 1 addition & 0 deletions LICENSE.md
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,7 @@ APPENDIX: How to apply the Apache License to your work.
identification within third-party archives.

Copyright 2023 Qiita Inc.
Copyright 2025 UniProject

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
Expand Down
21 changes: 17 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@

# Qiita CLI、Qiita Preview へようこそ!

> [!IMPORTANT]
> このパッケージはフォークです。
> 株式会社Qiita様より正式にリリースされているものは [increments/qiita-cli](https://github.com/increments/qiita-cli) をご覧ください。

Qiita CLI とは、手元の環境で記事の執筆・プレビュー・投稿ができるツールです。
Qiita CLI を使うことで、普段お使いのエディタなどを使って記事の執筆・投稿がしやすくなります。

Expand Down Expand Up @@ -46,12 +50,14 @@ Node.js をはじめて使う場合はインストールする必要がありま

> [!WARNING]
> Qiita公式で提供している Qiita CLI の npm package 名は **@qiita/qiita-cli** となります。
> また、このパッケージは **フォークであり、公式に公開されているものではありません**。
> このフォークの npm package 名は **@yuito-it/qiita-cli** となります。
> その他は異なるパッケージがインストールされてしまいます。必ずご確認の上、インストールしてください。

Qiita のコンテンツを管理したいディレクトリで、以下のコマンドを実行します。

```console
npm install @qiita/qiita-cli --save-dev
npm install @yuito-it/qiita-cli --save-dev
```

以下のコマンドでバージョンが表示されればインストール完了です。
Expand All @@ -65,7 +71,7 @@ npx qiita version
Qiita CLI をアップデートする場合は以下のコマンドを実行します。

```console
npm install @qiita/qiita-cli@latest
npm install @yuito-it/qiita-cli@fork
```

## Qiita CLI のセットアップ方法について
Expand Down Expand Up @@ -125,13 +131,17 @@ npx qiita preview
### 記事ファイルの配置について

1 つの記事の内容は、1 つの markdown ファイル(◯◯.md)で管理します。
記事ファイルは`public`ディレクトリ内に含める必要があります
記事ファイルは`public`ディレクトリ内もしくはその配下のディレクトリ内に含める必要があります

```console
.
└─ public
├── newArticle001.md
└── newArticle002.md
├── newArticle002.md
└── adventCalendar
├── day1.md
└── 2025
└── day1.md
```

## Qiita CLI で記事を管理する
Expand Down Expand Up @@ -201,6 +211,9 @@ Qiita CLI、Qiita Preview から記事の削除はできません。

## GitHub で記事を管理する

> [!CAUTION]
> フォーク版の機能である**フォルダー分けは機能しません**。

### GitHub の設定について

以下の流れで設定を行うことで、GitHub の特定のブランチにコミットしたタイミングで記事の投稿や更新を行うことが可能になります。
Expand Down
28 changes: 17 additions & 11 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,18 +1,24 @@
{
"name": "@qiita/qiita-cli",
"version": "1.6.2",
"name": "@unipro-tech/qiita-cli",
"version": "1.7.0-fork.0",
"description": "Qiita CLI is a tool that allows you to write, preview and publish articles on Qiita from local environment.",
"keywords": [
"Qiita"
],
"homepage": "https://github.com/increments/qiita-cli",
"homepage": "https://github.com/unipro-tech/qiita-cli",
"bugs": {
"url": "https://github.com/increments/qiita-discussions/discussions"
},
"repository": "git@github.com:increments/qiita-cli.git",
"author": {
"name": "Qiita Inc."
"url": "https://github.com/unipro-tech/qiita-cli/issues"
},
"repository": "https://github.com/unipro-tech/qiita-cli",
"contributors": [
{
"name": "Qiita Inc."
},
{
"name": "Yuito Akatsuki",
"email": "yuito@yuito-it.jp"
}
],
"license": "Apache-2.0",
"bin": {
"qiita": "dist/main.js"
Expand All @@ -28,7 +34,7 @@
"@types/jest": "^30.0.0",
"@types/node": "^22.15.30",
"@types/react": "^19.1.13",
"@types/react-dom": "^19.1.6",
"@types/react-dom": "^19.1.13",
"@types/webpack": "^5.28.5",
"@types/ws": "^8.18.1",
"@typescript-eslint/eslint-plugin": "^7.0.0",
Expand All @@ -43,8 +49,8 @@
"lint-staged": "^15.5.1",
"npm-run-all": "^4.1.5",
"prettier": "^3.6.2",
"react": "^19.1.1",
"react-dom": "^19.1.0",
"react": "^19.1.13",
"react-dom": "^19.1.13",
"react-router": "^7.8.2",
"ts-jest": "^29.4.4",
"ts-loader": "^9.5.4",
Expand Down
155 changes: 139 additions & 16 deletions src/client/components/SidebarArticles.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -59,26 +59,111 @@ export const SidebarArticles = ({ items, sortType, articleState }: Props) => {
localStorage.setItem(StorageName[articleState], isDetailsOpen.toString());
}, [isDetailsOpen]);

// 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 {
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 (
<li key={path}>
<details css={articleDetailsStyle} open>
<summary css={articleSummaryStyle}>
{node.name}
<span css={articleSectionTitleCountStyle}>
{countSubtreeItems(node)}
</span>
</summary>
<ul>
{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) => (
<li key={item.items_show_path}>
<Link css={articlesListItemStyle} to={item.items_show_path}>
<MaterialSymbol
fill={item.modified && articleState !== "Draft"}
>
note
</MaterialSymbol>
<span css={articleListItemInnerStyle}>
{item.modified && articleState !== "Draft" && "(差分あり) "}
{item.title}
</span>
</Link>
</li>
))}
</ul>
</details>
</li>
);
};

return (
<details css={articleDetailsStyle} open={isDetailsOpen}>
<summary css={articleSummaryStyle} onClick={toggleAccordion}>
{ArticleState[articleState]}
<span css={articleSectionTitleCountStyle}>{items.length}</span>
</summary>
<ul>
{items.sort(compare[sortType]).map((item) => (
<li key={item.items_show_path}>
<Link css={articlesListItemStyle} to={item.items_show_path}>
<MaterialSymbol fill={item.modified && articleState !== "Draft"}>
note
</MaterialSymbol>
<span css={articleListItemInnerStyle}>
{item.modified && articleState !== "Draft" && "(差分あり) "}
{item.title}
</span>
</Link>
</li>
))}
{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) => (
<li key={item.items_show_path}>
<Link css={articlesListItemStyle} to={item.items_show_path}>
<MaterialSymbol
fill={item.modified && articleState !== "Draft"}
>
note
</MaterialSymbol>
<span css={articleListItemInnerStyle}>
{item.modified && articleState !== "Draft" && "(差分あり) "}
{item.title}
</span>
</Link>
</li>
))}
</ul>
</details>
);
Expand All @@ -93,6 +178,44 @@ const articleDetailsStyle = css({
"&[open] > summary::before": {
content: "'expand_more'",
},
// nested lists: draw vertical guide lines inside the padded area
"& ul": {
listStyle: "none",
margin: 0,
paddingLeft: getSpace(1),
},
"& ul ul": {
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({
Expand Down Expand Up @@ -137,9 +260,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",

Expand Down
Loading
Loading