| """Central configuration: env, story styles, difficulty levels, file filters, palette. |
| |
| StoryCode reads a non-coder's project and explains it as a story. The values here |
| are deliberately plain-language: labels a non-technical person sees should never |
| contain jargon unless we explain it. |
| """ |
| from __future__ import annotations |
|
|
| import os |
| from dataclasses import dataclass |
|
|
| from dotenv import load_dotenv |
|
|
| load_dotenv() |
|
|
| |
| |
| MODAL_ENDPOINT_URL = os.getenv("MODAL_ENDPOINT_URL", "").rstrip("/") |
| MODAL_API_KEY = os.getenv("MODAL_API_KEY", "") |
| MODEL_ID = os.getenv("MODEL_ID", "openbmb/MiniCPM4.1-8B") |
| DB_PATH = os.getenv("STORYCODE_DB_PATH", "data/storycode.db") |
| USE_ZEROGPU_FALLBACK = os.getenv("USE_ZEROGPU_FALLBACK", "0") == "1" |
|
|
| |
| LLM_TIMEOUT_S = int(os.getenv("LLM_TIMEOUT_S", "90")) |
| MAX_FILES = int(os.getenv("STORYCODE_MAX_FILES", "60")) |
| MAX_FILE_BYTES = int(os.getenv("STORYCODE_MAX_FILE_BYTES", str(200_000))) |
| MAX_TOTAL_BYTES = int(os.getenv("STORYCODE_MAX_TOTAL_BYTES", str(5_000_000))) |
|
|
| |
| |
| LANG_BY_EXT: dict[str, str] = { |
| ".py": "python", |
| ".js": "javascript", ".jsx": "javascript", |
| ".ts": "typescript", ".tsx": "typescript", ".mjs": "javascript", ".cjs": "javascript", |
| ".html": "html", ".htm": "html", |
| ".css": "css", ".scss": "css", |
| ".json": "json", |
| ".yaml": "yaml", ".yml": "yaml", |
| ".md": "markdown", |
| } |
|
|
| |
| IGNORE_DIRS: frozenset[str] = frozenset({ |
| "node_modules", ".git", ".svn", "__pycache__", ".venv", "venv", "env", |
| "dist", "build", ".next", ".nuxt", "out", "target", ".idea", ".vscode", |
| "coverage", ".pytest_cache", ".mypy_cache", "site-packages", ".cache", |
| "vendor", "bower_components", ".gradio", |
| }) |
| IGNORE_FILES: frozenset[str] = frozenset({ |
| "package-lock.json", "yarn.lock", "pnpm-lock.yaml", "poetry.lock", |
| ".ds_store", "thumbs.db", |
| }) |
| |
| BINARY_EXTS: frozenset[str] = frozenset({ |
| ".png", ".jpg", ".jpeg", ".gif", ".webp", ".ico", ".svg", ".pdf", ".zip", |
| ".gz", ".tar", ".mp3", ".mp4", ".wav", ".woff", ".woff2", ".ttf", ".otf", |
| ".pyc", ".so", ".dll", ".exe", ".bin", ".db", ".sqlite", ".lock", |
| }) |
|
|
| |
| DEP_MANIFESTS: frozenset[str] = frozenset({ |
| "requirements.txt", "package.json", "pyproject.toml", "pipfile", |
| }) |
|
|
|
|
| |
| |
| |
| @dataclass(frozen=True) |
| class Role: |
| key: str |
| label: str |
| color: str |
|
|
|
|
| ROLES: tuple[Role, ...] = ( |
| Role("entry", "Front door", "#4b5bd6"), |
| Role("frontend", "What you see", "#3a7ca5"), |
| Role("backend", "The engine", "#6b4fb0"), |
| Role("data", "Memory", "#3f9b6b"), |
| Role("config", "Settings", "#8a7a66"), |
| Role("test", "Safety checks", "#9aa0a6"), |
| Role("util", "Helpers", "#b0793a"), |
| Role("other", "Other", "#9aa0a6"), |
| ) |
| ROLE_KEYS: tuple[str, ...] = tuple(r.key for r in ROLES) |
| ROLE_LABEL = {r.key: r.label for r in ROLES} |
| ROLE_COLOR = {r.key: r.color for r in ROLES} |
|
|
| |
| ENTRY_NAMES: frozenset[str] = frozenset({ |
| "app.py", "main.py", "manage.py", "server.py", "run.py", "wsgi.py", "asgi.py", |
| "index.js", "index.ts", "index.jsx", "index.tsx", "main.js", "main.ts", |
| "index.html", "app.js", "app.ts", |
| "train.py", "play.py", "demo.py", "infer.py", "evaluate.py", "test.py", |
| }) |
| CONFIG_HINTS: tuple[str, ...] = ( |
| "config", "settings", ".env", "requirements", "package.json", "pyproject", |
| "dockerfile", "makefile", ".yaml", ".yml", ".toml", ".ini", |
| ) |
| DATA_HINTS: tuple[str, ...] = ( |
| "db", "database", "model", "schema", "store", "embed", "vector", |
| "index", "ingest", "migration", "seed", |
| ) |
| TEST_HINTS: tuple[str, ...] = ("test", "spec", "__tests__") |
| UTIL_HINTS: tuple[str, ...] = ("util", "helper", "common", "lib", "shared", "tools") |
|
|
|
|
| |
| |
| |
| |
| @dataclass(frozen=True) |
| class Style: |
| key: str |
| label: str |
| emoji: str |
| voice: str |
|
|
|
|
| STYLES: tuple[Style, ...] = ( |
| Style("plain", "Simple Walkthrough", "π", |
| "Explain plainly and warmly, step by step, no metaphors, no jargon. " |
| "Like a patient friend who read the whole project for them."), |
| Style("kids", "Kids Book", "π§Έ", |
| "Tell it like a children's picture book. Each file is a friendly " |
| "character with a job. Keep sentences short and gentle."), |
| Style("thriller", "Thriller", "π΅οΈ", |
| "Tell it like a suspense thriller β the request arrives, tension " |
| "builds as it moves through the code β but every claim must stay true."), |
| Style("news", "News Report", "π°", |
| "Report it like a breaking-news anchor: who did what, in what order, " |
| "with punchy headlines. Accurate, never sensational about facts."), |
| Style("recipe", "Recipe", "π³", |
| "Explain it like a cooking recipe: ingredients (the pieces) and steps " |
| "(the flow), in order."), |
| ) |
| STYLE_KEYS: tuple[str, ...] = tuple(s.key for s in STYLES) |
| STYLE_BY_KEY = {s.key: s for s in STYLES} |
| DEFAULT_STYLE = "plain" |
|
|
|
|
| @dataclass(frozen=True) |
| class Difficulty: |
| key: str |
| label: str |
| voice: str |
|
|
|
|
| DIFFICULTIES: tuple[Difficulty, ...] = ( |
| Difficulty("kid", "Explain like I'm 5", |
| "Use no technical terms at all. Everyday words a child knows."), |
| Difficulty("teen", "Teenager", |
| "Light technical terms are fine if you explain each one in the " |
| "same sentence."), |
| Difficulty("adult", "Adult / curious beginner", |
| "Full clarity. Technical terms are okay but define them the first " |
| "time they appear."), |
| ) |
| DIFFICULTY_KEYS: tuple[str, ...] = tuple(d.key for d in DIFFICULTIES) |
| DIFFICULTY_BY_KEY = {d.key: d for d in DIFFICULTIES} |
| DEFAULT_DIFFICULTY = "teen" |
|
|
|
|
| |
| SOURCE_HINTS: tuple[str, ...] = ( |
| "Claude / Claude Code", "Cursor", "ChatGPT", "Bolt / v0 / Lovable", |
| "A ZIP someone sent me", "Not sure", |
| ) |
|
|
|
|
| |
| PALETTE = { |
| "paper": "#f7f4ef", |
| "ink": "#1d2430", |
| "accent": "#4b5bd6", |
| "safe": "#3f9b6b", |
| "careful": "#d8932f", |
| "danger": "#c2503f", |
| "muted": "#6b7280", |
| "line": "#e7e3da", |
| } |
|
|
| |
| SAFE = "safe" |
| CAREFUL = "careful" |
| DANGER = "danger" |
| SAFETY_COLOR = {SAFE: PALETTE["safe"], CAREFUL: PALETTE["careful"], DANGER: PALETTE["danger"]} |
| SAFETY_EMOJI = {SAFE: "π’", CAREFUL: "π ", DANGER: "π΄"} |
| SAFETY_LABEL = {SAFE: "Safe to change", CAREFUL: "Change carefully", DANGER: "Don't touch"} |
|
|