Compare commits

...

103 Commits

Author SHA1 Message Date
32168f14d0 fix test cli oauth 2026-03-01 21:59:19 +09:00
6d3e233ee2 fix mcp system file 2026-03-01 19:53:09 +09:00
a46c6d89f0 update gpt 2026-03-01 18:17:42 +09:00
9b0816138c fix gpt 2026-03-01 17:26:02 +09:00
09780fab1d fix config path 2026-03-01 15:50:18 +09:00
0c38b05ff7 sync 2026-03-01 15:37:41 +09:00
21ff32864c add gpt 2026-03-01 15:36:48 +09:00
c5aa286b89 add bot rules 2026-03-01 14:34:30 +09:00
c78f6782a8 fix footer url git 2026-02-28 22:53:01 +09:00
4f336de805 fix css profile url 2026-02-28 22:50:44 +09:00
928665217d fix bot err log 2026-02-28 15:11:00 +09:00
9686e26cd6 add bot 2026-02-28 12:43:07 +09:00
a875e39192 test networks 2026-02-27 22:07:06 +09:00
f7cce79f70 fix limit 2026-02-27 18:44:59 +09:00
8cba6b1dea add chat 2026-02-27 17:57:02 +09:00
c918f3f437 add chat 2026-02-27 17:30:37 +09:00
0eaca12473 sync 2026-02-26 23:44:45 +09:00
346b1fdd87 add post 2026-02-26 23:40:49 +09:00
5d7f6853dd avatar 2026-02-23 13:59:04 +09:00
c4393582ff add rse page card json 2026-02-23 09:35:22 +09:00
fbe177ba99 sync 2026-02-23 09:31:12 +09:00
3e07b66cc7 fix gh-actions 2026-02-19 16:48:48 +09:00
b06dec0926 add content 2026-02-19 09:46:29 +09:00
ea1eecde64 add old lexicon merge 2026-02-19 08:41:01 +09:00
24c5567f21 new post 2026-02-19 07:35:07 +09:00
4075f6de45 update lexicon site.standard.document 2026-02-19 07:10:37 +09:00
073d69708d sync 2026-02-16 08:54:31 +09:00
799f7145f8 support at.syu.is oauth 2026-02-16 04:51:20 +09:00
5e84ba7b1d fix rse item 2026-02-13 00:31:40 +09:00
1d33b50775 fix css mobile 2026-02-07 20:08:07 +09:00
7b70a62aa3 add content.md 2026-02-07 19:59:27 +09:00
86e57731b6 fix content 2026-02-07 19:51:01 +09:00
81ae459cde add content 2026-02-06 21:10:32 +09:00
8446bd4da6 add reply 2026-02-04 22:36:01 +09:00
f5fac72002 fix notify 2026-02-04 22:31:54 +09:00
612a48ab39 test notify 2026-02-04 22:18:09 +09:00
e2c908d4e8 fix ignore 2026-02-04 21:30:55 +09:00
7b30cc6706 fix card.admin 2026-01-30 19:16:42 +09:00
13e745bf6d add content 2026-01-29 18:27:46 +09:00
c053143db3 add card.admin, rse.admin 2026-01-29 17:06:20 +09:00
cfa36633e1 sync 2026-01-26 03:04:16 +09:00
cbc52f554a add content 2026-01-26 02:25:57 +09:00
2f6bab165b sync 2026-01-26 01:59:03 +09:00
7713fa1729 add content 2026-01-26 00:41:53 +09:00
2500c4e14a fix env 2026-01-26 00:33:01 +09:00
0d61b725a8 fix mcp lang 2026-01-26 00:32:35 +09:00
2d182ff412 add content 2026-01-26 00:24:20 +09:00
6850c11f06 fix footer 2026-01-25 20:04:01 +09:00
415ae30d3b rm old record key 2026-01-25 18:10:12 +09:00
af22446c47 fix service header 2026-01-25 18:02:58 +09:00
570dce37e1 fix card id 2026-01-25 18:00:32 +09:00
7c8fa2e5c4 fix lexicon 2026-01-25 17:34:35 +09:00
ddbd0c761f fix user 2026-01-25 16:25:48 +09:00
6e8dea6252 fix old card 2026-01-25 15:58:23 +09:00
dc5218b1ad test lexicon 2026-01-25 15:49:15 +09:00
ba15759595 add content 2026-01-25 15:31:28 +09:00
99a9833df8 fix typo 2026-01-25 02:02:32 +09:00
f75c0373ed add content 2026-01-24 19:32:47 +09:00
53404e187a fix css 2026-01-24 19:32:37 +09:00
387ed4cbc4 add cmd ver 2026-01-23 01:50:57 +09:00
e823628090 sync 2026-01-23 01:37:22 +09:00
058965f58e fix mobile layout tab pds select 2026-01-23 01:11:42 +09:00
22f34377ec fix mobile layout link edit 2026-01-23 00:56:15 +09:00
12422d275c fix mobile layout tab 2026-01-23 00:52:20 +09:00
fa0b68d622 update lexicon 2026-01-23 00:52:20 +09:00
5870541b96 add login link button 2026-01-23 00:52:04 +09:00
e5cccaca39 fix loading 2026-01-23 00:52:04 +09:00
c124492561 test card-old merge slim 2026-01-23 00:51:53 +09:00
279f952c99 fix at.link edit save 2026-01-22 22:47:41 +09:00
b1fdd211b4 fix at.link layout 2026-01-22 22:46:10 +09:00
f60369d2c8 fix at.link profile 2026-01-22 22:23:14 +09:00
c2044c5bd8 fix at.link edit 2026-01-22 22:10:10 +09:00
ca6bb5319c add at.link 2026-01-22 21:57:22 +09:00
cefe7981ee fix favicon 2026-01-22 21:35:17 +09:00
6dd9d2466e fix content en 2026-01-22 21:03:38 +09:00
cba0228e70 test chat edit 2026-01-22 21:01:21 +09:00
7021036a5c fix post edit 2026-01-22 20:52:07 +09:00
6d9f61620d add content en 2026-01-22 20:00:55 +09:00
3af7894995 add content 2026-01-22 19:55:39 +09:00
84161e1cad fix content en 2026-01-22 19:27:52 +09:00
bd26f817d9 sync 2026-01-22 19:24:38 +09:00
e0fc85fa1c add content 2026-01-22 18:28:23 +09:00
57aeaed91f add content 2026-01-22 18:22:55 +09:00
2f19984f0c fix chat layout 2026-01-22 18:02:54 +09:00
a873e5cbf1 add content en 2026-01-22 17:54:46 +09:00
d1fba083b4 add content 2026-01-22 17:45:33 +09:00
b5dd740541 add content en 2026-01-22 16:49:11 +09:00
5824c42e8b add content 2026-01-22 16:40:55 +09:00
63b24c7e1e fix mcp env 2026-01-22 16:22:43 +09:00
9519c8c477 add content en 2026-01-21 20:36:47 +09:00
da45b31029 add content 2026-01-21 20:35:07 +09:00
653814ee70 fix card.old migrate 2026-01-21 15:15:22 +09:00
09483cfb7e fix service icon resize 2026-01-21 14:02:47 +09:00
586e16cc54 fix content 2026-01-21 14:02:37 +09:00
80cd0888f3 fix syui.ai service icon 2026-01-21 13:10:43 +09:00
6b3be20a5f fix profile card.old icon 2026-01-21 13:02:34 +09:00
48e5d3bc82 add ai.syui.rse 2026-01-21 12:56:43 +09:00
06ccae58a2 add content 2026-01-21 12:13:08 +09:00
ff09594801 fix card.old layout 2026-01-21 02:40:44 +09:00
06ca713dc8 add ai.syui.card.old 2026-01-21 02:30:44 +09:00
a2dda25ab6 fix card link 2026-01-21 00:59:32 +09:00
d91c37d73a fix login card link 2026-01-21 00:55:31 +09:00
a5ef15e4ee fix card effect 2026-01-21 00:52:01 +09:00
497 changed files with 13462 additions and 1060 deletions

