Compare commits
4
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
33612bf6d4 | ||
|
|
ffecd62b83 | ||
|
|
c77dcf5890 | ||
|
|
9c80f2c857 |
No files matched your search
@@ -3,6 +3,22 @@
|
|||||||
All notable changes to CodeCraft. Versions follow [Semantic Versioning](https://semver.org/);
|
All notable changes to CodeCraft. Versions follow [Semantic Versioning](https://semver.org/);
|
||||||
until 1.0, minor versions may contain breaking changes.
|
until 1.0, minor versions may contain breaking changes.
|
||||||
|
|
||||||
|
## [v202611-00022] – 2026-11-11
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- **Theme "Tintenblau"** (dark: ink blue with cream text; light: paper with dark ink), from the
|
||||||
|
Tintenblau desktop theme; also in the terminal client.
|
||||||
|
|
||||||
|
## [v202611-00021] – 2026-11-11
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- **Commits of agents name the model:** every `git commit` an agent makes gets the trailer
|
||||||
|
`Co-Authored-By: <model> (<provider>) via CodeCraft` (a git wrapper in the agent shell, also in
|
||||||
|
the sandbox; your own commits stay unchanged). The text is set under *Settings → General*
|
||||||
|
(`server.commit_trailer`, `{model}`, `{provider}`, `{model_id}`; empty = off).
|
||||||
|
|
||||||
## [v202611-00020] – 2026-11-11
|
## [v202611-00020] – 2026-11-11
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|||||||
@@ -96,6 +96,8 @@ COPY --from=cli /out /app/cli
|
|||||||
# Used with a mounted /var/run/docker.sock (see docs/MOVING.md, --docker-sock).
|
# Used with a mounted /var/run/docker.sock (see docs/MOVING.md, --docker-sock).
|
||||||
COPY --from=dockercli /docker /usr/local/bin/docker
|
COPY --from=dockercli /docker /usr/local/bin/docker
|
||||||
COPY server/ghidra /app/ghidra
|
COPY server/ghidra /app/ghidra
|
||||||
|
# git for agent shells: commits get the co-author trailer of the model (server.commit_trailer).
|
||||||
|
COPY --chmod=755 docker/git-coauthor /usr/local/lib/codecraft/bin/git
|
||||||
COPY server/resources /app/resources
|
COPY server/resources /app/resources
|
||||||
# The agent's home lives on the data volume: tools it installs in user space
|
# The agent's home lives on the data volume: tools it installs in user space
|
||||||
# (pip --user, pipx, rustup, npm -g, downloaded binaries) survive rebuilds.
|
# (pip --user, pipx, rustup, npm -g, downloaded binaries) survive rebuilds.
|
||||||
|
|||||||
@@ -11,7 +11,7 @@
|
|||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p align="center">
|
<p align="center">
|
||||||
<img alt="Version v202611-00020" src="https://img.shields.io/badge/version-v202611--00020-d69e2e?style=flat-square">
|
<img alt="Version v202611-00022" src="https://img.shields.io/badge/version-v202611--00022-d69e2e?style=flat-square">
|
||||||
<img alt="License: MIT" src="https://img.shields.io/badge/license-MIT-689d6a?style=flat-square">
|
<img alt="License: MIT" src="https://img.shields.io/badge/license-MIT-689d6a?style=flat-square">
|
||||||
<img alt="Rust backend" src="https://img.shields.io/badge/backend-Rust-b7410e?style=flat-square&logo=rust">
|
<img alt="Rust backend" src="https://img.shields.io/badge/backend-Rust-b7410e?style=flat-square&logo=rust">
|
||||||
<img alt="React PWA" src="https://img.shields.io/badge/frontend-React%20PWA-2d81ff?style=flat-square&logo=react">
|
<img alt="React PWA" src="https://img.shields.io/badge/frontend-React%20PWA-2d81ff?style=flat-square&logo=react">
|
||||||
@@ -296,6 +296,7 @@ in turn are based on:
|
|||||||
| Coffee | Dark, Latte | [megabyte0x/omarchy-coffee-theme](https://github.com/megabyte0x/omarchy-coffee-theme) | MIT |
|
| Coffee | Dark, Latte | [megabyte0x/omarchy-coffee-theme](https://github.com/megabyte0x/omarchy-coffee-theme) | MIT |
|
||||||
| Solarized | Dark, Light | [Solarized by Ethan Schoonover](https://ethanschoonover.com/solarized/) | MIT |
|
| Solarized | Dark, Light | [Solarized by Ethan Schoonover](https://ethanschoonover.com/solarized/) | MIT |
|
||||||
| Tokyo Night | Night, Day | [folke/tokyonight.nvim](https://github.com/folke/tokyonight.nvim) | Apache-2.0 |
|
| Tokyo Night | Night, Day | [folke/tokyonight.nvim](https://github.com/folke/tokyonight.nvim) | Apache-2.0 |
|
||||||
|
| Tintenblau | Dark, Light | original (zink/tintenblau desktop theme) | private |
|
||||||
|
|
||||||
Further standard themes (palettes re-created from the published colour specifications):
|
Further standard themes (palettes re-created from the published colour specifications):
|
||||||
|
|
||||||
|
|||||||
@@ -701,6 +701,60 @@
|
|||||||
"syntax-type": "#b58900",
|
"syntax-type": "#b58900",
|
||||||
"syntax-variable": "#586e75"
|
"syntax-variable": "#586e75"
|
||||||
},
|
},
|
||||||
|
"tintenblau-dark": {
|
||||||
|
"accent": "#7aa0cc",
|
||||||
|
"accent2": "#d4b46a",
|
||||||
|
"aqua": "#96b4bc",
|
||||||
|
"bg0": "#232a44",
|
||||||
|
"bg1": "#2b3352",
|
||||||
|
"bg2": "#353e60",
|
||||||
|
"bg3": "#4a5478",
|
||||||
|
"blue": "#7aa0cc",
|
||||||
|
"fg0": "#f5efe3",
|
||||||
|
"fg1": "#eae4d6",
|
||||||
|
"fg2": "#d6d0c2",
|
||||||
|
"fg3": "#bdcfd4",
|
||||||
|
"green": "#96b4bc",
|
||||||
|
"muted": "#8e96b4",
|
||||||
|
"on-accent": "#232a44",
|
||||||
|
"purple": "#c896b2",
|
||||||
|
"red": "#d47a6a",
|
||||||
|
"syntax-comment": "#8e96b4",
|
||||||
|
"syntax-function": "#7aa0cc",
|
||||||
|
"syntax-keyword": "#c896b2",
|
||||||
|
"syntax-meta": "#baa0c4",
|
||||||
|
"syntax-number": "#96b4bc",
|
||||||
|
"syntax-string": "#d47a6a",
|
||||||
|
"syntax-type": "#d4b46a",
|
||||||
|
"syntax-variable": "#eae4d6"
|
||||||
|
},
|
||||||
|
"tintenblau-light": {
|
||||||
|
"accent": "#2a3f6f",
|
||||||
|
"accent2": "#6a4a2c",
|
||||||
|
"aqua": "#3a5a6a",
|
||||||
|
"bg0": "#f0ead9",
|
||||||
|
"bg1": "#e6dfd0",
|
||||||
|
"bg2": "#dbd3c1",
|
||||||
|
"bg3": "#c9c0ac",
|
||||||
|
"blue": "#3a5a8a",
|
||||||
|
"fg0": "#0f1628",
|
||||||
|
"fg1": "#1e2a4a",
|
||||||
|
"fg2": "#3a3f5a",
|
||||||
|
"fg3": "#5e5a6a",
|
||||||
|
"green": "#4a5a3a",
|
||||||
|
"muted": "#8a8694",
|
||||||
|
"on-accent": "#f0ead9",
|
||||||
|
"purple": "#5a4a72",
|
||||||
|
"red": "#8a3028",
|
||||||
|
"syntax-comment": "#8a8694",
|
||||||
|
"syntax-function": "#3a5a6a",
|
||||||
|
"syntax-keyword": "#2a3f6f",
|
||||||
|
"syntax-meta": "#4a5a3a",
|
||||||
|
"syntax-number": "#5a4a72",
|
||||||
|
"syntax-string": "#8a3028",
|
||||||
|
"syntax-type": "#6a4a2c",
|
||||||
|
"syntax-variable": "#1e2a4a"
|
||||||
|
},
|
||||||
"tokyonight-dark": {
|
"tokyonight-dark": {
|
||||||
"accent": "#7aa2f7",
|
"accent": "#7aa2f7",
|
||||||
"accent2": "#ff9e64",
|
"accent2": "#ff9e64",
|
||||||
|
|||||||
Executable
+21
@@ -0,0 +1,21 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# git for the shells of CodeCraft agents: `git commit` gets the co-author trailer from
|
||||||
|
# $CODECRAFT_COMMIT_TRAILER (model, provider, CodeCraft). Everything else goes to the real
|
||||||
|
# git unchanged. Git itself skips the trailer when the message already ends with it.
|
||||||
|
real=${CODECRAFT_REAL_GIT:-/usr/bin/git}
|
||||||
|
[[ -n $CODECRAFT_COMMIT_TRAILER ]] || exec "$real" "$@"
|
||||||
|
# The subcommand comes after git's global options (-C dir, -c key=value, --git-dir=…).
|
||||||
|
i=1
|
||||||
|
while (( i <= $# )); do
|
||||||
|
case ${!i} in
|
||||||
|
-C | -c | --git-dir | --work-tree | --namespace | --exec-path | --config-env) i=$((i + 2)) ;;
|
||||||
|
--) break ;;
|
||||||
|
-*) i=$((i + 1)) ;;
|
||||||
|
*) break ;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
if (( i <= $# )) && [[ ${!i} == commit ]]; then
|
||||||
|
# Right after "commit", so it stays an option even before "-- <paths>".
|
||||||
|
exec "$real" "${@:1:i}" --trailer "$CODECRAFT_COMMIT_TRAILER" "${@:i+1}"
|
||||||
|
fi
|
||||||
|
exec "$real" "$@"
|
||||||
@@ -307,8 +307,13 @@ pub struct ServerConfig {
|
|||||||
/// Bearer token for GET /metrics (Prometheus); empty = /metrics disabled.
|
/// Bearer token for GET /metrics (Prometheus); empty = /metrics disabled.
|
||||||
#[serde(skip_serializing_if = "String::is_empty")]
|
#[serde(skip_serializing_if = "String::is_empty")]
|
||||||
pub metrics_token: String,
|
pub metrics_token: String,
|
||||||
|
/// Trailer added to every git commit an agent makes ({model}, {provider}, {model_id});
|
||||||
|
/// empty = off.
|
||||||
|
pub commit_trailer: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub const DEFAULT_COMMIT_TRAILER: &str = "Co-Authored-By: {model} ({provider}) via CodeCraft";
|
||||||
|
|
||||||
impl Default for ServerConfig {
|
impl Default for ServerConfig {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
@@ -327,6 +332,7 @@ impl Default for ServerConfig {
|
|||||||
max_upload_mb: 500,
|
max_upload_mb: 500,
|
||||||
update_url: "https://git.familie-zink.org/api/v1/repos/zink/codecraft/releases/latest".into(),
|
update_url: "https://git.familie-zink.org/api/v1/repos/zink/codecraft/releases/latest".into(),
|
||||||
metrics_token: String::new(),
|
metrics_token: String::new(),
|
||||||
|
commit_trailer: DEFAULT_COMMIT_TRAILER.into(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -135,7 +135,7 @@ impl Tool for Bash {
|
|||||||
/// The bash command for a script, sandboxed for non-admin users.
|
/// The bash command for a script, sandboxed for non-admin users.
|
||||||
pub fn shell_command(ctx: &ToolCtx, command: &str) -> std::result::Result<Command, ToolOutput> {
|
pub fn shell_command(ctx: &ToolCtx, command: &str) -> std::result::Result<Command, ToolOutput> {
|
||||||
// stderr of every command (not just the last one) goes into the output.
|
// stderr of every command (not just the last one) goes into the output.
|
||||||
let script = format!("exec 2>&1\n{command}");
|
let script = format!("exec 2>&1\n{}{command}", commit_attribution(ctx));
|
||||||
Ok(match &ctx.sandbox {
|
Ok(match &ctx.sandbox {
|
||||||
Some(spec) => {
|
Some(spec) => {
|
||||||
if !crate::sandbox::available() {
|
if !crate::sandbox::available() {
|
||||||
@@ -161,6 +161,36 @@ pub fn shell_command(ctx: &ToolCtx, command: &str) -> std::result::Result<Comman
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Directory with the git wrapper that adds the co-author trailer to commits.
|
||||||
|
fn git_wrapper_dir() -> String {
|
||||||
|
std::env::var("CODECRAFT_GIT_WRAPPER").unwrap_or_else(|_| "/usr/local/lib/codecraft/bin".into())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Shell lines that make `git commit` in this command carry the co-author trailer of
|
||||||
|
/// the session's model (see docker/git-coauthor). Set in the script itself, so it
|
||||||
|
/// works inside the sandbox (which clears the environment) too.
|
||||||
|
fn commit_attribution(ctx: &ToolCtx) -> String {
|
||||||
|
let template = ctx.app.config().server.commit_trailer.clone();
|
||||||
|
let dir = git_wrapper_dir();
|
||||||
|
if template.trim().is_empty() || !std::path::Path::new(&dir).join("git").exists() {
|
||||||
|
return String::new();
|
||||||
|
}
|
||||||
|
let Ok(session) = ctx.app.session(&ctx.session_id) else { return String::new() };
|
||||||
|
let (provider, model_id) = session.model.split_once('/').unwrap_or(("", session.model.as_str()));
|
||||||
|
let name = ctx.app.registry().resolve(&session.model).ok().and_then(|(_, m)| m.name).filter(|n| !n.trim().is_empty()).unwrap_or_else(|| model_id.to_string());
|
||||||
|
let trailer = commit_trailer(&template, &name, provider, model_id);
|
||||||
|
format!("export PATH={}:\"$PATH\" CODECRAFT_COMMIT_TRAILER={}\n", shell_quote(&dir), shell_quote(&trailer))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The trailer text for a model ("Co-Authored-By: DeepSeek V4.1 Flash (opencode-go) via CodeCraft").
|
||||||
|
pub fn commit_trailer(template: &str, model: &str, provider: &str, model_id: &str) -> String {
|
||||||
|
template.replace("{model}", model).replace("{provider}", provider).replace("{model_id}", model_id).replace(['\n', '\r'], " ").trim().to_string()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn shell_quote(s: &str) -> String {
|
||||||
|
format!("'{}'", s.replace('\'', "'\\''"))
|
||||||
|
}
|
||||||
|
|
||||||
fn tail(s: &str, max: usize) -> String {
|
fn tail(s: &str, max: usize) -> String {
|
||||||
if s.len() <= max {
|
if s.len() <= max {
|
||||||
return s.to_string();
|
return s.to_string();
|
||||||
@@ -171,3 +201,18 @@ fn tail(s: &str, max: usize) -> String {
|
|||||||
}
|
}
|
||||||
s[start..].to_string()
|
s[start..].to_string()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod commit_tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn commit_trailer_text() {
|
||||||
|
assert_eq!(
|
||||||
|
commit_trailer(crate::config::DEFAULT_COMMIT_TRAILER, "DeepSeek V4.1 Flash", "opencode-go", "deepseek-v4.1-flash"),
|
||||||
|
"Co-Authored-By: DeepSeek V4.1 Flash (opencode-go) via CodeCraft"
|
||||||
|
);
|
||||||
|
assert_eq!(commit_trailer("By {model_id}\nx", "M", "p", "m-1"), "By m-1 x");
|
||||||
|
assert_eq!(shell_quote("it's"), "'it'\\''s'");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -248,6 +248,7 @@ function General({ cfg, reload }: { cfg: ConfigResponse; reload: () => void }) {
|
|||||||
small_model: s.small_model ?? "",
|
small_model: s.small_model ?? "",
|
||||||
currency: s.currency ?? "USD",
|
currency: s.currency ?? "USD",
|
||||||
auto_compact_threshold: s.auto_compact_threshold ?? 0.85,
|
auto_compact_threshold: s.auto_compact_threshold ?? 0.85,
|
||||||
|
commit_trailer: ((s as any).commit_trailer ?? "Co-Authored-By: {model} ({provider}) via CodeCraft") as string,
|
||||||
max_parallel_subagents: s.max_parallel_subagents ?? 4,
|
max_parallel_subagents: s.max_parallel_subagents ?? 4,
|
||||||
monthly_budget: s.monthly_budget ?? 0,
|
monthly_budget: s.monthly_budget ?? 0,
|
||||||
budget_warning: s.budget_warning ?? 0.8,
|
budget_warning: s.budget_warning ?? 0.8,
|
||||||
@@ -271,6 +272,7 @@ function General({ cfg, reload }: { cfg: ConfigResponse; reload: () => void }) {
|
|||||||
{ path: ["server", "small_model"], value: form.small_model },
|
{ path: ["server", "small_model"], value: form.small_model },
|
||||||
{ path: ["server", "currency"], value: form.currency },
|
{ path: ["server", "currency"], value: form.currency },
|
||||||
{ path: ["server", "auto_compact_threshold"], value: Number(form.auto_compact_threshold) },
|
{ path: ["server", "auto_compact_threshold"], value: Number(form.auto_compact_threshold) },
|
||||||
|
{ path: ["server", "commit_trailer"], value: form.commit_trailer.trim() },
|
||||||
{ path: ["server", "max_parallel_subagents"], value: Number(form.max_parallel_subagents) },
|
{ path: ["server", "max_parallel_subagents"], value: Number(form.max_parallel_subagents) },
|
||||||
{ path: ["server", "monthly_budget"], value: Number(form.monthly_budget) || 0 },
|
{ path: ["server", "monthly_budget"], value: Number(form.monthly_budget) || 0 },
|
||||||
{ path: ["server", "budget_warning"], value: Number(form.budget_warning) || 0.8 },
|
{ path: ["server", "budget_warning"], value: Number(form.budget_warning) || 0.8 },
|
||||||
@@ -305,6 +307,9 @@ function General({ cfg, reload }: { cfg: ConfigResponse; reload: () => void }) {
|
|||||||
<label>{t("settings.autoCompact")}</label>
|
<label>{t("settings.autoCompact")}</label>
|
||||||
<input type="number" min={0} max={1} step={0.05} value={form.auto_compact_threshold} onChange={(e) => setForm({ ...form, auto_compact_threshold: e.target.value })} />
|
<input type="number" min={0} max={1} step={0.05} value={form.auto_compact_threshold} onChange={(e) => setForm({ ...form, auto_compact_threshold: e.target.value })} />
|
||||||
<div className="hint">{t("settings.autoCompactHint")}</div>
|
<div className="hint">{t("settings.autoCompactHint")}</div>
|
||||||
|
<label>{t("settings.commitTrailer")}</label>
|
||||||
|
<input className="mono" value={form.commit_trailer} placeholder={t("settings.commitTrailerOff")} onChange={(e) => setForm({ ...form, commit_trailer: e.target.value })} />
|
||||||
|
<div className="hint">{t("settings.commitTrailerHint")}</div>
|
||||||
<label>{t("settings.monthlyBudgetTotal")}</label>
|
<label>{t("settings.monthlyBudgetTotal")}</label>
|
||||||
<input type="number" min={0} value={form.monthly_budget} onChange={(e) => setForm({ ...form, monthly_budget: e.target.value })} />
|
<input type="number" min={0} value={form.monthly_budget} onChange={(e) => setForm({ ...form, monthly_budget: e.target.value })} />
|
||||||
<div className="hint">{t("settings.monthlyBudgetHint")}</div>
|
<div className="hint">{t("settings.monthlyBudgetHint")}</div>
|
||||||
|
|||||||
@@ -770,6 +770,9 @@ export const de: Messages = {
|
|||||||
"settings.smallModelHint": "Für Session-Titel. Die Komprimierung nutzt das Session-Modell.",
|
"settings.smallModelHint": "Für Session-Titel. Die Komprimierung nutzt das Session-Modell.",
|
||||||
"settings.currency": "Währung",
|
"settings.currency": "Währung",
|
||||||
"settings.autoCompact": "Auto-Komprimierung bei",
|
"settings.autoCompact": "Auto-Komprimierung bei",
|
||||||
|
"settings.commitTrailer": "Commit-Vermerk der Agenten",
|
||||||
|
"settings.commitTrailerOff": "aus",
|
||||||
|
"settings.commitTrailerHint": "Wird als Trailer an jeden Git-Commit eines Agenten angehängt. {model} = Modellname, {provider} = Anbieter, {model_id} = Modell-ID. Leer = aus. Deine eigenen Commits bleiben unverändert.",
|
||||||
"settings.autoCompactHint": "Anteil des Kontextfensters (0 = aus).",
|
"settings.autoCompactHint": "Anteil des Kontextfensters (0 = aus).",
|
||||||
"settings.craft": "Craft-Modus",
|
"settings.craft": "Craft-Modus",
|
||||||
"settings.cleanRoom": "Clean Room",
|
"settings.cleanRoom": "Clean Room",
|
||||||
|
|||||||
@@ -769,6 +769,9 @@ export const en = {
|
|||||||
"settings.smallModelHint": "Used for session titles. Compaction uses the session model.",
|
"settings.smallModelHint": "Used for session titles. Compaction uses the session model.",
|
||||||
"settings.currency": "Currency",
|
"settings.currency": "Currency",
|
||||||
"settings.autoCompact": "Auto-compact at",
|
"settings.autoCompact": "Auto-compact at",
|
||||||
|
"settings.commitTrailer": "Commit note of agents",
|
||||||
|
"settings.commitTrailerOff": "off",
|
||||||
|
"settings.commitTrailerHint": "Added as a trailer to every git commit an agent makes. {model} = model name, {provider} = provider, {model_id} = model id. Empty = off. Your own commits stay unchanged.",
|
||||||
"settings.autoCompactHint": "Fraction of the context window (0 = off).",
|
"settings.autoCompactHint": "Fraction of the context window (0 = off).",
|
||||||
"settings.craft": "Craft mode",
|
"settings.craft": "Craft mode",
|
||||||
"settings.cleanRoom": "Clean room",
|
"settings.cleanRoom": "Clean room",
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ export const THEMES = [
|
|||||||
{ id: "rosepine", name: "Rosé Pine", source: "rose-pine/rose-pine-theme", license: "MIT" },
|
{ id: "rosepine", name: "Rosé Pine", source: "rose-pine/rose-pine-theme", license: "MIT" },
|
||||||
{ id: "everforest", name: "Everforest", source: "sainnhe/everforest", license: "MIT" },
|
{ id: "everforest", name: "Everforest", source: "sainnhe/everforest", license: "MIT" },
|
||||||
{ id: "kanagawa", name: "Kanagawa", source: "rebelot/kanagawa.nvim", license: "MIT" },
|
{ id: "kanagawa", name: "Kanagawa", source: "rebelot/kanagawa.nvim", license: "MIT" },
|
||||||
|
{ id: "tintenblau", name: "Tintenblau", source: "zink/tintenblau", license: "private" },
|
||||||
] as const;
|
] as const;
|
||||||
|
|
||||||
export type Theme = (typeof THEMES)[number]["id"];
|
export type Theme = (typeof THEMES)[number]["id"];
|
||||||
|
|||||||
@@ -30,3 +30,5 @@ import "./everforest-dark.css";
|
|||||||
import "./everforest-light.css";
|
import "./everforest-light.css";
|
||||||
import "./kanagawa-dark.css";
|
import "./kanagawa-dark.css";
|
||||||
import "./kanagawa-light.css";
|
import "./kanagawa-light.css";
|
||||||
|
import "./tintenblau-dark.css";
|
||||||
|
import "./tintenblau-light.css";
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
/*
|
||||||
|
* Tintenblau Dark
|
||||||
|
* Palette: zink/tintenblau (colors.toml, neovim.lua, zed.json of the desktop theme).
|
||||||
|
*/
|
||||||
|
:root[data-theme="tintenblau-dark"] {
|
||||||
|
color-scheme: dark;
|
||||||
|
--theme-color: #232a44;
|
||||||
|
|
||||||
|
--bg0: #232a44;
|
||||||
|
--bg1: #2b3352;
|
||||||
|
--bg2: #353e60;
|
||||||
|
--bg3: #4a5478;
|
||||||
|
--fg0: #f5efe3;
|
||||||
|
--fg1: #eae4d6;
|
||||||
|
--fg2: #d6d0c2;
|
||||||
|
--fg3: #bdcfd4;
|
||||||
|
--muted: #8e96b4;
|
||||||
|
|
||||||
|
--accent: #7aa0cc;
|
||||||
|
--accent2: #d4b46a;
|
||||||
|
--on-accent: #232a44;
|
||||||
|
--red: #d47a6a;
|
||||||
|
--green: #96b4bc;
|
||||||
|
--aqua: #96b4bc;
|
||||||
|
--blue: #7aa0cc;
|
||||||
|
--purple: #c896b2;
|
||||||
|
|
||||||
|
--diff-add: rgba(150, 180, 188, 0.16);
|
||||||
|
--diff-del: rgba(212, 122, 106, 0.16);
|
||||||
|
--shadow: 0 6px 24px rgba(10, 14, 30, 0.45);
|
||||||
|
|
||||||
|
--syntax-keyword: #c896b2;
|
||||||
|
--syntax-string: #d47a6a;
|
||||||
|
--syntax-number: #96b4bc;
|
||||||
|
--syntax-comment: #8e96b4;
|
||||||
|
--syntax-function: #7aa0cc;
|
||||||
|
--syntax-type: #d4b46a;
|
||||||
|
--syntax-variable: #eae4d6;
|
||||||
|
--syntax-meta: #baa0c4;
|
||||||
|
}
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
/*
|
||||||
|
* Tintenblau (light)
|
||||||
|
* Palette: zink/tintenblau (colors.toml, neovim.lua, zed.json of the desktop theme).
|
||||||
|
*/
|
||||||
|
:root[data-theme="tintenblau-light"] {
|
||||||
|
color-scheme: light;
|
||||||
|
--theme-color: #f0ead9;
|
||||||
|
|
||||||
|
--bg0: #f0ead9;
|
||||||
|
--bg1: #e6dfd0;
|
||||||
|
--bg2: #dbd3c1;
|
||||||
|
--bg3: #c9c0ac;
|
||||||
|
--fg0: #0f1628;
|
||||||
|
--fg1: #1e2a4a;
|
||||||
|
--fg2: #3a3f5a;
|
||||||
|
--fg3: #5e5a6a;
|
||||||
|
--muted: #8a8694;
|
||||||
|
|
||||||
|
--accent: #2a3f6f;
|
||||||
|
--accent2: #6a4a2c;
|
||||||
|
--on-accent: #f0ead9;
|
||||||
|
--red: #8a3028;
|
||||||
|
--green: #4a5a3a;
|
||||||
|
--aqua: #3a5a6a;
|
||||||
|
--blue: #3a5a8a;
|
||||||
|
--purple: #5a4a72;
|
||||||
|
|
||||||
|
--diff-add: rgba(74, 90, 58, 0.14);
|
||||||
|
--diff-del: rgba(138, 48, 40, 0.12);
|
||||||
|
--shadow: 0 6px 24px rgba(30, 42, 74, 0.16);
|
||||||
|
|
||||||
|
--syntax-keyword: #2a3f6f;
|
||||||
|
--syntax-string: #8a3028;
|
||||||
|
--syntax-number: #5a4a72;
|
||||||
|
--syntax-comment: #8a8694;
|
||||||
|
--syntax-function: #3a5a6a;
|
||||||
|
--syntax-type: #6a4a2c;
|
||||||
|
--syntax-variable: #1e2a4a;
|
||||||
|
--syntax-meta: #4a5a3a;
|
||||||
|
}
|
||||||
@@ -3,6 +3,14 @@
|
|||||||
* under "unreleased"; scripts/release.sh renames the key to the release version.
|
* under "unreleased"; scripts/release.sh renames the key to the release version.
|
||||||
*/
|
*/
|
||||||
export const WHATS_NEW: Record<string, { en: string[]; de: string[] }> = {
|
export const WHATS_NEW: Record<string, { en: string[]; de: string[] }> = {
|
||||||
|
"202611-00022": {
|
||||||
|
en: ["New theme “Tintenblau” (Settings → General → Theme)"],
|
||||||
|
de: ["Neues Theme „Tintenblau“ (Einstellungen → Allgemein → Theme)"],
|
||||||
|
},
|
||||||
|
"202611-00021": {
|
||||||
|
en: ["Git commits of agents name the model: “Co-Authored-By: <model> (<provider>) via CodeCraft” (Settings → General)"],
|
||||||
|
de: ["Git-Commits von Agenten nennen das Modell: „Co-Authored-By: <Modell> (<Anbieter>) via CodeCraft“ (Einstellungen → Allgemein)"],
|
||||||
|
},
|
||||||
"202611-00020": {
|
"202611-00020": {
|
||||||
en: ["Reverse engineering: decompile with Ghidra can be enabled for any mode (Settings → Agents & commands), now also for raw firmware"],
|
en: ["Reverse engineering: decompile with Ghidra can be enabled for any mode (Settings → Agents & commands), now also for raw firmware"],
|
||||||
de: ["Reverse Engineering: Dekompilieren mit Ghidra lässt sich für jeden Modus einschalten (Einstellungen → Agenten & Befehle), jetzt auch für rohe Firmware"],
|
de: ["Reverse Engineering: Dekompilieren mit Ghidra lässt sich für jeden Modus einschalten (Einstellungen → Agenten & Befehle), jetzt auch für rohe Firmware"],
|
||||||
|
|||||||
Reference in new issue
Block a user