Compare commits
105 Commits
12797dde7f
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
32168f14d0
|
|||
|
6d3e233ee2
|
|||
|
a46c6d89f0
|
|||
|
9b0816138c
|
|||
|
09780fab1d
|
|||
|
0c38b05ff7
|
|||
|
21ff32864c
|
|||
|
c5aa286b89
|
|||
|
c78f6782a8
|
|||
|
4f336de805
|
|||
|
928665217d
|
|||
|
9686e26cd6
|
|||
|
a875e39192
|
|||
|
f7cce79f70
|
|||
|
8cba6b1dea
|
|||
|
c918f3f437
|
|||
|
0eaca12473
|
|||
|
346b1fdd87
|
|||
|
5d7f6853dd
|
|||
|
c4393582ff
|
|||
|
fbe177ba99
|
|||
|
3e07b66cc7
|
|||
|
b06dec0926
|
|||
|
ea1eecde64
|
|||
|
24c5567f21
|
|||
|
4075f6de45
|
|||
|
073d69708d
|
|||
|
799f7145f8
|
|||
|
5e84ba7b1d
|
|||
|
1d33b50775
|
|||
|
7b70a62aa3
|
|||
|
86e57731b6
|
|||
|
81ae459cde
|
|||
|
8446bd4da6
|
|||
|
f5fac72002
|
|||
|
612a48ab39
|
|||
|
e2c908d4e8
|
|||
|
7b30cc6706
|
|||
|
13e745bf6d
|
|||
|
c053143db3
|
|||
|
cfa36633e1
|
|||
|
cbc52f554a
|
|||
|
2f6bab165b
|
|||
|
7713fa1729
|
|||
|
2500c4e14a
|
|||
|
0d61b725a8
|
|||
|
2d182ff412
|
|||
|
6850c11f06
|
|||
|
415ae30d3b
|
|||
|
af22446c47
|
|||
|
570dce37e1
|
|||
|
7c8fa2e5c4
|
|||
|
ddbd0c761f
|
|||
|
6e8dea6252
|
|||
|
dc5218b1ad
|
|||
|
ba15759595
|
|||
|
99a9833df8
|
|||
|
f75c0373ed
|
|||
|
53404e187a
|
|||
|
387ed4cbc4
|
|||
|
e823628090
|
|||
|
058965f58e
|
|||
|
22f34377ec
|
|||
|
12422d275c
|
|||
|
fa0b68d622
|
|||
|
5870541b96
|
|||
|
e5cccaca39
|
|||
|
c124492561
|
|||
|
279f952c99
|
|||
|
b1fdd211b4
|
|||
|
f60369d2c8
|
|||
|
c2044c5bd8
|
|||
|
ca6bb5319c
|
|||
|
cefe7981ee
|
|||
|
6dd9d2466e
|
|||
|
cba0228e70
|
|||
|
7021036a5c
|
|||
|
6d9f61620d
|
|||
|
3af7894995
|
|||
|
84161e1cad
|
|||
|
bd26f817d9
|
|||
|
e0fc85fa1c
|
|||
|
57aeaed91f
|
|||
|
2f19984f0c
|
|||
|
a873e5cbf1
|
|||
|
d1fba083b4
|
|||
|
b5dd740541
|
|||
|
5824c42e8b
|
|||
|
63b24c7e1e
|
|||
|
9519c8c477
|
|||
|
da45b31029
|
|||
|
653814ee70
|
|||
|
09483cfb7e
|
|||
|
586e16cc54
|
|||
|
80cd0888f3
|
|||
|
6b3be20a5f
|
|||
|
48e5d3bc82
|
|||
|
06ccae58a2
|
|||
|
ff09594801
|
|||
|
06ca713dc8
|
|||
|
a2dda25ab6
|
|||
|
d91c37d73a
|
|||
|
a5ef15e4ee
|
|||
|
968ea019a6
|
|||
|
b2f36073ec
|
@@ -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)
|
||||
|
||||
6
.github/workflows/cf-pages.yml
vendored
@@ -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
@@ -8,3 +8,4 @@ package-lock.json
|
||||
Cargo.lock
|
||||
.env
|
||||
.mcp.json
|
||||
bot
|
||||
|
||||
@@ -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
@@ -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
|
||||
48
lexicons/ai.syui.at.link.json
Normal 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."
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
71
lexicons/ai.syui.card.admin.json
Normal 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" }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
52
lexicons/ai.syui.card.old.json
Normal 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"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
34
lexicons/ai.syui.card.user.json
Normal 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" }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
55
lexicons/ai.syui.gpt.core.json
Normal 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."
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
50
lexicons/ai.syui.gpt.memory.json
Normal 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."
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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",
|
||||
|
||||
99
lexicons/ai.syui.rse.admin.json
Normal 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" }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
48
lexicons/ai.syui.rse.user.json
Normal 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" }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
79
lexicons/site.standard.document.json
Normal 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."
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
62
lexicons/site.standard.publication.json
Normal 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."
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
1
public/.well-known/site.standard.publication
Normal file
@@ -0,0 +1 @@
|
||||
at://did:plc:vzsvtbtbnwn22xjqhcu3vd6y/site.standard.publication/syui.ai
|
||||
@@ -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
|
||||
|
||||
BIN
public/card/0.webp
Normal file
|
After Width: | Height: | Size: 27 KiB |
BIN
public/card/1.webp
Normal file
|
After Width: | Height: | Size: 90 KiB |
BIN
public/card/10.webp
Normal file
|
After Width: | Height: | Size: 152 KiB |
BIN
public/card/100.webp
Normal file
|
After Width: | Height: | Size: 32 KiB |
BIN
public/card/101.webp
Normal file
|
After Width: | Height: | Size: 37 KiB |
BIN
public/card/102.webp
Normal file
|
After Width: | Height: | Size: 40 KiB |
BIN
public/card/103.webp
Normal file
|
After Width: | Height: | Size: 38 KiB |
BIN
public/card/104.webp
Normal file
|
After Width: | Height: | Size: 25 KiB |
BIN
public/card/105.webp
Normal file
|
After Width: | Height: | Size: 40 KiB |
BIN
public/card/106.webp
Normal file
|
After Width: | Height: | Size: 41 KiB |
BIN
public/card/107.webp
Normal file
|
After Width: | Height: | Size: 40 KiB |
BIN
public/card/108.webp
Normal file
|
After Width: | Height: | Size: 33 KiB |
BIN
public/card/109.webp
Normal file
|
After Width: | Height: | Size: 30 KiB |
BIN
public/card/11.webp
Normal file
|
After Width: | Height: | Size: 122 KiB |
BIN
public/card/110.webp
Normal file
|
After Width: | Height: | Size: 41 KiB |
BIN
public/card/111.webp
Normal file
|
After Width: | Height: | Size: 38 KiB |
BIN
public/card/112.webp
Normal file
|
After Width: | Height: | Size: 38 KiB |
BIN
public/card/113.webp
Normal file
|
After Width: | Height: | Size: 37 KiB |
BIN
public/card/114.webp
Normal file
|
After Width: | Height: | Size: 34 KiB |
BIN
public/card/115.webp
Normal file
|
After Width: | Height: | Size: 34 KiB |
BIN
public/card/116.webp
Normal file
|
After Width: | Height: | Size: 31 KiB |
BIN
public/card/117.webp
Normal file
|
After Width: | Height: | Size: 33 KiB |
BIN
public/card/118.webp
Normal file
|
After Width: | Height: | Size: 36 KiB |
BIN
public/card/119.webp
Normal file
|
After Width: | Height: | Size: 41 KiB |
BIN
public/card/12.webp
Normal file
|
After Width: | Height: | Size: 86 KiB |
BIN
public/card/120.webp
Normal file
|
After Width: | Height: | Size: 32 KiB |
BIN
public/card/121.webp
Normal file
|
After Width: | Height: | Size: 34 KiB |
BIN
public/card/122.webp
Normal file
|
After Width: | Height: | Size: 124 KiB |
BIN
public/card/123.webp
Normal file
|
After Width: | Height: | Size: 96 KiB |
BIN
public/card/124.webp
Normal file
|
After Width: | Height: | Size: 95 KiB |
BIN
public/card/125.webp
Normal file
|
After Width: | Height: | Size: 103 KiB |
BIN
public/card/126.webp
Normal file
|
After Width: | Height: | Size: 80 KiB |
BIN
public/card/127.webp
Normal file
|
After Width: | Height: | Size: 221 KiB |
BIN
public/card/128.webp
Normal file
|
After Width: | Height: | Size: 166 KiB |
BIN
public/card/129.webp
Normal file
|
After Width: | Height: | Size: 112 KiB |
BIN
public/card/13.webp
Normal file
|
After Width: | Height: | Size: 74 KiB |
BIN
public/card/130.webp
Normal file
|
After Width: | Height: | Size: 205 KiB |
BIN
public/card/131.webp
Normal file
|
After Width: | Height: | Size: 87 KiB |
BIN
public/card/132.webp
Normal file
|
After Width: | Height: | Size: 58 KiB |
BIN
public/card/133.webp
Normal file
|
After Width: | Height: | Size: 56 KiB |
BIN
public/card/134.webp
Normal file
|
After Width: | Height: | Size: 49 KiB |
BIN
public/card/14.webp
Normal file
|
After Width: | Height: | Size: 41 KiB |
BIN
public/card/15.webp
Normal file
|
After Width: | Height: | Size: 126 KiB |
BIN
public/card/16.webp
Normal file
|
After Width: | Height: | Size: 44 KiB |
BIN
public/card/17.webp
Normal file
|
After Width: | Height: | Size: 87 KiB |
BIN
public/card/18.webp
Normal file
|
After Width: | Height: | Size: 46 KiB |
BIN
public/card/19.webp
Normal file
|
After Width: | Height: | Size: 62 KiB |
BIN
public/card/2.webp
Normal file
|
After Width: | Height: | Size: 59 KiB |
BIN
public/card/20.webp
Normal file
|
After Width: | Height: | Size: 60 KiB |
BIN
public/card/200.webp
Normal file
|
After Width: | Height: | Size: 32 KiB |
BIN
public/card/201.webp
Normal file
|
After Width: | Height: | Size: 130 KiB |
BIN
public/card/21.webp
Normal file
|
After Width: | Height: | Size: 60 KiB |
BIN
public/card/22.webp
Normal file
|
After Width: | Height: | Size: 78 KiB |
BIN
public/card/23.webp
Normal file
|
After Width: | Height: | Size: 35 KiB |
BIN
public/card/24.webp
Normal file
|
After Width: | Height: | Size: 33 KiB |
BIN
public/card/25.webp
Normal file
|
After Width: | Height: | Size: 34 KiB |
BIN
public/card/26.webp
Normal file
|
After Width: | Height: | Size: 35 KiB |
BIN
public/card/27.webp
Normal file
|
After Width: | Height: | Size: 137 KiB |
BIN
public/card/28.webp
Normal file
|
After Width: | Height: | Size: 97 KiB |
BIN
public/card/29.webp
Normal file
|
After Width: | Height: | Size: 98 KiB |
BIN
public/card/3.webp
Normal file
|
After Width: | Height: | Size: 116 KiB |
BIN
public/card/30.webp
Normal file
|
After Width: | Height: | Size: 32 KiB |
BIN
public/card/300.webp
Normal file
|
After Width: | Height: | Size: 32 KiB |
BIN
public/card/301.webp
Normal file
|
After Width: | Height: | Size: 39 KiB |
BIN
public/card/31.webp
Normal file
|
After Width: | Height: | Size: 25 KiB |
BIN
public/card/32.webp
Normal file
|
After Width: | Height: | Size: 33 KiB |
BIN
public/card/33.webp
Normal file
|
After Width: | Height: | Size: 108 KiB |
BIN
public/card/34.webp
Normal file
|
After Width: | Height: | Size: 53 KiB |
BIN
public/card/35.webp
Normal file
|
After Width: | Height: | Size: 37 KiB |
BIN
public/card/36.webp
Normal file
|
After Width: | Height: | Size: 88 KiB |
BIN
public/card/37.webp
Normal file
|
After Width: | Height: | Size: 32 KiB |
BIN
public/card/38.webp
Normal file
|
After Width: | Height: | Size: 34 KiB |
BIN
public/card/39.webp
Normal file
|
After Width: | Height: | Size: 93 KiB |
BIN
public/card/4.webp
Normal file
|
After Width: | Height: | Size: 119 KiB |
BIN
public/card/40.webp
Normal file
|
After Width: | Height: | Size: 43 KiB |
BIN
public/card/41.webp
Normal file
|
After Width: | Height: | Size: 43 KiB |
BIN
public/card/42.webp
Normal file
|
After Width: | Height: | Size: 32 KiB |
BIN
public/card/43.webp
Normal file
|
After Width: | Height: | Size: 78 KiB |
BIN
public/card/44.webp
Normal file
|
After Width: | Height: | Size: 65 KiB |
BIN
public/card/45.webp
Normal file
|
After Width: | Height: | Size: 68 KiB |
BIN
public/card/46.webp
Normal file
|
After Width: | Height: | Size: 98 KiB |