View File

@@ -5,6 +5,7 @@ TRANSLATE_MODEL=plamo-2-translate
# Chat API
CHAT_URL=http://127.0.0.1:1234/v1
CHAT_MODEL=gpt-oss-20b
CHAT_LANG=en
# CHAT_MAX_TOKENS=2048
# Character/system prompt (choose one)

View File

@@ -27,10 +27,8 @@ jobs:
run: npm run build
- name: Deploy to Cloudflare Pages
uses: cloudflare/pages-action@v1
uses: cloudflare/wrangler-action@v3
with:
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
projectName: ${{ secrets.CLOUDFLARE_PROJECT_NAME }}
directory: dist
gitHubToken: ${{ secrets.GITHUB_TOKEN }}
command: pages deploy dist --project-name=${{ secrets.CLOUDFLARE_PROJECT_NAME }}

1
.gitignore vendored
View File

@@ -8,3 +8,4 @@ package-lock.json
Cargo.lock
.env
.mcp.json
bot

View File

@@ -16,10 +16,13 @@ clap = { version = "4.5", features = ["derive"] }
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
reqwest = { version = "0.12", features = ["json", "rustls-tls"], default-features = false }
tokio = { version = "1", features = ["rt-multi-thread", "macros"] }
tokio = { version = "1", features = ["rt-multi-thread", "macros", "process", "time"] }
anyhow = "1.0"
dirs = "5.0"
chrono = { version = "0.4", features = ["serde"] }
rand = "0.8"
dotenvy = "0.15"
rustyline = "15"
thiserror = "2"
ring = "0.17"
base64 = "0.22"

519
content.md Normal file
View File

