fix
This commit is contained in:
1
browser/dist/assets/index-CvFXbZtL.css
vendored
1
browser/dist/assets/index-CvFXbZtL.css
vendored
@@ -1 +0,0 @@
|
|||||||
.container{max-width:800px;margin:0 auto;padding:2rem}h1{font-size:2rem;margin-bottom:1.5rem;color:#333}.input-section{margin-bottom:2rem;display:flex;gap:.5rem}.at-uri-input{flex:1;padding:.75rem;font-size:1rem;border:2px solid #ddd;border-radius:4px;font-family:monospace}.at-uri-input:focus{outline:none;border-color:#06c}.input-section button{padding:.75rem 2rem;font-size:1rem;background:#06c;color:#fff;border:none;border-radius:4px;cursor:pointer;font-weight:500}.input-section button:hover{background:#0052a3}.info-section{background:#fff;padding:1.5rem;border-radius:8px;margin-bottom:2rem;box-shadow:0 2px 4px #0000001a}.info-section h2{font-size:1.5rem;margin-bottom:1rem;color:#333}.info-section ul{list-style-position:inside;color:#666}.info-section li{margin-bottom:.5rem}.back-link{display:inline-block;color:#06c;text-decoration:none;font-weight:500}.back-link:hover{text-decoration:underline}.record-view{background:#fff;padding:2rem;border-radius:8px;margin-bottom:2rem;box-shadow:0 2px 4px #0000001a}.record-view h2{font-size:2rem;margin-bottom:1rem;color:#333}.record-meta{margin-bottom:1.5rem;padding-bottom:1rem;border-bottom:1px solid #eee}.record-meta p{margin:.5rem 0;color:#666;font-size:.9rem;font-family:monospace}.record-content{line-height:1.8;color:#333}.record-content pre{white-space:pre-wrap;word-wrap:break-word;font-family:inherit;margin:0}*{margin:0;padding:0;box-sizing:border-box}body{font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen,Ubuntu,Cantarell,Fira Sans,Droid Sans,Helvetica Neue,sans-serif;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;background:#f5f5f5}code{font-family:source-code-pro,Menlo,Monaco,Consolas,Courier New,monospace}
|
|
||||||
File diff suppressed because one or more lines are too long
1
browser/dist/assets/index-t2ajyYjt.css
vendored
Normal file
1
browser/dist/assets/index-t2ajyYjt.css
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
.container{max-width:800px;margin:0 auto;padding:2rem}h1{font-size:2rem;margin-bottom:1.5rem;color:#333}.input-section{margin-bottom:2rem;display:flex;gap:.5rem}.at-uri-input{flex:1;padding:.75rem;font-size:1rem;border:2px solid #ddd;border-radius:4px;font-family:monospace}.at-uri-input:focus{outline:none;border-color:#06c}.input-section button{padding:.75rem 2rem;font-size:1rem;background:#06c;color:#fff;border:none;border-radius:4px;cursor:pointer;font-weight:500}.input-section button:hover{background:#0052a3}.info-section{background:#fff;padding:1.5rem;border-radius:8px;margin-bottom:2rem;box-shadow:0 2px 4px #0000001a}.info-section h2{font-size:1.5rem;margin-bottom:1rem;color:#333}.info-section ul{list-style-position:inside;color:#666}.info-section li{margin-bottom:.5rem}.back-link{display:inline-block;color:#06c;text-decoration:none;font-weight:500}.back-link:hover{text-decoration:underline}.record-view{background:#fff;padding:2rem;border-radius:8px;margin-bottom:2rem;box-shadow:0 2px 4px #0000001a}.record-view h2{font-size:2rem;margin-bottom:1rem;color:#333}.record-meta{margin-bottom:1.5rem;padding-bottom:1rem;border-bottom:1px solid #eee}.record-meta p{margin:.5rem 0;color:#666;font-size:.9rem;font-family:monospace}.record-content{line-height:1.8;color:#333}.record-content pre{white-space:pre-wrap;word-wrap:break-word;font-family:inherit;margin:0}.error-section{background:#fee;padding:1rem;border-radius:4px;margin-bottom:1rem;color:#c33}.records-list{background:#fff;padding:2rem;border-radius:8px;margin-bottom:2rem;box-shadow:0 2px 4px #0000001a}.records-list h2{font-size:1.5rem;margin-bottom:1rem;color:#333}.records-list ul{list-style:none;padding:0;margin:0}.records-list li{border-bottom:1px solid #eee}.records-list li:last-child{border-bottom:none}.record-link{display:flex;justify-content:space-between;align-items:center;width:100%;padding:1rem;background:none;border:none;cursor:pointer;text-align:left;transition:background .2s}.record-link:hover{background:#f5f5f5}.record-title{font-size:1.1rem;color:#06c;font-weight:500}.record-date{color:#666;font-size:.9rem}.back-button{padding:.5rem 1rem;margin-bottom:1rem;background:#f5f5f5;border:1px solid #ddd;border-radius:4px;cursor:pointer;font-size:.9rem;color:#666}.back-button:hover{background:#eee}.input-section button:disabled{background:#ccc;cursor:not-allowed}*{margin:0;padding:0;box-sizing:border-box}body{font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen,Ubuntu,Cantarell,Fira Sans,Droid Sans,Helvetica Neue,sans-serif;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;background:#f5f5f5}code{font-family:source-code-pro,Menlo,Monaco,Consolas,Courier New,monospace}
|
||||||
4
browser/dist/index.html
vendored
4
browser/dist/index.html
vendored
@@ -4,8 +4,8 @@
|
|||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<title>AT Browser</title>
|
<title>AT Browser</title>
|
||||||
<script type="module" crossorigin src="/at/assets/index-DrFpc8Xj.js"></script>
|
<script type="module" crossorigin src="/at/assets/index-CyrVFHrY.js"></script>
|
||||||
<link rel="stylesheet" crossorigin href="/at/assets/index-CvFXbZtL.css">
|
<link rel="stylesheet" crossorigin href="/at/assets/index-t2ajyYjt.css">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="root"></div>
|
<div id="root"></div>
|
||||||
|
|||||||
381
claude.md
381
claude.md
@@ -1,381 +0,0 @@
|
|||||||
# ailog
|
|
||||||
|
|
||||||
## 概要
|
|
||||||
|
|
||||||
atprotoベースの静的ブログジェネレーター。markdownファイルをatproto recordとして保存し、それを元に静的サイトを生成する。
|
|
||||||
|
|
||||||
## Collection Schema
|
|
||||||
|
|
||||||
### ai.syui.log.post (ブログ記事)
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"lexicon": 1,
|
|
||||||
"id": "ai.syui.log.post",
|
|
||||||
"defs": {
|
|
||||||
"main": {
|
|
||||||
"type": "record",
|
|
||||||
"description": "Record containing a blog post.",
|
|
||||||
"key": "tid",
|
|
||||||
"record": {
|
|
||||||
"type": "object",
|
|
||||||
"required": ["title", "content", "createdAt"],
|
|
||||||
"properties": {
|
|
||||||
"title": {
|
|
||||||
"type": "string",
|
|
||||||
"maxLength": 3000,
|
|
||||||
"maxGraphemes": 300,
|
|
||||||
"description": "The title of the post."
|
|
||||||
},
|
|
||||||
"content": {
|
|
||||||
"type": "string",
|
|
||||||
"maxLength": 1000000,
|
|
||||||
"maxGraphemes": 100000,
|
|
||||||
"description": "The content of the post."
|
|
||||||
},
|
|
||||||
"createdAt": {
|
|
||||||
"type": "string",
|
|
||||||
"format": "datetime",
|
|
||||||
"description": "Client-declared timestamp when this post was originally created."
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### ai.syui.log.comment (コメント)
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"lexicon": 1,
|
|
||||||
"id": "ai.syui.log.comment",
|
|
||||||
"defs": {
|
|
||||||
"main": {
|
|
||||||
"type": "record",
|
|
||||||
"description": "Record containing a comment.",
|
|
||||||
"key": "tid",
|
|
||||||
"record": {
|
|
||||||
"type": "object",
|
|
||||||
"required": ["content", "createdAt", "post"],
|
|
||||||
"properties": {
|
|
||||||
"content": {
|
|
||||||
"type": "string",
|
|
||||||
"maxLength": 100000,
|
|
||||||
"maxGraphemes": 10000,
|
|
||||||
"description": "The content of the comment."
|
|
||||||
},
|
|
||||||
"createdAt": {
|
|
||||||
"type": "string",
|
|
||||||
"format": "datetime",
|
|
||||||
"description": "Client-declared timestamp when this comment was originally created."
|
|
||||||
},
|
|
||||||
"parent": {
|
|
||||||
"type": "ref",
|
|
||||||
"ref": "com.atproto.repo.strongRef"
|
|
||||||
},
|
|
||||||
"post": {
|
|
||||||
"type": "ref",
|
|
||||||
"ref": "com.atproto.repo.strongRef"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## CLIコマンド仕様
|
|
||||||
|
|
||||||
### login - ログイン
|
|
||||||
|
|
||||||
```bash
|
|
||||||
ailog login ${handle} -p ${password} -s ${pds}
|
|
||||||
```
|
|
||||||
|
|
||||||
- handleからDID, PDSを解決
|
|
||||||
- 認証情報を`~/.config/syui/ai/log/config.json`に保存
|
|
||||||
|
|
||||||
### post - 記事をatprotoに投稿
|
|
||||||
|
|
||||||
```bash
|
|
||||||
ailog post
|
|
||||||
```
|
|
||||||
|
|
||||||
- `./content/post/*.md`を読み込む
|
|
||||||
- frontmatterからtitle, dateなどを抽出
|
|
||||||
- markdown本文をcontentに設定
|
|
||||||
- `ai.syui.log.post`としてputRecord
|
|
||||||
- 既存recordがあれば更新、なければ新規作成
|
|
||||||
|
|
||||||
### build - 静的サイト生成
|
|
||||||
|
|
||||||
```bash
|
|
||||||
ailog build
|
|
||||||
```
|
|
||||||
|
|
||||||
- atprotoから`ai.syui.log.post`のrecordを取得
|
|
||||||
- markdownをHTMLに変換
|
|
||||||
- 静的ファイルとして`./public`に出力
|
|
||||||
|
|
||||||
## URL構造
|
|
||||||
|
|
||||||
### 記事一覧
|
|
||||||
|
|
||||||
```
|
|
||||||
localhost/at://did:plc:ragtjsm2j2vknwkz3zp4oxrd/ai.syui.log.post/
|
|
||||||
```
|
|
||||||
|
|
||||||
- atprotoから全recordを取得
|
|
||||||
- 一覧表示
|
|
||||||
|
|
||||||
### 個別記事
|
|
||||||
|
|
||||||
```
|
|
||||||
localhost/.../3mbnbdt4bas2a
|
|
||||||
```
|
|
||||||
|
|
||||||
- atproto URIからrecordを取得
|
|
||||||
- markdownをレンダリングして表示
|
|
||||||
|
|
||||||
## データフロー
|
|
||||||
|
|
||||||
### 記事投稿
|
|
||||||
|
|
||||||
```
|
|
||||||
./content/post/*.md
|
|
||||||
↓ (frontmatter + markdown解析)
|
|
||||||
ailog post
|
|
||||||
↓ (putRecord)
|
|
||||||
atproto PDS (ai.syui.log.post)
|
|
||||||
```
|
|
||||||
|
|
||||||
### 静的サイト生成
|
|
||||||
|
|
||||||
```
|
|
||||||
atproto PDS (ai.syui.log.post)
|
|
||||||
↓ (listRecords)
|
|
||||||
ailog build
|
|
||||||
↓ (markdown → HTML)
|
|
||||||
./public/*.html
|
|
||||||
```
|
|
||||||
|
|
||||||
## at browser統合
|
|
||||||
|
|
||||||
- atproto URIでの記事アクセス
|
|
||||||
- handle解決 → DID → PDS URLの取得
|
|
||||||
- NetworkConfig (pdsApi, bskyApi, plcApi) の割り当て
|
|
||||||
|
|
||||||
```rust
|
|
||||||
match pds {
|
|
||||||
"bsky.social" | "bsky.app" => NetworkConfig {
|
|
||||||
pds_api: format!("https://{}", pds),
|
|
||||||
plc_api: "https://plc.directory".to_string(),
|
|
||||||
bsky_api: "https://public.api.bsky.app".to_string(),
|
|
||||||
web_url: "https://bsky.app".to_string(),
|
|
||||||
},
|
|
||||||
"syu.is" => NetworkConfig {
|
|
||||||
pds_api: "https://syu.is".to_string(),
|
|
||||||
plc_api: "https://plc.syu.is".to_string(),
|
|
||||||
bsky_api: "https://bsky.syu.is".to_string(),
|
|
||||||
web_url: "https://web.syu.is".to_string(),
|
|
||||||
},
|
|
||||||
_ => NetworkConfig {
|
|
||||||
pds_api: format!("https://{}", pds),
|
|
||||||
plc_api: "https://plc.directory".to_string(),
|
|
||||||
bsky_api: "https://public.api.bsky.app".to_string(),
|
|
||||||
web_url: "https://bsky.app".to_string(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## 参考実装
|
|
||||||
|
|
||||||
- `./repos/log` - 既存のailog実装 (Rust)
|
|
||||||
- `./repos/frontpage` - frontpage (fyi.unravel.frontpage)
|
|
||||||
- `./repos/pfrazee.com` - Paul FrazeeのLeaflet実装 (TypeScript)
|
|
||||||
|
|
||||||
## Paul Frazeeの仕組み
|
|
||||||
|
|
||||||
pfrazee.comは以下の流れでLeafletをblogに統合:
|
|
||||||
|
|
||||||
1. **lexicon schema取得**
|
|
||||||
```bash
|
|
||||||
lex install pub.leaflet.document
|
|
||||||
lex build --out ./util
|
|
||||||
```
|
|
||||||
|
|
||||||
2. **データ取得**
|
|
||||||
```typescript
|
|
||||||
// handle → DID → PDS
|
|
||||||
const did = await resolver.handle.resolve('pfrazee.com')
|
|
||||||
const pds = await resolver.did.resolveAtprotoData(did).pds
|
|
||||||
|
|
||||||
// listRecords
|
|
||||||
const result = await client.list(leaflet.document, {
|
|
||||||
repo: did,
|
|
||||||
limit: 50
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
3. **画像取得**
|
|
||||||
```typescript
|
|
||||||
for (const blobRef of enumBlobRefs(leaflet.value)) {
|
|
||||||
const blobRes = await client.getBlob(did, blobRef.ref)
|
|
||||||
await fsp.writeFile(imagePath, blobRes.payload.body)
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
4. **静的サイト生成** (Next.js)
|
|
||||||
- JSONからHTMLレンダリング
|
|
||||||
- 型安全なblock rendering
|
|
||||||
|
|
||||||
## 既存のai.syui.log形式
|
|
||||||
|
|
||||||
現在のchat形式:
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"uri": "at://did:plc:6qyecktefllvenje24fcxnie/ai.syui.log/2025-06-14-blog",
|
|
||||||
"value": {
|
|
||||||
"url": "https://syui.ai/posts/2025-06-14-blog",
|
|
||||||
"post": {
|
|
||||||
"url": "https://syui.ai/posts/2025-06-14-blog",
|
|
||||||
"date": "",
|
|
||||||
"slug": "",
|
|
||||||
"tags": [],
|
|
||||||
"title": "syui.ai",
|
|
||||||
"language": "ja"
|
|
||||||
},
|
|
||||||
"text": "test",
|
|
||||||
"type": "comment",
|
|
||||||
"$type": "ai.syui.log",
|
|
||||||
"author": {
|
|
||||||
"did": "did:plc:6qyecktefllvenje24fcxnie",
|
|
||||||
"avatar": "https://...",
|
|
||||||
"handle": "ai.syui.ai",
|
|
||||||
"displayName": "ai"
|
|
||||||
},
|
|
||||||
"createdAt": "2025-06-17T06:24:37.386Z"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## 設定ファイル
|
|
||||||
|
|
||||||
### ~/.config/syui/ai/log/config.json
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"pds": "syu.is",
|
|
||||||
"handle": "ai.syui.ai",
|
|
||||||
"did": "did:plc:6qyecktefllvenje24fcxnie",
|
|
||||||
"access_jwt": "...",
|
|
||||||
"refresh_jwt": "..."
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### ~/.config/syui/ai/log/mapping.json
|
|
||||||
|
|
||||||
ファイル名とatproto recordの紐付け(自動生成):
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"2026-01-08-test.md": {
|
|
||||||
"rkey": "3mbnbdt4bas2a",
|
|
||||||
"uri": "at://did:plc:xxx/ai.syui.log.post/3mbnbdt4bas2a",
|
|
||||||
"cid": "bafyrei..."
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
**動作:**
|
|
||||||
- すべてのAPI呼び出し前に自動でrefreshSessionを実行してJWTトークンを更新
|
|
||||||
- `ailog post` - 初回投稿時にTID形式のrkeyが自動生成され、mappingに保存
|
|
||||||
- `ailog post` - 2回目以降は既存rkeyで更新(putRecord)
|
|
||||||
- `ailog delete` - 全削除時にmappingもクリア
|
|
||||||
|
|
||||||
## ディレクトリ構造
|
|
||||||
|
|
||||||
```
|
|
||||||
ailog/
|
|
||||||
├── content/
|
|
||||||
│ └── post/
|
|
||||||
│ ├── 2025-01-08-example.md
|
|
||||||
│ └── ...
|
|
||||||
├── public/
|
|
||||||
│ ├── index.html
|
|
||||||
│ ├── posts/
|
|
||||||
│ │ └── 2025-01-08-example.html
|
|
||||||
│ └── at/
|
|
||||||
│ └── ...
|
|
||||||
├── lexicons/
|
|
||||||
│ └── ai/
|
|
||||||
│ └── syui/
|
|
||||||
│ └── log/
|
|
||||||
│ ├── post.json
|
|
||||||
│ └── comment.json
|
|
||||||
└── templates/
|
|
||||||
├── index.html
|
|
||||||
├── post.html
|
|
||||||
└── layout.html
|
|
||||||
```
|
|
||||||
|
|
||||||
## 開発優先順位
|
|
||||||
|
|
||||||
1. CLIコマンド実装 ✅
|
|
||||||
- [x] collection schema定義
|
|
||||||
- [x] `ailog login` - handle → DID解決 + JWT保存
|
|
||||||
- [x] `ailog post` - ./content/post/*.md → putRecord
|
|
||||||
- [x] `ailog build` - listRecords → 静的HTML生成
|
|
||||||
|
|
||||||
2. at browser統合 (進行中)
|
|
||||||
- [x] React/TypeScript環境セットアップ
|
|
||||||
- [x] atproto client library (repos/log/pdsベース)
|
|
||||||
- [ ] at browser components
|
|
||||||
- [ ] 静的サイトへの統合
|
|
||||||
- [ ] ai.syui.log.post 表示スタイル
|
|
||||||
|
|
||||||
3. スタイリング
|
|
||||||
- [ ] Tailwind CSS統合
|
|
||||||
- [ ] repos/log風デザイン
|
|
||||||
|
|
||||||
## 実装済み
|
|
||||||
|
|
||||||
### Rust CLI (src/)
|
|
||||||
|
|
||||||
- `src/main.rs` - CLIエントリーポイント
|
|
||||||
- `src/config.rs` - ~/.config/syui/ai/log/config.json管理
|
|
||||||
- `src/login.rs` - atproto認証 (describeRepo + createSession)
|
|
||||||
- `src/refresh.rs` - セッションリフレッシュ (refreshSession)
|
|
||||||
- `src/post.rs` - markdown投稿 (createRecord/putRecord)
|
|
||||||
- `src/build.rs` - 静的サイト生成 (listRecords + markdown→HTML)
|
|
||||||
- `src/delete.rs` - record削除 (deleteRecord)
|
|
||||||
|
|
||||||
### at browser (browser/)
|
|
||||||
|
|
||||||
- `browser/package.json` - React + TypeScript + Tailwind
|
|
||||||
- 実装予定: repos/log/pdsの実装をベースに作成
|
|
||||||
|
|
||||||
## 使用方法
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# ログイン
|
|
||||||
ailog l ai.syui.ai -p PASSWORD -s syu.is
|
|
||||||
|
|
||||||
# 記事投稿(初回: 新規作成、2回目以降: 更新)
|
|
||||||
ailog p
|
|
||||||
|
|
||||||
# サイト生成
|
|
||||||
ailog b
|
|
||||||
|
|
||||||
# 全削除
|
|
||||||
ailog d
|
|
||||||
```
|
|
||||||
|
|
||||||
**エイリアス:**
|
|
||||||
- `l` = `login`
|
|
||||||
- `p` = `post`
|
|
||||||
- `b` = `build`
|
|
||||||
- `d` = `delete`
|
|
||||||
Reference in New Issue
Block a user