Skip to content

Commit c30696b

Browse files
committed
Merge remote-tracking branch 'origin/pagetransition' into pagetransition
2 parents 8989673 + a675ecc commit c30696b

33 files changed

Lines changed: 6205 additions & 314 deletions

.github/workflows/node.js.yml

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,20 @@ jobs:
3333
- run: npm ci
3434
- run: npm run tsc
3535

36+
check-docs:
37+
runs-on: ubuntu-latest
38+
strategy:
39+
matrix:
40+
node-version: [22.x]
41+
steps:
42+
- uses: actions/checkout@v4
43+
- uses: actions/setup-node@v4
44+
with:
45+
node-version: ${{ matrix.node-version }}
46+
cache: 'npm'
47+
- run: npm ci
48+
- run: npm run checkDocs
49+
3650
test-js-eval:
3751
runs-on: ubuntu-latest
3852
strategy:

.github/workflows/update-docs.yml

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
name: Update Docs Revision And Database
2+
on:
3+
push:
4+
branches: [ "main" ]
5+
permissions:
6+
contents: write
7+
jobs:
8+
check-docs:
9+
runs-on: ubuntu-latest
10+
strategy:
11+
matrix:
12+
node-version: [22.x]
13+
steps:
14+
- uses: actions/checkout@v4
15+
- uses: actions/setup-node@v4
16+
with:
17+
node-version: ${{ matrix.node-version }}
18+
cache: 'npm'
19+
- run: npm ci
20+
- run: npx drizzle-kit migrate
21+
env:
22+
DATABASE_URL: ${{ secrets.DATABASE_URL }}
23+
- run: npx tsx ./scripts/checkDocs.ts --write
24+
env:
25+
DATABASE_URL: ${{ secrets.DATABASE_URL }}
26+
- name: Configure git for push
27+
run: |
28+
git remote set-url origin "https://github-actions:${GITHUB_TOKEN}@github.com/${{ github.repository }}"
29+
git config user.name "github-actions[bot]"
30+
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
31+
env:
32+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
33+
- name: Commit build output
34+
run: |
35+
git add .
36+
git diff --staged --exit-code || (git commit -m "[ci] update revisions.yml" && git push origin main)
37+
# The `||` ensures the commit and push only happen if there are changes (git diff exits with non-zero)

.gitignore

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,9 @@
33
/.open-next
44
/cloudflare-env.d.ts
55

6-
# generated docs section file lists (regenerated by npm run generateSections)
7-
/public/docs/**/sections.yml
8-
9-
# generated languages list (regenerated by npm run generateLanguages)
10-
/public/docs/languages.yml
6+
# generated docs section file lists (regenerated by npm run generateDocsMeta)
7+
/public/docs/**/sections.json
8+
/public/docs/languages.json
119

1210
# dependencies
1311
/node_modules

Dockerfile

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,8 @@ COPY --from=dependencies /app/node_modules ./node_modules
2121
# Copy application source code
2222
COPY . .
2323

24-
ENV NODE_ENV=production
24+
# Stop if documentation has any change that is not reflected to revisions.yml and database.
25+
RUN npx tsx ./scripts/checkDocs.ts --check-diff
2526

2627
# Next.js collects completely anonymous telemetry data about general usage.
2728
# Learn more here: https://nextjs.org/telemetry

README.md

