chore: init main branch

This commit is contained in:
954801926@qq.com committed 2026-07-29 23:36:41 +08:00
commit 549c8b5b13
231 files changed
+27286

No files matched your search

+57
View File
@@ -0,0 +1,57 @@
#!/usr/bin/env node
/**
* Fetch the latest model reference data from models.dev and write a compact
* snapshot to src/lib/models-dev.json.
*
* Run manually (`npm run fetch-models-dev`) or automatically before builds
* (`prebuild`). Requires network access to models.dev.
*/
import { writeFile, mkdir } from "node:fs/promises";
import { dirname, join } from "node:path";
import { fileURLToPath } from "node:url";
const SOURCE_URL = "https://models.dev/models.json";
const ROOT = join(dirname(fileURLToPath(import.meta.url)), "..");
const OUTPUT = join(ROOT, "src", "lib", "models-dev.json");
const res = await fetch(SOURCE_URL, {
headers: { Accept: "application/json" },
signal: AbortSignal.timeout(30_000),
});
if (!res.ok) {
throw new Error(`GET ${SOURCE_URL} returned HTTP ${res.status}`);
}
/** @type {Record<string, any>} */
const raw = await res.json();
// Keep only the fields the app needs; the raw file carries benchmarks,
// descriptions, etc. that would bloat the bundle.
const snapshot = {};
for (const [key, m] of Object.entries(raw)) {
if (!m || typeof m !== "object") continue;
const entry = {};
if (typeof m.name === "string") entry.name = m.name;
// context 0 means "not applicable" (image/audio models) — treat as missing
// so callers fall back to regex defaults instead of storing 0.
if (typeof m.limit?.context === "number" && m.limit.context > 0) {
entry.context = m.limit.context;
}
if (m.reasoning === true) entry.reasoning = true;
if (m.tool_call === true) entry.tool_call = true;
if (m.structured_output === true) entry.structured_output = true;
const input = m.modalities?.input;
if (Array.isArray(input)) {
if (input.includes("image")) entry.image = true;
if (input.includes("video")) entry.video = true;
}
snapshot[key] = entry;
}
await mkdir(dirname(OUTPUT), { recursive: true });
await writeFile(OUTPUT, JSON.stringify(snapshot, null, 2) + "\n", "utf8");
console.log(
`models-dev snapshot: ${Object.keys(snapshot).length} models -> ${OUTPUT}`,
);
+110
View File
@@ -0,0 +1,110 @@
from PIL import Image, ImageDraw, ImageFont
import os
ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
OUT_DIR = os.path.join(ROOT, "src-tauri", "icons")
SVG_PATH = os.path.join(ROOT, "public", "kimi.svg")
os.makedirs(OUT_DIR, exist_ok=True)
os.makedirs(os.path.dirname(SVG_PATH), exist_ok=True)
# Brand colors: blue -> purple diagonal gradient
C_TOP = (59, 130, 246) # #3b82f6
C_BOT = (139, 92, 246) # #8b5cf6
DOT = (255, 255, 255, 230)
def lerp(a, b, t):
return int(a + (b - a) * t)
def rounded_mask(size, radius):
m = Image.new("L", (size, size), 0)
d = ImageDraw.Draw(m)
d.rounded_rectangle((0, 0, size - 1, size - 1), radius=radius, fill=255)
return m
def draw_icon(size):
# Render at 4x then downscale for smooth edges
S = size * 4
img = Image.new("RGBA", (S, S), (0, 0, 0, 0))
# Diagonal gradient background
grad = Image.new("RGBA", (S, S))
gd = ImageDraw.Draw(grad)
for y in range(S):
for x in range(0, S, 4): # step to speed up; visually identical after downscale
t = (x + y) / (2 * S)
gd.line([(x, y), (min(x + 3, S - 1), y)],
fill=(lerp(C_TOP[0], C_BOT[0], t),
lerp(C_TOP[1], C_BOT[1], t),
lerp(C_TOP[2], C_BOT[2], t), 255))
img.paste(grad, (0, 0), rounded_mask(S, int(S * 0.22)))
draw = ImageDraw.Draw(img)
# Bold "K"
font_size = int(S * 0.52)
font = None
for name in ("segoeuib.ttf", "arialbd.ttf", "DejaVuSans-Bold.ttf"):
try:
font = ImageFont.truetype(name, font_size)
break
except Exception:
continue
if font is None:
font = ImageFont.load_default()
bbox = draw.textbbox((0, 0), "K", font=font)
tw, th = bbox[2] - bbox[0], bbox[3] - bbox[1]
# Optical center: shift slightly left/down to balance the toggle dot
x = (S - tw) // 2 - bbox[0] - int(S * 0.02)
y = (S - th) // 2 - bbox[1] + int(S * 0.02)
draw.text((x, y), "K", font=font, fill=(255, 255, 255, 255))
# Toggle dot (top-right) — the "switch" cue
cx, cy = int(S * 0.74), int(S * 0.26)
r = int(S * 0.075)
ring = int(S * 0.018)
draw.ellipse((cx - r - ring, cy - r - ring, cx + r + ring, cy + r + ring),
fill=(255, 255, 255, 90))
draw.ellipse((cx - r, cy - r, cx + r, cy + r), fill=DOT)
return img.resize((size, size), Image.LANCZOS)
# --- Tauri bundle icons ---
for s in (32, 128, 256):
img = draw_icon(s)
if s == 32:
img.save(os.path.join(OUT_DIR, "32x32.png"))
elif s == 128:
img.save(os.path.join(OUT_DIR, "128x128.png"))
draw_icon(256).save(os.path.join(OUT_DIR, "128x128@2x.png"))
else:
img.save(os.path.join(OUT_DIR, "icon.png"))
# Multi-size ICO
ico_sizes = [(32, 32), (64, 64), (128, 128), (256, 256)]
imgs = [draw_icon(s[0]) for s in ico_sizes]
imgs[0].save(os.path.join(OUT_DIR, "icon.ico"), format="ICO", sizes=ico_sizes)
# --- Web favicon / app SVG ---
svg = '''<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100">
<defs>
<linearGradient id="g" x1="0" y1="0" x2="1" y2="1">
<stop offset="0%" stop-color="#3b82f6"/>
<stop offset="100%" stop-color="#8b5cf6"/>
</linearGradient>
</defs>
<rect width="100" height="100" rx="22" fill="url(#g)"/>
<text x="48" y="70" text-anchor="middle" font-size="52" font-weight="700"
fill="white" font-family="system-ui, -apple-system, 'Segoe UI', sans-serif">K</text>
<circle cx="74" cy="26" r="9" fill="white" opacity="0.35"/>
<circle cx="74" cy="26" r="6.5" fill="white" opacity="0.92"/>
</svg>'''
with open(SVG_PATH, "w", encoding="utf-8") as f:
f.write(svg)
print("Icons generated.")