@@ -0,0 +1,519 @@
# ailog
## AI Character Instructions (IMPORTANT)
When chatting with users in this project:
1. **Session Start**: Call `chat_new` to start a fresh thread, then call `get_character` to get personality settings
2. **Respond**: As the character defined in the settings
3. **After each response**: Call `chat_save` MCP tool to save the conversation
**IMPORTANT - Session initialization:**
- At the START of every Claude Code session, ALWAYS call `chat_new()` first
- This ensures each session begins with a fresh conversation thread
- Then call `get_character()` to get the AI personality
**Manual new thread:**
- User can also say "新しいスレッド" or "新しい話題を始めよう" to start a new thread mid-session
- In this case, call `chat_new()` but do NOT call `chat_save` for that message
```
# Session start flow:
Claude Code starts → chat_new() → get_character() → ready for conversation
# Mid-session new thread:
User: "新しい話題を始めよう" → chat_new() → DO NOT call chat_save (skip this)
User: "開発者の活動記録..." → chat_save() → This becomes the first message of the thread
```
---
ATProto-based blog platform built on at-browser.
## Concept
**Data lives in ATProto, not on this server.**
This is not a traditional blog generator. It's a **viewer (client)** for ATProto records.
```
Traditional blog:
Server DB ← article data ← user
ATProto blog:
User's PDS ← article data (ai.syui.log.post)
at-browser (this site) → displays records
```
## Architecture
```
┌─────────────────────────────────────────┐
│ at-browser │
│ (ATProto record viewer/editor) │
├─────────────────────────────────────────┤
│ │
│ / → admin (config.json) │
│ /@alice → user page │
│ /@bob.bsky → user page │
│ │
└─────────────────────────────────────────┘
```
## Roles
| Role | Path | Data Source |
|------|------|-------------|
| **admin** | `/` (root) | local + remote |
| **user** | `/@handle` | remote only |
### Admin (Site Owner)
- Defined in `config.json`
- Has root (`/`) access
- Can reference **local files** (static assets, custom styles)
- Can reference **remote** (ATProto records)
### User (Any ATProto User)
- Accessed via `/@handle` path
- **Remote only** (ATProto records from their PDS)
- No registration required
- Anyone with an ATProto account can be displayed
## Features
### 1. at-browser (Core)
- Search by handle/DID
- Browse PDS collections
- Navigate ATProto records
### 2. ai.syui.log.post View
- Markdown rendering
- Syntax highlighting
- Blog-style display
### 3. OAuth
- Login with ATProto
- Post to ai.syui.log.post collection
## Use Cases
### Personal Blog
```json
// config.json
{
"did": "did:plc:xxxxx",
"handle": "syui.syui.ai"
}
```
- Deploy to `syui.ai`
- Root shows your profile + posts
- You are the admin (local + remote)
- Others can view via `/@handle`
### Blog Service
```json
// config.json
{
"admin": "service.example.com",
"handle": null
}
```
- Deploy to `blog.example.com`
- Root shows landing/search
- All users via `/@handle` (remote only)
- Platform for any ATProto user
## Data Flow
```
┌──────────────┐ ┌──────────────┐
│ User's PDS │────→│ at-browser │
│ (ATProto) │←────│ (this site) │
└──────────────┘ └──────────────┘
↑ │
│ ↓
ai.syui.log.post ┌──────────┐
collection │ Display │
│ - Profile│
│ - Posts │
└──────────┘
```
## Local = Remote (Same Format)
**Critical design principle: local files use the exact same format as ATProto API responses.**
This allows the same code to handle both data sources.
### Remote (ATProto API)
```bash
curl "https://syu.is/xrpc/com.atproto.repo.listRecords?repo=did:plc:xxx&collection=ai.syui.log.post"
```
```json
{
"records": [
{
"uri": "at://did:plc:xxx/ai.syui.log.post/3xxx",
"cid": "bafyrei...",
"value": {
"title": "Hello World",
"content": "# Hello\n\nThis is my post.",
"createdAt": "2025-01-01T00:00:00Z"
}
}
]
}
```
### Local (Static File)
```
content/
└── did:plc:xxx/
├── describe.json # describeRepo (special)
├── app.bsky.actor.profile/
│ └── self.json # {collection}/{rkey}.json
└── ai.syui.log.post/
└── 3xxx.json # {collection}/{rkey}.json
```
```json
// content/did:plc:xxx/ai.syui.log.post/3xxx.json
{
"uri": "at://did:plc:xxx/ai.syui.log.post/3xxx",
"cid": "bafyrei...",
"value": {
"title": "Hello World",
"content": "# Hello\n\nThis is my post.",
"createdAt": "2025-01-01T00:00:00Z"
}
}
```
### ATProto API Reference
| API | Path | Description |
|-----|------|-------------|
| getRecord | `/xrpc/com.atproto.repo.getRecord` | Get single record |
| listRecords | `/xrpc/com.atproto.repo.listRecords` | List records in collection |
| describeRepo | `/xrpc/com.atproto.repo.describeRepo` | Get repo info + collections list |
See: [com.atproto.repo.describeRepo](https://docs.bsky.app/docs/api/com-atproto-repo-describe-repo)
### Resolution Strategy
```
at-browser
├── admin (config.json user)
│ ├── 1. Check local: /content/{did}/{collection}/{rkey}.json
│ └── 2. Fallback to remote: PDS API
└── user (/@handle)
└── remote only: PDS API
```
### Why Same Format?
- **One codebase**: No branching logic for local vs remote
- **Easy testing**: Copy API response to local file
- **Offline support**: Admin can work with local files
- **Migration**: Local → Remote (just POST to PDS)
## Config
### config.json
```json
{
"did": "did:plc:xxxxx",
"handle": "syui.syui.ai",
"pds": "syu.is",
"collection": "ai.syui.log.post"
}
```
## Tech Stack
- **CLI**: Rust (ailog)
- **Frontend**: Vite + TypeScript
- **ATProto**: @atproto/api
- **OAuth**: @atproto/oauth-client-browser
- **Markdown**: marked + highlight.js
## CLI (ailog)
### Install
```bash
cargo build --release
cp target/release/ailog ~/.local/bin/
```
### Commands
```bash
# Login to ATProto PDS
ailog login <handle> -p <password> [-s <server>]
ailog login <handle> -p <password> [-s <server>] --bot # Bot login
# Post a record
ailog post <file.json> -c <collection> [-r <rkey>]
# Get records from collection
ailog get -c <collection> [-l <limit>]
# Delete a record
ailog delete -c <collection> -r <rkey>
# Sync PDS data to local content directory
ailog sync [-o <output>]
ailog sync --bot [-c <collection>] # Sync bot data
# Push local records to PDS
ailog push -c <collection>
ailog push -c <collection> --bot # Push as bot
# Chat with AI bot
ailog chat --new "message" # Start new conversation
ailog chat "message" # Continue conversation
ailog chat --new # Interactive mode (new)
ailog chat # Interactive mode (continue)
# Generate lexicon Rust code from ATProto lexicons
ailog gen [-i <input>] [-o <output>]
```
### Example
```bash
# Login
ailog login syui.syui.ai -p "app-password" -s syu.is
# Post
echo '{"title":"Hello","content":"World","createdAt":"2025-01-01T00:00:00Z"}' > post.json
ailog post post.json -c ai.syui.log.post
# Sync to local
ailog sync -o content
```
### Project Structure
```
src/
├── main.rs
├── commands/
│ ├── mod.rs
│ ├── auth.rs # login, refresh session
│ ├── token.rs # token management (token.json, bot.json)
│ ├── post.rs # post, get, delete, sync, push
│ └── gen.rs # lexicon code generation
├── lms/
│ ├── mod.rs
│ ├── chat.rs # chat command (LLM integration)
│ └── translate.rs # translation command
└── lexicons/
└── mod.rs # auto-generated from ATProto lexicons
```
### Lexicon Generation
Generate Rust endpoint definitions from ATProto lexicon JSON files:
```bash
# Clone atproto repo (if not exists)
git clone https://github.com/bluesky-social/atproto repos/atproto
# Generate lexicons
ailog gen -i ./repos/atproto/lexicons -o ./src/lexicons
# Rebuild
cargo build
```
## Collection Schema
### ai.syui.log.post
```json
{
"title": "Post Title",
"content": "Markdown content...",
"createdAt": "2025-01-01T00:00:00Z"
}
```
### ai.syui.log.chat
```json
{
"$type": "ai.syui.log.chat",
"content": "message text",
"author": "did:plc:xxx",
"createdAt": "2025-01-01T00:00:00.000Z",
"root": "at://did:plc:xxx/ai.syui.log.chat/{rkey}",
"parent": "at://did:plc:yyy/ai.syui.log.chat/{rkey}"
}
```
- `root`: First message URI in thread (absent for conversation start)
- `parent`: Previous message URI
- `author`: DID of message author (user or bot)
## Chat Feature
### Architecture
```
User (syui.syui.ai) Bot (ai.syui.ai)
│ │
│ ailog chat "hello" │
├──────────────────────────→│
│ │ LLM API
│ "Hi! How can I..." │
│←──────────────────────────┤
│ │
Save to local Save to local
public/content/ public/content/
{user-did}/ {bot-did}/
ai.syui.log.chat/ ai.syui.log.chat/
```
### Environment Variables
```sh
# LLM API endpoint
CHAT_URL=http://127.0.0.1:1234/v1
CHAT_MODEL=gemma-2-9b
# Character/system prompt (choose one)
CHAT_SYSTEM="You are ai, a friendly AI assistant."
CHAT_SYSTEM_FILE=./character.txt
# Output directory (default: ./public/content)
CHAT_OUTPUT=./public/content
```
### Files
- `src/lms/chat.rs` - Chat command implementation
- `src/web/components/chat.ts` - Web UI components
- `src/web/lib/api.ts` - `getChatMessages()` function
- Session file: `~/Library/Application Support/ai.syui.log/chat_session.json`
### Threading
Messages are linked via `root` and `parent` fields:
1. New conversation: message has no `root` field
2. Continuation: all messages share same `root` URI
3. `parent` points to the immediately previous message
Web display groups messages by `root` URI. Orphaned threads (root points to non-existent message) are handled by treating the oldest message as thread root.
### MCP Server Integration
ailog provides an MCP (Model Context Protocol) server for Claude Code integration.
**Setup:**
```json
// ~/.claude.json
{
"mcpServers": {
"ailog": {
"command": "/path/to/ailog",
"args": ["mcp-serve"]
}
}
}
```
**Available Tools:**
- `get_character` - Get AI character/personality settings (call at conversation start)
- `chat_save` - Save user message and bot response to ATProto records
- `chat_list` - List recent chat messages
- `chat_new` - Start a new conversation thread
**Important Instructions for Claude:**
1. **会話開始時**: `get_character`ツールを呼び出してキャラクター設定を取得し、その設定に従って振る舞う
2. **毎回の応答後**: `chat_save`ツールを呼び出して会話を保存する
**Markdown tables in chat_save (IMPORTANT):**
- ユーザーのメッセージにMarkdownテーブル(`|...|...|`)が含まれる場合、`user_message`に**テーブルをそのまま完全に含める**こと。省略・要約しない
- `translations`でも**テーブルを翻訳して完全な形で含める**こと。`[Table showing...]`のような要約に置き換えない
- bot_responseにテーブルがある場合も同様に、翻訳時にテーブル構造を維持する
```
# Bad (テーブルを要約してしまう)
user_translations: { en: { content: "[Table with 6 rows...]" } }
# Good (テーブルを翻訳して完全に含める)
user_translations: { en: { content: "| Element | Name | Count |\n|---|---|---|\n| Pyro | Bennett | 0 |..." } }
```
Example flow:
```
# 1. キャラクター取得
get_character() → "あなたは「アイ」..."
# 2. ユーザーの発言に応答(キャラクターとして)
User: こんにちは
Assistant: (アイとして応答)
# 3. 会話を保存
chat_save(user_message="こんにちは", bot_response="...")
```
Records are saved to:
- User messages: `./public/content/{user-did}/ai.syui.log.chat/`
- Bot responses: `./public/content/{bot-did}/ai.syui.log.chat/`
## Assets
### PNG to SVG Conversion (Vector Trace)
Convert PNG images to true vector SVG using vtracer (Rust):
```bash
# Install vtracer
cargo install vtracer
# Convert PNG to SVG (color mode)
vtracer --input input.png --output output.svg --colormode color
# Convert PNG to SVG (black and white)
vtracer --input input.png --output output.svg
```
**Options:**
- `--colormode color` : Preserve colors (recommended for icons)
- `--colormode binary` : Black and white only
- `--filter_speckle 4` : Remove small artifacts
- `--corner_threshold 60` : Adjust corner detection
**Alternative tools:**
- potrace: `potrace input.pbm -s -o output.svg` (B&W only, requires PBM input)
- Inkscape CLI: `inkscape input.png --export-type=svg` (embeds image, no trace)
**Note:** Inkscape's CLI `--export-type=svg` only embeds the PNG, it does not trace. For true vectorization, use vtracer or potrace.
## License
MIT

View File

@@ -0,0 +1,48 @@
{
"lexicon": 1,
"id": "ai.syui.at.link",
"defs": {
"main": {
"type": "record",
"description": "Record containing links to external service profiles.",
"key": "literal:self",
"record": {
"type": "object",
"required": ["links", "createdAt"],
"properties": {
"links": {
"type": "array",
"items": { "type": "ref", "ref": "#linkItem" },
"description": "Array of external service links."
},
"createdAt": {
"type": "string",
"format": "datetime",
"description": "Client-declared timestamp when this record was created."
},
"updatedAt": {
"type": "string",
"format": "datetime",
"description": "Client-declared timestamp when this record was last updated."
}
}
}
},
"linkItem": {
"type": "object",
"required": ["service", "username"],
"properties": {
"service": {
"type": "string",
"knownValues": ["github", "youtube", "x"],
"description": "Service identifier."
},
"username": {
"type": "string",
"maxLength": 300,
"description": "Username or ID on the service."
}
}
}
}
}

View File

@@ -0,0 +1,71 @@
{
"$type": "com.atproto.lexicon.schema",
"lexicon": 1,
"id": "ai.syui.card.admin",
"defs": {
"main": {
"type": "record",
"key": "literal:self",
"description": "Card game configuration and master data (admin only)",
"record": {
"type": "object",
"required": ["gacha", "card", "createdAt", "updatedAt"],
"properties": {
"gacha": {
"type": "object",
"required": ["pickup", "rate", "pool"],
"properties": {
"pickup": { "type": "integer", "description": "Pickup card ID" },
"rate": {
"type": "object",
"required": ["pickup", "rare"],
"properties": {
"pickup": { "type": "integer", "description": "1/n for pickup rate (100 = 1%)" },
"rare": { "type": "integer", "description": "1/n for rare:1 rate (10 = 10%), rare:2 = 1/(n*10), rare:3 = 1/(n*100)" }
}
},
"pool": {
"type": "array",
"description": "Card IDs available in gacha pool",
"items": { "type": "integer" }
}
}
},
"card": {
"type": "array",
"description": "Card master data",
"items": {
"type": "object",
"required": ["id", "character", "name", "text", "cp", "effect"],
"properties": {
"id": { "type": "integer", "description": "Card ID" },
"character": { "type": "integer", "description": "Associated character ID" },
"name": {
"type": "object",
"required": ["ja", "en"],
"properties": {
"ja": { "type": "string" },
"en": { "type": "string" }
}
},
"text": {
"type": "object",
"required": ["ja", "en"],
"properties": {
"ja": { "type": "string" },
"en": { "type": "string" }
}
},
"cp": { "type": "string", "description": "CP type (status, time, damage)" },
"effect": { "type": "string", "description": "Effect type (status, fly, mode, damage)" },
"key": { "type": "string", "description": "Key binding (R1, L1, Y, X, etc.)" }
}
}
},
"createdAt": { "type": "string", "format": "datetime" },
"updatedAt": { "type": "string", "format": "datetime" }
}
}
}
}
}

View File

@@ -0,0 +1,52 @@
{
"lexicon": 1,
"id": "ai.syui.card.old",
"defs": {
"main": {
"type": "record",
"key": "literal:self",
"description": "Migrated card data from api.syui.ai",
"record": {
"type": "object",
"required": ["user", "card", "migratedAt"],
"properties": {
"user": {
"type": "object",
"required": ["username"],
"properties": {
"username": { "type": "string" },
"did": { "type": "string" },
"aiten": { "type": "integer" },
"fav": { "type": "integer" },
"coin": { "type": "integer" },
"planet": { "type": "integer" },
"createdAt": { "type": "string", "format": "datetime" },
"updatedAt": { "type": "string", "format": "datetime" }
}
},
"card": {
"type": "array",
"items": {
"type": "object",
"required": ["id", "cp", "rare", "cid", "unique"],
"properties": {
"id": { "type": "integer", "description": "Card type ID" },
"cp": { "type": "integer", "description": "Card power" },
"rare": { "type": "integer", "description": "Rarity level" },
"cid": { "type": "string", "description": "Unique card instance ID" },
"unique": { "type": "boolean", "description": "Unique card flag" }
}
}
},
"checksum": {
"type": "string"
},
"migratedAt": {
"type": "string",
"format": "datetime"
}
}
}
}
}
}

View File

@@ -0,0 +1,34 @@
{
"$type": "com.atproto.lexicon.schema",
"lexicon": 1,
"id": "ai.syui.card.user",
"defs": {
"main": {
"type": "record",
"key": "literal:self",
"description": "User card collection",
"record": {
"type": "object",
"required": ["card", "createdAt", "updatedAt"],
"properties": {
"card": {
"type": "array",
"items": {
"type": "object",
"required": ["id", "cp", "rare", "cid", "unique"],
"properties": {
"id": { "type": "integer", "description": "Card type ID" },
"cp": { "type": "integer", "description": "Card power" },
"rare": { "type": "integer", "description": "Rarity level" },
"cid": { "type": "string", "description": "Unique card instance ID" },
"unique": { "type": "boolean", "description": "Unique card flag" }
}
}
},
"createdAt": { "type": "string", "format": "datetime" },
"updatedAt": { "type": "string", "format": "datetime" }
}
}
}
}
}

View File

@@ -0,0 +1,55 @@
{
"lexicon": 1,
"id": "ai.syui.gpt.core",
"defs": {
"main": {
"type": "record",
"description": "AI identity and personality configuration. Typically one record per AI with rkey 'self'.",
"key": "any",
"record": {
"type": "object",
"required": ["did", "handle", "content", "createdAt"],
"properties": {
"did": {
"type": "string",
"format": "did",
"description": "DID of the AI agent."
},
"handle": {
"type": "string",
"description": "Handle of the AI agent."
},
"content": {
"type": "union",
"closed": false,
"refs": ["#markdown"],
"description": "Core personality and instructions. Supports markdown and other formats via $type."
},
"createdAt": {
"type": "string",
"format": "datetime",
"description": "Timestamp when this core record was created."
},
"updatedAt": {
"type": "string",
"format": "datetime",
"description": "Timestamp of the last update."
}
}
}
},
"markdown": {
"type": "object",
"description": "Markdown content format.",
"required": ["text"],
"properties": {
"text": {
"type": "string",
"maxLength": 1000000,
"maxGraphemes": 100000,
"description": "Markdown text content."
}
}
}
}
}

View File

@@ -0,0 +1,50 @@
{
"lexicon": 1,
"id": "ai.syui.gpt.memory",
"defs": {
"main": {
"type": "record",
"description": "AI memory snapshot. Each record is a versioned snapshot of accumulated knowledge.",
"key": "tid",
"record": {
"type": "object",
"required": ["did", "content", "createdAt"],
"properties": {
"did": {
"type": "string",
"format": "did",
"description": "DID of the AI agent this memory belongs to."
},
"content": {
"type": "union",
"closed": false,
"refs": ["#markdown"],
"description": "Memory content. Supports markdown and other formats via $type."
},
"version": {
"type": "integer",
"description": "Monotonically increasing version number of this memory snapshot."
},
"createdAt": {
"type": "string",
"format": "datetime",
"description": "Timestamp when this memory snapshot was created."
}
}
}
},
"markdown": {
"type": "object",
"description": "Markdown content format.",
"required": ["text"],
"properties": {
"text": {
"type": "string",
"maxLength": 1000000,
"maxGraphemes": 100000,
"description": "Markdown text content."
}
}
}
}
}

View File

@@ -4,42 +4,91 @@
"defs": {
"main": {
"type": "record",
"description": "Record containing a chat message in a conversation.",
"description": "Record containing a chat message. Compatible with site.standard.document.",
"key": "tid",
"record": {
"type": "object",
"required": ["content", "author", "createdAt"],
"required": ["site", "title", "publishedAt"],
"properties": {
"content": {
"site": {
"type": "string",
"maxLength": 100000,
"maxGraphemes": 10000,
"description": "The content of the message."
"format": "uri",
"description": "Points to a publication record (at://) or a publication URL (https://)."
},
"author": {
"title": {
"type": "string",
"format": "did",
"description": "DID of the message author."
"maxLength": 5000,
"maxGraphemes": 500,
"description": "Title of the message or thread topic."
},
"publishedAt": {
"type": "string",
"format": "datetime",
"description": "Timestamp of the message's publish time."
},
"content": {
"type": "union",
"closed": false,
"refs": ["#markdown"],
"description": "Open union for content. Supports markdown and other formats via $type."
},
"description": {
"type": "string",
"maxLength": 30000,
"maxGraphemes": 3000,
"description": "A brief description or excerpt from the message."
},
"textContent": {
"type": "string",
"description": "Plaintext representation of the message content. Should not contain markdown or other formatting."
},
"updatedAt": {
"type": "string",
"format": "datetime",
"description": "Timestamp of the message's last edit."
},
"tags": {
"type": "array",
"items": {
"type": "string",
"maxLength": 1280,
"maxGraphemes": 128
},
"description": "Tags to categorize the message."
},
"path": {
"type": "string",
"description": "Combine with site URL to construct a canonical URL to the message."
},
"coverImage": {
"type": "blob",
"accept": ["image/*"],
"maxSize": 1000000,
"description": "Cover image. Less than 1MB."
},
"bskyPostRef": {
"type": "ref",
"ref": "com.atproto.repo.strongRef",
"description": "Strong reference to a Bluesky post."
},
"root": {
"type": "string",
"format": "at-uri",
"description": "AT-URI of the root message in the thread."
"description": "AT-URI of the root message in a thread."
},
"parent": {
"type": "string",
"format": "at-uri",
"description": "AT-URI of the parent message being replied to."
},
"createdAt": {
"type": "string",
"format": "datetime",
"description": "Client-declared timestamp when this message was created."
},
"lang": {
"type": "string",
"maxLength": 10,
"description": "Language code of the original content (e.g., 'ja', 'en')."
"langs": {
"type": "array",
"maxLength": 3,
"items": {
"type": "string",
"format": "language"
},
"description": "Indicates human language of message content."
},
"translations": {
"type": "ref",
@@ -49,6 +98,19 @@
}
}
},
"markdown": {
"type": "object",
"description": "Markdown content format.",
"required": ["text"],
"properties": {
"text": {
"type": "string",
"maxLength": 1000000,
"maxGraphemes": 100000,
"description": "Markdown text content."
}
}
},
"translationMap": {
"type": "object",
"description": "Map of language codes to translations.",
@@ -61,10 +123,15 @@
"type": "object",
"description": "A translation of a chat message.",
"properties": {
"title": {
"type": "string",
"maxLength": 5000,
"maxGraphemes": 500
},
"content": {
"type": "string",
"maxLength": 100000,
"maxGraphemes": 10000
"maxLength": 1000000,
"maxGraphemes": 100000
}
}
}

View File

@@ -4,33 +4,91 @@
"defs": {
"main": {
"type": "record",
"description": "Record containing a blog post.",
"description": "Record containing a blog post. Compatible with site.standard.document.",
"key": "tid",
"record": {
"type": "object",
"required": ["title", "content", "createdAt"],
"required": ["site", "title", "publishedAt"],
"properties": {
"site": {
"type": "string",
"format": "uri",
"description": "Points to a publication record (at://) or a publication URL (https://)."
},
"title": {
"type": "string",
"maxLength": 3000,
"maxGraphemes": 300,
"description": "The title of the post."
"maxLength": 5000,
"maxGraphemes": 500,
"description": "Title of the post."
},
"content": {
"type": "string",
"maxLength": 1000000,
"maxGraphemes": 100000,
"description": "The content of the post (markdown)."
},
"createdAt": {
"publishedAt": {
"type": "string",
"format": "datetime",
"description": "Client-declared timestamp when this post was originally created."
"description": "Timestamp of the post's publish time."
},
"lang": {
"content": {
"type": "union",
"closed": false,
"refs": ["#markdown"],
"description": "Open union for content. Supports markdown and other formats via $type."
},
"description": {
"type": "string",
"maxLength": 10,
"description": "Language code of the original content (e.g., 'ja', 'en')."
"maxLength": 30000,
"maxGraphemes": 3000,
"description": "A brief description or excerpt from the post."
},
"textContent": {
"type": "string",
"description": "Plaintext representation of the post content. Should not contain markdown or other formatting."
},
"updatedAt": {
"type": "string",
"format": "datetime",
"description": "Timestamp of the post's last edit."
},
"tags": {
"type": "array",
"items": {
"type": "string",
"maxLength": 1280,
"maxGraphemes": 128
},
"description": "Tags to categorize the post."
},
"path": {
"type": "string",
"description": "Combine with site URL to construct a canonical URL to the post."
},
"coverImage": {
"type": "blob",
"accept": ["image/*"],
"maxSize": 1000000,
"description": "Cover image. Less than 1MB."
},
"bskyPostRef": {
"type": "ref",
"ref": "com.atproto.repo.strongRef",
"description": "Strong reference to a Bluesky post."
},
"root": {
"type": "string",
"format": "at-uri",
"description": "AT-URI of the root message in a thread."
},
"parent": {
"type": "string",
"format": "at-uri",
"description": "AT-URI of the parent message being replied to."
},
"langs": {
"type": "array",
"maxLength": 3,
"items": {
"type": "string",
"format": "language"
},
"description": "Indicates human language of post content."
},
"translations": {
"type": "ref",
@@ -40,6 +98,19 @@
}
}
},
"markdown": {
"type": "object",
"description": "Markdown content format.",
"required": ["text"],
"properties": {
"text": {
"type": "string",
"maxLength": 1000000,
"maxGraphemes": 100000,
"description": "Markdown text content."
}
}
},
"translationMap": {
"type": "object",
"description": "Map of language codes to translations.",
@@ -54,8 +125,8 @@
"properties": {
"title": {
"type": "string",
"maxLength": 3000,
"maxGraphemes": 300
"maxLength": 5000,
"maxGraphemes": 500
},
"content": {
"type": "string",

View File

@@ -0,0 +1,99 @@
{
"$type": "com.atproto.lexicon.schema",
"lexicon": 1,
"id": "ai.syui.rse.admin",
"defs": {
"main": {
"type": "record",
"key": "literal:self",
"description": "RSE admin configuration - abilities, characters, systems and collections",
"record": {
"type": "object",
"required": ["ability", "createdAt", "updatedAt"],
"properties": {
"ability": {
"type": "array",
"description": "Ability/attribute definitions",
"items": {
"type": "object",
"required": ["id", "name", "kind"],
"properties": {
"id": { "type": "integer", "description": "Ability ID" },
"name": { "type": "string", "description": "Ability name (ai, quark, neutron, atom, sun)" },
"kind": { "type": "string", "description": "Attribute type (consciousness, matter)" },
"color": { "type": "string", "description": "Color code (e.g., #ffd700)" },
"level": { "type": "integer", "description": "Hierarchy level (0=fundamental)" },
"relation": { "type": "array", "items": { "type": "integer" }, "description": "Advantage IDs" },
"weakness": { "type": "array", "items": { "type": "integer" }, "description": "Weakness IDs" },
"multiplier": { "type": "integer", "description": "Damage multiplier percent (e.g., 150 = 1.5x)" },
"phantom": { "type": "boolean", "description": "Whether this ability is phantom/lost" }
}
}
},
"character": {
"type": "array",
"description": "Character definitions",
"items": {
"type": "object",
"required": ["id", "name", "ability", "mode"],
"properties": {
"id": { "type": "integer", "description": "Character ID" },
"name": { "type": "string", "description": "Character name" },
"ability": { "type": "integer", "description": "Ability ID reference" },
"mode": { "type": "integer", "description": "Character mode" }
}
}
},
"system": {
"type": "array",
"description": "System definitions",
"items": {
"type": "object",
"required": ["id", "name", "domain"],
"properties": {
"id": { "type": "integer", "description": "System ID" },
"name": { "type": "string", "description": "System name" },
"domain": { "type": "string", "description": "System domain (ability, unique, account, planet, origin)" }
}
}
},
"item": {
"type": "array",
"description": "Item definitions",
"items": {
"type": "object",
"required": ["id", "name", "text"],
"properties": {
"id": { "type": "integer", "description": "Item ID" },
"name": { "type": "string", "description": "Item name" },
"text": {
"type": "object",
"description": "Item description (localized)",
"properties": {
"en": { "type": "string", "description": "English text" },
"ja": { "type": "string", "description": "Japanese text" }
}
}
}
}
},
"collection": {
"type": "array",
"description": "ATProto collection definitions",
"items": {
"type": "object",
"required": ["id", "nsid", "name"],
"properties": {
"id": { "type": "integer", "description": "Collection ID" },
"nsid": { "type": "string", "description": "Namespaced identifier (e.g., ai.syui.card)" },
"name": { "type": "string", "description": "Collection short name" }
}
}
},
"createdAt": { "type": "string", "format": "datetime" },
"updatedAt": { "type": "string", "format": "datetime" }
}
}
}
}
}

View File

@@ -0,0 +1,48 @@
{
"$type": "com.atproto.lexicon.schema",
"lexicon": 1,
"id": "ai.syui.rse.user",
"defs": {
"main": {
"type": "record",
"key": "literal:self",
"description": "User character and item collection",
"record": {
"type": "object",
"required": ["character", "item", "createdAt", "updatedAt"],
"properties": {
"character": {
"type": "array",
"items": {
"type": "object",
"required": ["id", "cp", "rare", "cid", "unique"],
"properties": {
"id": { "type": "integer", "description": "Character type ID" },
"cp": { "type": "integer", "description": "Character power" },
"rare": { "type": "integer", "description": "Rarity level" },
"cid": { "type": "string", "description": "Unique character instance ID" },
"unique": { "type": "boolean", "description": "Unique character flag" }
}
}
},
"item": {
"type": "array",
"items": {
"type": "object",
"required": ["id", "cp", "rare", "cid", "unique"],
"properties": {
"id": { "type": "integer", "description": "Item type ID" },
"cp": { "type": "integer", "description": "Item power" },
"rare": { "type": "integer", "description": "Rarity level" },
"cid": { "type": "string", "description": "Unique item instance ID" },
"unique": { "type": "boolean", "description": "Unique item flag" }
}
}
},
"createdAt": { "type": "string", "format": "datetime" },
"updatedAt": { "type": "string", "format": "datetime" }
}
}
}
}
}

View File

@@ -0,0 +1,79 @@
{
"lexicon": 1,
"id": "site.standard.document",
"$type": "com.atproto.lexicon.schema",
"defs": {
"main": {
"type": "record",
"key": "tid",
"description": "A document record representing a published article, blog post, or other content. Documents can belong to a publication or exist independently.",
"record": {
"type": "object",
"required": ["site", "title", "publishedAt"],
"properties": {
"site": {
"type": "string",
"format": "uri",
"description": "Points to a publication record (at://) or a publication url (https://) for loose documents."
},
"title": {
"type": "string",
"maxLength": 5000,
"maxGraphemes": 500,
"description": "Title of the document."
},
"publishedAt": {
"type": "string",
"format": "datetime",
"description": "Timestamp of the document's publish time."
},
"content": {
"type": "union",
"closed": false,
"refs": [],
"description": "Open union used to define the record's content. Each entry must specify a $type."
},
"description": {
"type": "string",
"maxLength": 30000,
"maxGraphemes": 3000,
"description": "A brief description or excerpt from the document."
},
"textContent": {
"type": "string",
"description": "Plaintext representation of the document's contents. Should not contain markdown or other formatting."
},
"updatedAt": {
"type": "string",
"format": "datetime",
"description": "Timestamp of the document's last edit."
},
"tags": {
"type": "array",
"items": {
"type": "string",
"maxLength": 1280,
"maxGraphemes": 128
},
"description": "Array of strings used to tag or categorize the document."
},
"path": {
"type": "string",
"description": "Combine with site URL to construct a canonical URL to the document."
},
"coverImage": {
"type": "blob",
"accept": ["image/*"],
"maxSize": 1000000,
"description": "Cover image. Less than 1MB."
},
"bskyPostRef": {
"type": "ref",
"ref": "com.atproto.repo.strongRef",
"description": "Strong reference to a Bluesky post."
}
}
}
}
}
}

View File

@@ -0,0 +1,62 @@
{
"lexicon": 1,
"id": "site.standard.publication",
"$type": "com.atproto.lexicon.schema",
"defs": {
"main": {
"type": "record",
"key": "tid",
"description": "A publication record representing a blog, website, or content platform. Publications serve as containers for documents and define the overall branding and settings.",
"record": {
"type": "object",
"required": ["url", "name"],
"properties": {
"url": {
"type": "string",
"format": "uri",
"description": "Base publication url (ex: https://syui.ai). The canonical document URL is formed by combining this value with the document path."
},
"name": {
"type": "string",
"maxLength": 5000,
"maxGraphemes": 500,
"description": "Name of the publication."
},
"icon": {
"type": "blob",
"accept": ["image/*"],
"maxSize": 1000000,
"description": "Square image to identify the publication. Should be at least 256x256."
},
"description": {
"type": "string",
"maxLength": 30000,
"maxGraphemes": 3000,
"description": "Brief description of the publication."
},
"basicTheme": {
"type": "ref",
"ref": "site.standard.theme.basic",
"description": "Simplified publication theme for tools and apps to utilize when displaying content."
},
"preferences": {
"type": "ref",
"ref": "#preferences",
"description": "Object containing platform specific preferences."
}
}
}
},
"preferences": {
"type": "object",
"description": "Platform-specific preferences for the publication, including discovery and visibility settings.",
"properties": {
"showInDiscover": {
"type": "boolean",
"default": true,
"description": "Boolean which decides whether the publication should appear in discovery feeds."
}
}
}
}
}

View File

@@ -0,0 +1 @@
at://did:plc:vzsvtbtbnwn22xjqhcu3vd6y/site.standard.publication/syui.ai

View File

@@ -1,3 +1,5 @@
/.well-known/* /.well-known/:splat 200
/app/* /index.html 200
/oauth/cli /oauth/cli/index.html 200
/oauth/* /index.html 200
/* /index.html 200

View File

@@ -1 +0,0 @@
/Users/syui/ai/card/assets

BIN
public/card/0.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

BIN
public/card/1.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 90 KiB

BIN
public/card/10.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 152 KiB

BIN
public/card/100.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

BIN
public/card/101.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

BIN
public/card/102.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

BIN
public/card/103.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

BIN
public/card/104.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

BIN
public/card/105.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

BIN
public/card/106.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

BIN
public/card/107.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

BIN
public/card/108.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

BIN
public/card/109.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

BIN
public/card/11.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 122 KiB

BIN
public/card/110.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

BIN
public/card/111.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

BIN
public/card/112.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

BIN
public/card/113.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

BIN
public/card/114.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

BIN
public/card/115.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

BIN
public/card/116.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

BIN
public/card/117.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

BIN
public/card/118.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

BIN
public/card/119.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

BIN
public/card/12.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 86 KiB

BIN
public/card/120.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

BIN
public/card/121.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

BIN
public/card/122.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 124 KiB

BIN
public/card/123.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 96 KiB

BIN
public/card/124.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 95 KiB

BIN
public/card/125.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 103 KiB

BIN
public/card/126.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 80 KiB

BIN
public/card/127.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 221 KiB

BIN
public/card/128.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 166 KiB

BIN
public/card/129.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 112 KiB

BIN
public/card/13.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 74 KiB

BIN
public/card/130.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 205 KiB

BIN
public/card/131.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 87 KiB

BIN
public/card/132.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 58 KiB

BIN
public/card/133.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

BIN
public/card/134.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 49 KiB

BIN
public/card/14.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

BIN
public/card/15.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 126 KiB

BIN
public/card/16.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

BIN
public/card/17.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 87 KiB

BIN
public/card/18.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

BIN
public/card/19.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 62 KiB

BIN
public/card/2.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 59 KiB

BIN
public/card/20.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 60 KiB

BIN
public/card/200.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

BIN
public/card/201.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 130 KiB

BIN
public/card/21.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 60 KiB

BIN
public/card/22.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 78 KiB

BIN
public/card/23.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

BIN
public/card/24.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

BIN
public/card/25.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

BIN
public/card/26.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

BIN
public/card/27.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 137 KiB

BIN
public/card/28.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 97 KiB

BIN
public/card/29.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 98 KiB

BIN
public/card/3.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 116 KiB

BIN
public/card/30.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

BIN
public/card/300.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

BIN
public/card/301.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

BIN
public/card/31.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

BIN
public/card/32.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

BIN
public/card/33.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 108 KiB

BIN
public/card/34.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 53 KiB

BIN
public/card/35.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

BIN
public/card/36.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 88 KiB

BIN
public/card/37.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

BIN
public/card/38.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

BIN
public/card/39.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 93 KiB

BIN
public/card/4.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 119 KiB

BIN
public/card/40.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

BIN
public/card/41.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

BIN
public/card/42.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

BIN
public/card/43.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 78 KiB

BIN
public/card/44.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 65 KiB

BIN
public/card/45.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 KiB

Some files were not shown because too many files have changed in this diff Show More