Lines changed: 28 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,24 @@ npx prisma dev
1515
```
1616
を実行し、`t` キーを押して表示される DATABASE_URL をコピー
1717

18-
ルートディレクトリに .env.local という名前のファイルを作成し、以下の内容を記述
18+
ルートディレクトリに .env または .env.local という名前のファイルを作成し、以下の内容を記述
1919
```dotenv
2020
API_KEY=GeminiAPIキー
2121
BETTER_AUTH_URL=http://localhost:3000
2222
DATABASE_URL="postgres://... (prisma devの出力)"
23+
GOOGLE_CLIENT_ID=
24+
GOOGLE_CLIENT_SECRET=
25+
GITHUB_CLIENT_ID=
26+
GITHUB_CLIENT_SECRET=
2327
```
2428

29+
* `API_KEY` はGeminiのAPIキーを作成して設定します。未設定の場合チャットが使えません
30+
* `GITHUB_CLIENT_ID` `GITHUB_CLIENT_SECRET` はGitHub OAuthのクライアントIDとシークレットを設定します。未設定の場合「GitHubでログイン」が使えません。
31+
作り方については https://www.better-auth.com/docs/authentication/github を参照
32+
* `GOOGLE_CLIENT_ID` `GOOGLE_CLIENT_SECRET` はGoogle OAuthのクライアントIDとシークレットを設定します。未設定の場合「Googleでログイン」が使えません。
33+
作り方については https://www.better-auth.com/docs/authentication/google を参照 (GitHubのほうがかんたんかも)
34+
35+
2536
別のターミナルで、
2637
```bash
2738
npx drizzle-kit migrate
@@ -43,23 +54,31 @@ npm run lint
4354
```
4455
でコードをチェックします。出てくるwarningやerrorはできるだけ直しましょう。
4556

57+
### データベースのスキーマ
58+
4659
* データベースのスキーマ(./app/schema/hoge.ts)を編集した場合、 `npx drizzle-kit generate` でmigrationファイルを作成し、 `npx drizzle-kit migrate` でデータベースに反映します。
47-
* また、mainにマージする際に本番環境のデータベースにもmigrateをする必要があります
60+
* 本番環境のデータベースのmigrateはmainにpushされた際にGitHub Actionで実行されます
4861
* スキーマのファイルを追加した場合は app/lib/drizzle.ts でimportを追加する必要があります(たぶん)
4962
* `npx prisma dev` で立ち上げたデータベースは `npx prisma dev ls` でデータベース名の確認・ `npx prisma dev rm default` で削除ができるらしい
5063

5164
### 本番環境の場合
5265

5366
上記の環境変数以外に、
54-
* BETTER_AUTH_SECRET に任意の文字列
55-
* GOOGLE_CLIENT_IDとGOOGLE_CLIENT_SECRETにGoogle OAuthのクライアントIDとシークレット https://www.better-auth.com/docs/authentication/google
56-
* GITHUB_CLIENT_IDとGITHUB_CLIENT_SECRETにGitHub OAuthのクライアントIDとシークレット https://www.better-auth.com/docs/authentication/github
67+
68+
* `BETTER_AUTH_SECRET` に任意の文字列
69+
70+
が必要です。
5771

5872
現在は本番環境(my-code.utcode.net)はCoolifyでデプロイしています。
5973
Cloudflare Worker のビルドログとステータス表示が見れますが、そちらは使っていません。
6074

6175
## ドキュメント
6276

77+
```bash
78+
npm run checkDocs
79+
```
80+
でドキュメントの読み込み時にエラーにならないか確認できます (index.ymlの間違いなど)
81+
6382
* ドキュメントはセクション(見出し)ごとにわけ、 public/docs/言語id/ページid/並び替え用連番-セクション名.md に置く。
6483
* ページはディレクトリの名前によらず 言語id/index.yml に書かれている順で表示される。
6584
* セクションはセクションIDによらずファイル名順で表示される。
@@ -99,6 +118,8 @@ Cloudflare Worker のビルドログとステータス表示が見れますが
99118
* REPLのコード例は1セクションに最大1つまで。
100119
* コードエディターとコード実行ブロックはいくつでも置けます。
101120
* ページ0以外の各ページの最後はレベル2見出し「この章のまとめ」と、レベル3見出し「練習問題n」を置く
121+
* 編集したドキュメントにローカルの開発環境でアクセスする際は `npx tsx ./scripts/checkDocs.ts --write` でrevisions.ymlを更新してください。チャットを正しく動作させるために必要です。基本的には手動でこのファイルを編集する必要はありません。
122+
* 本番環境ではドキュメントの変更がmainブランチにpushされた際に自動で更新されます。gitのコミットidを参照して更新するため、ローカルで更新したrevisions.ymlはpushしないでください。
102123

103124
### ベースとなるドキュメントの作り方
104125

@@ -129,18 +150,17 @@ Cloudflare Worker のビルドログとステータス表示が見れますが
129150
- Canvasを使われた場合はやり直す。(Canvasはファイル名付きコードブロックで壊れる)
130151
- 太字がなぜか `**キーワード**` の代わりに `\*\*キーワード\*\*` となっている場合がある。 `\*\*` → `**` の置き換えで対応
131152
- 見出しの前に `-----` (水平線)が入る場合がある。my.code();は水平線の表示に対応しているが、消す方向で統一
132-
- `言語名-repl` にはページ内で一意なIDを追加する (例: `言語名-repl:1`)
133153
- REPLの出力部分に書かれたコメントは消えるので修正する
134154
- ダメな例
135155
````
136-
```js-repl:1
156+
```js-repl
137157
> console.log("Hello")
138158
Hello // 文字列を表示する
139159
```
140160
````
141161
- 以下のようにすればok
142162
````
143-
```js-repl:1
163+
```js-repl
144164
> console.log("Hello") // 文字列を表示する
145165
Hello
146166
@@ -150,7 +170,6 @@ Cloudflare Worker のビルドログとステータス表示が見れますが
150170
```
151171
````
152172
- 練習問題のファイル名は不都合がなければ `practice(章番号)_(問題番号).拡張子` で統一。空でもよいのでファイルコードブロックとexecコードブロックを置く
153-
- 1章にはたぶん練習問題要らない。
154173

155174
## markdown仕様
156175

app/[lang]/[pageId]/chatForm.tsx

Lines changed: 7 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -11,20 +11,15 @@ import { DynamicMarkdownSection } from "./pageContent";
1111
import { useEmbedContext } from "@/terminal/embedContext";
1212
import { useChatHistoryContext } from "./chatHistory";
1313
import { askAI } from "@/actions/chatActions";
14+
import { PagePath } from "@/lib/docs";
1415

1516
interface ChatFormProps {
16-
docs_id: string;
17-
documentContent: string;
17+
path: PagePath;
1818
sectionContent: DynamicMarkdownSection[];
1919
close: () => void;
2020
}
2121

22-
export function ChatForm({
23-
docs_id,
24-
documentContent,
25-
sectionContent,
26-
close,
27-
}: ChatFormProps) {
22+
export function ChatForm({ path, sectionContent, close }: ChatFormProps) {
2823
// const [messages, updateChatHistory] = useChatHistory(sectionId);
2924
const [inputValue, setInputValue] = useState("");
3025
const [isLoading, setIsLoading] = useState(false);
@@ -80,9 +75,8 @@ export function ChatForm({
8075
// }
8176

8277
const result = await askAI({
78+
path,
8379
userQuestion,
84-
docsId: docs_id,
85-
documentContent,
8680
sectionContent,
8781
replOutputs,
8882
files,
@@ -94,7 +88,9 @@ export function ChatForm({
9488
console.log(result.error);
9589
} else {
9690
addChat(result.chat);
97-
// TODO: chatIdが指す対象の回答にフォーカス
91+
document.getElementById(result.chat.sectionId)?.scrollIntoView({
92+
behavior: "smooth",
93+
});
9894
setInputValue("");
9995
close();
10096
}

app/[lang]/[pageId]/chatHistory.tsx

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
"use client";
22

33
import { ChatWithMessages, getChat } from "@/lib/chatHistory";
4+
import { PagePath } from "@/lib/docs";
45
import {
56
createContext,
67
ReactNode,
@@ -28,11 +29,11 @@ export function useChatHistoryContext() {
2829

2930
export function ChatHistoryProvider({
3031
children,
31-
docs_id,
32+
path,
3233
initialChatHistories,
3334
}: {
3435
children: ReactNode;
35-
docs_id: string;
36+
path: PagePath;
3637
initialChatHistories: ChatWithMessages[];
3738
}) {
3839
const [chatHistories, setChatHistories] =
@@ -43,7 +44,7 @@ export function ChatHistoryProvider({
4344
}, [initialChatHistories]);
4445
// その後、クライアント側で最新のchatHistoriesを改めて取得して更新する
4546
const { data: fetchedChatHistories } = useSWR<ChatWithMessages[]>(
46-
docs_id,
47+
path,
4748
getChat,
4849
{
4950
// リクエストは古くても構わないので1回でいい

app/[lang]/[pageId]/page.tsx

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,17 @@ import { notFound } from "next/navigation";
33
import { PageContent } from "./pageContent";
44
import { ChatHistoryProvider } from "./chatHistory";
55
import { getChatFromCache, initContext } from "@/lib/chatHistory";
6-
import { getMarkdownSections, getPagesList } from "@/lib/docs";
6+
import {
7+
getMarkdownSections,
8+
getPagesList,
9+
LangId,
10+
PageSlug,
11+
} from "@/lib/docs";
712

813
export async function generateMetadata({
914
params,
1015
}: {
11-
params: Promise<{ lang: string; pageId: string }>;
16+
params: Promise<{ lang: LangId; pageId: PageSlug }>;
1217
}): Promise<Metadata> {
1318
const { lang, pageId } = await params;
1419
const pagesList = await getPagesList();
@@ -28,7 +33,7 @@ export async function generateMetadata({
2833
export default async function Page({
2934
params,
3035
}: {
31-
params: Promise<{ lang: string; pageId: string }>;
36+
params: Promise<{ lang: LangId; pageId: PageSlug }>;
3237
}) {
3338
const { lang, pageId } = await params;
3439
const pagesList = await getPagesList();
@@ -41,28 +46,28 @@ export default async function Page({
4146
const nextPage = langEntry.pages[pageEntryIndex + 1];
4247

4348
const docsId = `${lang}/${pageId}`;
49+
// server componentなのでuseMemoいらない
50+
const path = { lang: lang, page: pageId };
4451
const sections = await getMarkdownSections(lang, pageId);
4552

46-
// AI用のドキュメント全文(rawContentを結合)
47-
const documentContent = sections.map((s) => s.rawContent).join("\n");
48-
4953
const context = await initContext();
50-
const initialChatHistories = await getChatFromCache(docsId, context);
54+
const initialChatHistories = await getChatFromCache(path, context);
5155

5256
return (
5357
<ChatHistoryProvider
5458
initialChatHistories={initialChatHistories}
55-
docs_id={docsId}
59+
path={path}
5660
>
5761
<PageContent
58-
documentContent={documentContent}
5962
splitMdContent={sections}
63+
langEntry={langEntry}
6064
pageEntry={pageEntry}
6165
docs_id={docsId}
6266
lang={lang}
6367
pageId={pageId}
6468
prevPage={prevPage}
6569
nextPage={nextPage}
70+
path={path}
6671
/>
6772
</ChatHistoryProvider>
6873
);

0 commit comments

Comments
 (0)