mirror of
https://github.com/codewhale-hq/Codewhale.git
synced 2026-11-11 10:01:30 -10:00
Compare commits
6
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f86aac6275 | ||
|
|
c390b981b7 | ||
|
|
fef5762aac | ||
|
|
35e5e150e3 | ||
|
|
abcdd8ec3c | ||
|
|
cdb2679825 |
No files matched your search
@@ -14,7 +14,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
- **External agents over ACP:** Codewhale now drives external agents through the official Agent Client Protocol SDK. Request ids on the wire are strings, and an agent's error is treated as a sign-in problem only when it carries the protocol's auth-required code.
|
||||
- **Creator skills:** Codewhale now ships DeepSeek Harness's four plugin-authoring skills (`cordis-plugin-development`, `editing-cordis-compositions`, `cordis-composition-reference`, `agent-experience`) unmodified, with their reference and template files. A bundled skill can now carry files: `load_skill` prints the skill's base directory and a short Codewhale host-notes block after DSH's text. A copy you edited is never overwritten on upgrade. Source-level only: the Creator plugin switch and agent entry that make these useful land in later slices.
|
||||
- **Creator skills:** Codewhale now ships DeepSeek Harness's four plugin-authoring skills (`cordis-plugin-development`, `editing-cordis-compositions`, `cordis-composition-reference`, `agent-experience`) bundled, unmodified, with their reference and template files and DeepSeek's MIT notice beside each. They stay hidden from the model until Creator mode. A bundled skill can now carry files; `load_skill` prints its base directory and a short Codewhale host-notes block, and a file you edited is never overwritten on upgrade.
|
||||
- **Website home page, rebuilt around one first screen:** the headline stays "The model can be anyone's. The agent should be yours.", centred, with one plain sentence under it that says what Codewhale is (an open-source coding agent for your terminal, written in Rust, working with any model and able to split a task across several agents), the install command, and one wide picture of a real terminal session that runs off the bottom of the first screen. A row of facts under the picture takes its counts of provider routes and models from the published release, and the page says several agents share a task only while the release has it. The route of where the product is going moves lower and smaller, and the home page names no other tools. The new text in the 17 non-English languages is machine-drafted and is labelled as such in the footer; the Arabic page now reads the opening sentence right to left.
|
||||
- **Website home page:** the page is reorganized around what a visitor does: a dated OrcaRouter offer strip with a visible referral tag, one proof strip (release, stars, contributors), a models section with the newest first-party models, one capabilities section that includes Computer Use, an "Everything is a plugin" teaser and a community panel whose live GitHub numbers fill in from the browser and hide when unavailable. Unused home copy was removed from all 18 site languages.
|
||||
- **Website home page, rebuilt around one first screen:** the headline stays "The model can be anyone's. The agent should be yours.", centred, with one plain sentence under it that says what Codewhale is (an open-source coding agent for your terminal, written in Rust, working with any model and able to split a task across several agents), the install command, and one wide picture of a real terminal session that runs off the bottom of the first screen. A row of facts under the picture takes its counts of provider routes and models from the published release, and the page says several agents share a task only while the release has it. The route of where the product is going moves lower and smaller, and the home page names no other tools. This replaces the earlier reorganization of the home page, so the dated OrcaRouter offer strip and the community panel whose GitHub numbers filled in from the browser are no longer on it; the models and contribute pages keep their own content. The new text in the 17 non-English languages is machine-drafted and is labelled as such in the footer; the Arabic page now reads the opening sentence right to left.
|
||||
- **Website home page, rebuilt around one first screen:** the headline stays "The model can be anyone's. The agent should be yours.", centred, with one plain sentence under it that says what Codewhale is (an open-source coding agent for your terminal, written in Rust, working with any model and able to split a task across several agents), the install command, and one wide picture of a real terminal session that runs off the bottom of the first screen. A row of facts under the picture takes its counts of provider routes and models from the published release, and the facts row lists many agents on one task only while the release has it. The route of where the product is going moves lower and smaller, and the home page names no other coding agents. This replaces the earlier reorganization of the home page, so the community panel whose GitHub numbers filled in from the browser is no longer on it, and the dated OrcaRouter offer strip, English only, with its referral disclosure, stays below the first screen until it ends; the models and contribute pages keep their own content. The new text in the 17 non-English languages is machine-drafted and is labelled as such in the footer; the Arabic page now reads the opening sentence right to left.
|
||||
- **Website models and plugins:** /models leads with recently added models, three ways to connect (DeepSeek, first-party providers, any OpenAI-compatible endpoint) and a catalog grouped by maker; Ollama and OpenRouter are listed but no longer featured. /plugins leads with "Everything is a plugin" (credited to DeepSeek Harness) and shows MCP connectors from the connection catalog.
|
||||
- **Model catalog:** Claude Fable 5.2 (`claude-fable-5-2`) and Gemini 4 Carbon (`gemini-4-carbon`) are in the bundled catalog, and a daily catalog-refresh workflow proposes new first-party models from models.dev as a pull request. The workflow has not run in GitHub Actions yet.
|
||||
- **Gemini 4 output limit:** Gemini 4 Carbon and Gemini 4 Argon now allow 65,536 output tokens per reply, up from 32,768, matching Google's published limit.
|
||||
|
||||
@@ -14,7 +14,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
- **External agents over ACP:** Codewhale now drives external agents through the official Agent Client Protocol SDK. Request ids on the wire are strings, and an agent's error is treated as a sign-in problem only when it carries the protocol's auth-required code.
|
||||
- **Creator skills:** Codewhale now ships DeepSeek Harness's four plugin-authoring skills (`cordis-plugin-development`, `editing-cordis-compositions`, `cordis-composition-reference`, `agent-experience`) unmodified, with their reference and template files. A bundled skill can now carry files: `load_skill` prints the skill's base directory and a short Codewhale host-notes block after DSH's text. A copy you edited is never overwritten on upgrade. Source-level only: the Creator plugin switch and agent entry that make these useful land in later slices.
|
||||
- **Creator skills:** Codewhale now ships DeepSeek Harness's four plugin-authoring skills (`cordis-plugin-development`, `editing-cordis-compositions`, `cordis-composition-reference`, `agent-experience`) bundled, unmodified, with their reference and template files and DeepSeek's MIT notice beside each. They stay hidden from the model until Creator mode. A bundled skill can now carry files; `load_skill` prints its base directory and a short Codewhale host-notes block, and a file you edited is never overwritten on upgrade.
|
||||
- **Website home page, rebuilt around one first screen:** the headline stays "The model can be anyone's. The agent should be yours.", centred, with one plain sentence under it that says what Codewhale is (an open-source coding agent for your terminal, written in Rust, working with any model and able to split a task across several agents), the install command, and one wide picture of a real terminal session that runs off the bottom of the first screen. A row of facts under the picture takes its counts of provider routes and models from the published release, and the page says several agents share a task only while the release has it. The route of where the product is going moves lower and smaller, and the home page names no other tools. The new text in the 17 non-English languages is machine-drafted and is labelled as such in the footer; the Arabic page now reads the opening sentence right to left.
|
||||
- **Website home page:** the page is reorganized around what a visitor does: a dated OrcaRouter offer strip with a visible referral tag, one proof strip (release, stars, contributors), a models section with the newest first-party models, one capabilities section that includes Computer Use, an "Everything is a plugin" teaser and a community panel whose live GitHub numbers fill in from the browser and hide when unavailable. Unused home copy was removed from all 18 site languages.
|
||||
- **Website home page, rebuilt around one first screen:** the headline stays "The model can be anyone's. The agent should be yours.", centred, with one plain sentence under it that says what Codewhale is (an open-source coding agent for your terminal, written in Rust, working with any model and able to split a task across several agents), the install command, and one wide picture of a real terminal session that runs off the bottom of the first screen. A row of facts under the picture takes its counts of provider routes and models from the published release, and the page says several agents share a task only while the release has it. The route of where the product is going moves lower and smaller, and the home page names no other tools. This replaces the earlier reorganization of the home page, so the dated OrcaRouter offer strip and the community panel whose GitHub numbers filled in from the browser are no longer on it; the models and contribute pages keep their own content. The new text in the 17 non-English languages is machine-drafted and is labelled as such in the footer; the Arabic page now reads the opening sentence right to left.
|
||||
- **Website home page, rebuilt around one first screen:** the headline stays "The model can be anyone's. The agent should be yours.", centred, with one plain sentence under it that says what Codewhale is (an open-source coding agent for your terminal, written in Rust, working with any model and able to split a task across several agents), the install command, and one wide picture of a real terminal session that runs off the bottom of the first screen. A row of facts under the picture takes its counts of provider routes and models from the published release, and the facts row lists many agents on one task only while the release has it. The route of where the product is going moves lower and smaller, and the home page names no other coding agents. This replaces the earlier reorganization of the home page, so the community panel whose GitHub numbers filled in from the browser is no longer on it, and the dated OrcaRouter offer strip, English only, with its referral disclosure, stays below the first screen until it ends; the models and contribute pages keep their own content. The new text in the 17 non-English languages is machine-drafted and is labelled as such in the footer; the Arabic page now reads the opening sentence right to left.
|
||||
- **Website models and plugins:** /models leads with recently added models, three ways to connect (DeepSeek, first-party providers, any OpenAI-compatible endpoint) and a catalog grouped by maker; Ollama and OpenRouter are listed but no longer featured. /plugins leads with "Everything is a plugin" (credited to DeepSeek Harness) and shows MCP connectors from the connection catalog.
|
||||
- **Model catalog:** Claude Fable 5.2 (`claude-fable-5-2`) and Gemini 4 Carbon (`gemini-4-carbon`) are in the bundled catalog, and a daily catalog-refresh workflow proposes new first-party models from models.dev as a pull request. The workflow has not run in GitHub Actions yet.
|
||||
- **Gemini 4 output limit:** Gemini 4 Carbon and Gemini 4 Argon now allow 65,536 output tokens per reply, up from 32,768, matching Google's published limit.
|
||||
|
||||
@@ -142,6 +142,11 @@ def gpui_geometry(data, digest):
|
||||
for mode, stops in data["water"].items():
|
||||
ramp = ", ".join(f"#{stop['color']} {stop['at']}%" for stop in stops)
|
||||
lines.append(f" --gpui-water-{mode}: linear-gradient(180deg, {ramp});")
|
||||
for mode, light in data["surface_light"].items():
|
||||
stops = ", ".join(
|
||||
f"rgb({' '.join(str(v) for v in bytes.fromhex(stop['color']['hex']))} / {stop['color']['alpha']:g}) {stop['at']}%"
|
||||
for stop in light["stops"])
|
||||
lines.append(f" --gpui-surface-light-{mode}: radial-gradient(ellipse at {light['x']}% {light['y']}%, {stops});")
|
||||
rise, deep = data["ombre"]["rise"], data["ombre"]["deep"]
|
||||
lines.append(f" --gpui-ombre: linear-gradient(135deg, #{rise}, #{deep});")
|
||||
fallbacks = ", ".join(json.dumps(v) for v in data["typography"]["fallbacks"])
|
||||
|
||||
+255
-219
@@ -3,255 +3,291 @@ import Image from "next/image";
|
||||
import Link from "next/link";
|
||||
import { HeroInstall } from "@/components/hero-install";
|
||||
import { Icon } from "@/components/icon";
|
||||
import { Section } from "@/components/page-header";
|
||||
import { Strata } from "@/components/strata";
|
||||
import { NativeTerminalGallery } from "@/components/native-terminal-gallery";
|
||||
import { LivingHeroWhale } from "@/components/living-hero-whale";
|
||||
import { releaseView } from "@/lib/capabilities";
|
||||
import { getFacts } from "@/lib/facts";
|
||||
import { GitHubStar, FaceWall, MergedList, PulseStats, statsAsOf } from "@/components/project-pulse";
|
||||
import { LiveStarLink } from "@/components/github-live";
|
||||
import { loadProjectPulse } from "@/lib/project-pulse";
|
||||
import { PROJECT_COPY } from "@/lib/content/project";
|
||||
import { HOME_OVERVIEW } from "@/lib/content/home-overview";
|
||||
import { PRODUCT_COPY } from "@/lib/content/product";
|
||||
import { readAsOf } from "@/components/project-pulse";
|
||||
import { InlineCode } from "@/components/route/inline-code";
|
||||
import { RouteMap, type RoutePart } from "@/components/route/product-route";
|
||||
import { SessionFrames, type SessionFrame } from "@/components/route/session-frames";
|
||||
import { TerminalCapture } from "@/components/terminal-capture";
|
||||
import { releaseView, runtimeCapabilityContext } from "@/lib/capabilities";
|
||||
import { captureCaption, frameNote } from "@/lib/capture-caption";
|
||||
import { PARTNER_PROMO } from "@/lib/content/partner-promo";
|
||||
import { daysLeft, isActiveAt } from "@/lib/expiry";
|
||||
import { fill, getChrome, getComputerUse, getHome, pickText, splitToken } from "@/lib/i18n/dictionaries";
|
||||
import { REPO_ISSUES_URL, REPO_URL } from "@/lib/i18n/links";
|
||||
import { PROJECT_COPY } from "@/lib/content/project";
|
||||
import { PRODUCT_MAP_STATE_KEY, PRODUCT_MAP_TEXT_KEYS, productMap, type ProductMapId } from "@/lib/content/product-map";
|
||||
import { getFacts } from "@/lib/facts";
|
||||
import { fill, getChrome, getContribute, getHome, pickText, splitToken } from "@/lib/i18n/dictionaries";
|
||||
import type { HomeDict } from "@/lib/i18n/dictionaries/types";
|
||||
import { REPO_URL } from "@/lib/i18n/links";
|
||||
import { pluralText } from "@/lib/i18n/plural";
|
||||
import { serializeJsonLd } from "@/lib/json-ld";
|
||||
import { TERMINAL_SCREENSHOT } from "@/lib/media-manifest";
|
||||
import { RELEASED_CAPTURE_FRAMES, type ReleasedCaptureFrameId } from "@/lib/released-capture.generated";
|
||||
import { parseExclusions, repoActivityView, type ActivityPull } from "@/lib/repo-activity";
|
||||
import { buildSoftwareApplicationJsonLd } from "@/lib/software-application-schema";
|
||||
import { getReleaseUpdates } from "@/lib/release-updates";
|
||||
|
||||
// Resolve published releases at request time; source fetches cache for five
|
||||
// minutes. `getFacts()` rejects legacy or older KV snapshots.
|
||||
export const dynamic = "force-dynamic";
|
||||
export const revalidate = 300;
|
||||
|
||||
const PART_DOC: Partial<Record<ProductMapId, string>> = {
|
||||
terminal: "/install",
|
||||
localBrowser: "/docs/web",
|
||||
manyAgents: "/docs/fleet",
|
||||
};
|
||||
|
||||
const SESSION_STEPS = [
|
||||
{ key: "02-task-prompt", name: "frameLabelPrompt", summary: "frameSummaryPrompt" },
|
||||
{ key: "03-task-diff", name: "frameLabelEdit", summary: "frameSummaryEdit" },
|
||||
{ key: "04-task-approval", name: "frameLabelApproval", summary: "frameSummaryApproval" },
|
||||
{ key: "05-task-result", name: "frameLabelResult", summary: "frameSummaryResult" },
|
||||
] as const satisfies readonly { key: string; name: keyof HomeDict; summary: keyof HomeDict }[];
|
||||
const SESSION_OPENS_ON = 2;
|
||||
const PROVIDER_ROWS = [9, 21] as const;
|
||||
const RECEIPT_ROWS_NARROW = [18, 60] as const;
|
||||
const MERGED_ROWS = 8;
|
||||
|
||||
const avatar = (url: string, size: number) => `${url}${url.includes("?") ? "&" : "?"}s=${size}`;
|
||||
|
||||
function latestPerAccount(pulls: readonly ActivityPull[], limit: number): ActivityPull[] {
|
||||
const seen = new Set<string>();
|
||||
const rows: ActivityPull[] = [];
|
||||
for (const pull of pulls) {
|
||||
if (seen.has(pull.login)) continue;
|
||||
seen.add(pull.login);
|
||||
rows.push(pull);
|
||||
if (rows.length === limit) break;
|
||||
}
|
||||
return rows;
|
||||
}
|
||||
|
||||
function splitCount(text: string, count: string): [string, string] {
|
||||
const at = text.indexOf(count);
|
||||
return at < 0 ? [text, ""] : [text.slice(0, at), text.slice(at + count.length)];
|
||||
}
|
||||
|
||||
interface Fact {
|
||||
key: string;
|
||||
before: string;
|
||||
number: string | null;
|
||||
after: string;
|
||||
href: string;
|
||||
}
|
||||
|
||||
export default async function HomePage({ params }: { params: Promise<{ locale: string }> }) {
|
||||
const { locale } = await params;
|
||||
const d = getHome(locale);
|
||||
const chrome = getChrome(locale);
|
||||
const contribute = getContribute(locale);
|
||||
const facts = await getFacts();
|
||||
const updates = await getReleaseUpdates(facts.latestPublishedRelease);
|
||||
const pulse = await loadProjectPulse(5, 48);
|
||||
const release = facts.latestPublishedRelease;
|
||||
const jsonLd = buildSoftwareApplicationJsonLd(release, locale);
|
||||
const numbers = new Intl.NumberFormat(chrome.dateLocale);
|
||||
const promoLeft = Date.parse(PARTNER_PROMO.endsAt) - Date.now();
|
||||
const promoDays = Math.max(1, Math.ceil(promoLeft / 86_400_000));
|
||||
const showPromo = promoLeft > 0 && PARTNER_PROMO.locales.includes(locale);
|
||||
|
||||
const parts: RoutePart[] = productMap({ capabilities: runtimeCapabilityContext(facts) }).map((row) => {
|
||||
const keys = PRODUCT_MAP_TEXT_KEYS[row.id];
|
||||
return {
|
||||
id: row.id,
|
||||
state: row.state,
|
||||
name: d[keys.name],
|
||||
stateWord: d[PRODUCT_MAP_STATE_KEY[row.state]],
|
||||
sentence: row.sentenceHolds ? d[keys.sentence] : null,
|
||||
href: row.state === "released" ? (PART_DOC[row.id] ?? null) : null,
|
||||
};
|
||||
});
|
||||
|
||||
const frame = (id: ReleasedCaptureFrameId, summary: string, rows?: readonly [number, number]) => (
|
||||
<TerminalCapture data={RELEASED_CAPTURE_FRAMES[id]} label={summary} regionLabel={d.shotPreview} rows={rows} />
|
||||
);
|
||||
const sessionWide = SESSION_STEPS.map((step) => RELEASED_CAPTURE_FRAMES[`cols100/${step.key}`]);
|
||||
const sessionCaption = captureCaption(sessionWide).caption;
|
||||
const session: SessionFrame[] = SESSION_STEPS.map((step, index) => ({
|
||||
key: step.key,
|
||||
name: d[step.name],
|
||||
wide: frame(`cols100/${step.key}`, d[step.summary]),
|
||||
narrow: frame(`cols48/${step.key}`, d[step.summary]),
|
||||
note: frameNote(sessionWide[index]).needsPostureNote,
|
||||
}));
|
||||
const providerNote = frameNote(RELEASED_CAPTURE_FRAMES["cols56/01-custom-form"]).needsPostureNote;
|
||||
|
||||
const released = releaseView(facts);
|
||||
const sourceVersion = facts.version ?? "unknown";
|
||||
const publishedRelease = updates.terminal.release;
|
||||
const sourceIsPublished = publishedRelease?.version === sourceVersion;
|
||||
const promoDays = daysLeft(PARTNER_PROMO.endsAt);
|
||||
const showPromo = isActiveAt(PARTNER_PROMO.endsAt) && PARTNER_PROMO.locales.includes(locale);
|
||||
const jsonLd = buildSoftwareApplicationJsonLd(publishedRelease, locale);
|
||||
const ledeParts = splitToken(d.heroIntro, "brand");
|
||||
const gateways = new Set(["OpenRouter", "Together", "OpenAI Codex"]);
|
||||
const makers = [...new Set(released.models.map((model) => model.provider).filter((name): name is string => !!name && !gateways.has(name)))]
|
||||
.sort((a, b) => a.localeCompare(b));
|
||||
const firstParty = new Set(makers);
|
||||
const newest = released.models
|
||||
.filter((model) => model.addedAt && model.provider && firstParty.has(model.provider))
|
||||
.sort((a, b) => (b.addedAt ?? "").localeCompare(a.addedAt ?? ""))
|
||||
.slice(0, 6);
|
||||
const releaseDate = publishedRelease
|
||||
? new Intl.DateTimeFormat(chrome.dateLocale, { month: "short", day: "numeric", year: "numeric" }).format(new Date(publishedRelease.publishedAt))
|
||||
: null;
|
||||
const counted = (key: string, forms: HomeDict["factModels"], n: number, href: string): Fact => {
|
||||
const number = numbers.format(n);
|
||||
const [before, after] = splitCount(pluralText(forms, n, locale, chrome.dateLocale), number);
|
||||
return { key, before, number, after, href };
|
||||
};
|
||||
const plain = (key: string, text: string, href: string): Fact => ({ key, before: text, number: null, after: "", href });
|
||||
const manyAgents = parts.find((part) => part.id === "manyAgents");
|
||||
const factList: Fact[] = [
|
||||
plain("rust", d.factRust, REPO_URL),
|
||||
...(facts.license ? [plain("license", fill(d.factLicense, { license: facts.license }), `${REPO_URL}/blob/main/LICENSE`)] : []),
|
||||
...(released.counts
|
||||
? [
|
||||
counted("providers", d.factProviders, released.counts.providerRoutes, `/${locale}/models`),
|
||||
counted("models", d.factModels, released.counts.models, `/${locale}/models`),
|
||||
]
|
||||
: []),
|
||||
...(manyAgents?.state === "released" ? [plain("agents", manyAgents.name, `/${locale}${PART_DOC.manyAgents}`)] : []),
|
||||
];
|
||||
|
||||
const activity = repoActivityView();
|
||||
const merged = latestPerAccount(activity?.pulls ?? [], MERGED_ROWS);
|
||||
const accountForms = parseExclusions().confirmed ? contribute.mergedPeopleCount : contribute.mergedAccountsCount;
|
||||
const accounts = activity?.count != null ? pluralText(accountForms, activity.count, locale, chrome.dateLocale) : null;
|
||||
const accountsAsOf = readAsOf(activity?.readAt, locale);
|
||||
const contributors = numbers.format(facts.contributorCount);
|
||||
const [contributorsBefore, contributorsAfter] = splitCount(pluralText(d.contributorsCount, facts.contributorCount, locale, chrome.dateLocale), contributors);
|
||||
|
||||
const titleAnswer = d.heroTitle.slice(d.heroTitleLead.length).trim();
|
||||
const brand = (text: string) =>
|
||||
splitToken(text, "brand").map((part, index) => (
|
||||
<Fragment key={index}>
|
||||
{index > 0 ? <bdi>Codewhale</bdi> : null}
|
||||
{part}
|
||||
</Fragment>
|
||||
));
|
||||
|
||||
const install = (
|
||||
<div className="home-install">
|
||||
<HeroInstall locale={locale} ariaLabel={d.heroInstallAria} copyLabel={d.copy} copiedLabel={d.copied} />
|
||||
<p className="home-version">
|
||||
{release ? <span>{fill(d.installNote, { version: release.version })}</span> : null}
|
||||
<a href={REPO_URL} className="home-version-link">
|
||||
{d.readTheCode}
|
||||
</a>
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
const factRow =
|
||||
factList.length > 0 ? (
|
||||
<ul className="home-facts" role="list" aria-label={d.factsLabel}>
|
||||
{factList.map((fact) => {
|
||||
const content = (
|
||||
<>
|
||||
{fact.before}
|
||||
{fact.number ? <strong className="tabular">{fact.number}</strong> : null}
|
||||
{fact.after}
|
||||
</>
|
||||
);
|
||||
return (
|
||||
<li key={fact.key}>
|
||||
{fact.href.startsWith("/") ? <Link href={fact.href}>{content}</Link> : <a href={fact.href}>{content}</a>}
|
||||
</li>
|
||||
);
|
||||
})}
|
||||
</ul>
|
||||
) : null;
|
||||
const sessionShot = (
|
||||
<SessionFrames frames={session} initial={SESSION_OPENS_ON} label={d.shotPreview} caption={sessionCaption ? d[sessionCaption] : null} note={d.captureFrameNoPosture} />
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="home">
|
||||
<script
|
||||
type="application/ld+json"
|
||||
dangerouslySetInnerHTML={{ __html: serializeJsonLd(jsonLd) }}
|
||||
/>
|
||||
<script type="application/ld+json" dangerouslySetInnerHTML={{ __html: serializeJsonLd(jsonLd) }} />
|
||||
|
||||
<div className="home-intro">
|
||||
<div className="home-water" aria-hidden="true"><Strata /></div>
|
||||
<section className="home-hero" aria-labelledby="home-title">
|
||||
<div className="home-hero-inner">
|
||||
<div className="home-hero-copy">
|
||||
<h1 id="home-title" className="home-title">{d.heroTitle}</h1>
|
||||
<p className="home-lede">
|
||||
{ledeParts.map((part, index) => (
|
||||
<Fragment key={index}>
|
||||
{index > 0 && <strong>Codewhale</strong>}
|
||||
{part}
|
||||
</Fragment>
|
||||
))}
|
||||
</p>
|
||||
<HeroInstall locale={locale} ariaLabel={d.heroInstallAria} copyLabel={d.copy} copiedLabel={d.copied} />
|
||||
<div className="actions">
|
||||
<Link href={`/${locale}/install`} className="btn btn-primary btn-lg">
|
||||
{d.getCodewhale}
|
||||
</Link>
|
||||
<GitHubStar stars={pulse.stats?.stars ?? undefined} locale={locale} asOf={statsAsOf(pulse.stats, locale)} />
|
||||
<Link href={`/${locale}/contribute`} className="btn btn-ghost btn-lg">
|
||||
{d.contribute}
|
||||
<Icon name="arrow-right" className="icon icon-flip" />
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
<div className="home-whale">
|
||||
<LivingHeroWhale locale={locale} />
|
||||
</div>
|
||||
<section className="home-hero" data-hero="center" aria-labelledby="home-title">
|
||||
<div className="home-hero-text">
|
||||
<h1 id="home-title" className="home-title">
|
||||
<span className="home-title-lead">{d.heroTitleLead}</span> <span className="home-title-answer">{titleAnswer}</span>
|
||||
</h1>
|
||||
<p className="home-subhead">{brand(d.heroIntro)}</p>
|
||||
{install}
|
||||
</div>
|
||||
<div className="home-hero-shot">{sessionShot}</div>
|
||||
{factRow}
|
||||
</section>
|
||||
|
||||
<div className="home-sections">
|
||||
<section className="home-section home-split" aria-labelledby="home-model">
|
||||
<div className="home-text">
|
||||
<h2 id="home-model" className="home-h2">{d.sectionModelHeading}</h2>
|
||||
<p className="home-body"><InlineCode text={d.sectionModelBody} /></p>
|
||||
</div>
|
||||
<figure className="home-figure">
|
||||
<div className="home-frame home-only-wide">{frame("cols56/01-custom-form", d.frameSummaryProvider, PROVIDER_ROWS)}</div>
|
||||
<div className="home-frame home-only-narrow">{frame("cols48/01-custom-form", d.frameSummaryProvider, PROVIDER_ROWS)}</div>
|
||||
{providerNote ? <figcaption className="home-caption">{d.captureFrameNoPosture}</figcaption> : null}
|
||||
</figure>
|
||||
</section>
|
||||
|
||||
<section className="home-terminal" aria-label={d.shotPreview}>
|
||||
<div className="home-terminal-inner">
|
||||
<figure className="home-shot figure">
|
||||
<div className="figure-frame stage" dir="ltr">
|
||||
<NativeTerminalGallery
|
||||
locale={locale}
|
||||
defaultFrame="home"
|
||||
regionLabel={d.shotPreview}
|
||||
label={fill(d.screenshotAlt, { version: TERMINAL_SCREENSHOT.version })}
|
||||
/>
|
||||
</div>
|
||||
</figure>
|
||||
<div
|
||||
className="home-proof"
|
||||
data-source-state={sourceIsPublished ? "published release" : "source candidate"}
|
||||
data-source-state-label={sourceIsPublished ? d.publishedRelease : d.sourceCandidate}
|
||||
>
|
||||
{publishedRelease ? (
|
||||
<Link href={`/${locale}/changelog`} className="section-link">
|
||||
{fill(pickText(HOME_OVERVIEW.releaseLabel, locale), { version: `v${publishedRelease.version}` })}
|
||||
{releaseDate ? <span className="home-proof-date"> · {releaseDate}</span> : null}
|
||||
</Link>
|
||||
) : (
|
||||
<Link href={`/${locale}/changelog`} className="section-link">{chrome.footerChangelog}</Link>
|
||||
)}
|
||||
<LiveStarLink initial={pulse.stats?.stars ?? undefined} label={pickText(PROJECT_COPY.statStars, locale)} href={REPO_URL} numberLocale={chrome.dateLocale} asOf={statsAsOf(pulse.stats, locale)} />
|
||||
<Link href={`/${locale}/contribute`} className="section-link">{fill(d.proofContributors, { count: facts.contributorCount })}</Link>
|
||||
<span>{facts.license ?? "MIT"}</span>
|
||||
<Link href={`/${locale}/roadmap`} className="section-link">{chrome.footerRoadmap}</Link>
|
||||
{showPromo && (
|
||||
<aside className="home-promo-bar home-promo-inline" aria-label={PARTNER_PROMO.title}>
|
||||
<div className="home-promo-bar-inner">
|
||||
<Image src={PARTNER_PROMO.image.src} alt="" width={64} height={36} className="home-promo-bar-art" unoptimized />
|
||||
<p>
|
||||
<strong>{PARTNER_PROMO.title}</strong>
|
||||
<span>{pickText(PARTNER_PROMO.bar, locale)}</span>
|
||||
<span className="home-promo-bar-days">{fill(pickText(promoDays === 1 ? PARTNER_PROMO.dayLeft : PARTNER_PROMO.daysLeft, locale), { count: promoDays })}</span>
|
||||
</p>
|
||||
<span className="home-promo-bar-tag">{pickText(PARTNER_PROMO.referralTag, locale)}</span>
|
||||
<p className="home-promo-bar-disclosure">{pickText(PARTNER_PROMO.disclosure, locale)}</p>
|
||||
<a href={PARTNER_PROMO.href} rel="sponsored noopener" className="btn btn-primary">
|
||||
{pickText(PARTNER_PROMO.cta, locale)}
|
||||
<Icon name="external" className="icon" />
|
||||
</a>
|
||||
</div>
|
||||
</aside>
|
||||
)}
|
||||
|
||||
<section className="home-section home-split home-split-flip" aria-labelledby="home-control">
|
||||
<div className="home-text">
|
||||
<h2 id="home-control" className="home-h2">{d.sectionControlHeading}</h2>
|
||||
<p className="home-body"><InlineCode text={d.sectionControlBody} /></p>
|
||||
</div>
|
||||
<figure className="home-figure">
|
||||
<div className="home-frame home-only-wide">{frame("cols56/06-receipt", d.frameSummaryReceipt)}</div>
|
||||
<div className="home-frame home-only-narrow">{frame("cols48/06b-receipt-tall", d.frameSummaryReceipt, RECEIPT_ROWS_NARROW)}</div>
|
||||
</figure>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<div className="home-body">
|
||||
<Section
|
||||
id="home-models"
|
||||
className="home-section"
|
||||
title={d.modelsHeading}
|
||||
scope={released.counts && publishedRelease ? fill(pickText(HOME_OVERVIEW.modelsCount, locale), { version: `v${publishedRelease.version}`, routes: released.counts.providerRoutes, models: released.counts.models }) : undefined}
|
||||
link={<Link href={`/${locale}/models`} className="section-link">{d.modelsLink}<Icon name="arrow-right" className="icon icon-flip" /></Link>}
|
||||
>
|
||||
<ul className="home-makers" role="list">
|
||||
{makers.map((maker) => <li key={maker} className="pill">{maker}</li>)}
|
||||
<li className="pill pill-quiet">{pickText(HOME_OVERVIEW.modelsAny, locale)}</li>
|
||||
</ul>
|
||||
{newest.length > 0 && (
|
||||
<div className="home-newest">
|
||||
<h3 className="home-newest-title">{pickText(HOME_OVERVIEW.modelsNewest, locale)}</h3>
|
||||
<ol className="home-newest-list" role="list">
|
||||
{newest.map((model) => (
|
||||
<li key={model.id}>
|
||||
<code>{model.id}</code>
|
||||
<span>{model.provider}</span>
|
||||
<section className="home-direction" aria-labelledby="home-direction">
|
||||
<div className="home-direction-head">
|
||||
<h2 id="home-direction" className="home-h3">{d.sectionDirectionHeading}</h2>
|
||||
<p className="home-body"><InlineCode text={d.sectionDirectionBody} /></p>
|
||||
</div>
|
||||
<RouteMap parts={parts} engine={d.mapHeading} label={d.sectionDirectionHeading} locale={locale} />
|
||||
</section>
|
||||
|
||||
<div className="home-sections">
|
||||
<section className="home-section home-split" aria-labelledby="home-open">
|
||||
<div className="home-text">
|
||||
<h2 id="home-open" className="home-h2">{d.sectionOpenHeading}</h2>
|
||||
<p className="home-body"><InlineCode text={d.sectionOpenBody} /></p>
|
||||
</div>
|
||||
<div className="home-people">
|
||||
<p className="home-count">
|
||||
{contributorsBefore.trim() ? <span className="home-count-label">{contributorsBefore.trim()}</span> : null}
|
||||
<a className="home-count-number tabular" href={`${REPO_URL}/blob/main/docs/CONTRIBUTORS.md`}>{contributors}</a>
|
||||
{contributorsAfter.trim() ? <span className="home-count-label">{contributorsAfter.trim()}</span> : null}
|
||||
</p>
|
||||
{accounts ? (
|
||||
<p className="home-accounts tabular" title={accountsAsOf}>
|
||||
{accounts}
|
||||
{accountsAsOf ? <span className="sr-only"> {accountsAsOf}</span> : null}
|
||||
</p>
|
||||
) : null}
|
||||
{merged.length > 0 ? (
|
||||
<ol className="home-merged" role="list" aria-label={pickText(PROJECT_COPY.mergedHeading, locale)}>
|
||||
{merged.map((pull) => (
|
||||
<li key={pull.number}>
|
||||
{/* eslint-disable-next-line @next/next/no-img-element */}
|
||||
<img src={avatar(pull.avatar, 48)} alt="" width={24} height={24} loading="lazy" decoding="async" />
|
||||
<span className="home-merged-login" dir="ltr">{pull.login}</span>
|
||||
<a className="home-merged-title" href={pull.url} dir="ltr" lang="en">{pull.title}</a>
|
||||
</li>
|
||||
))}
|
||||
</ol>
|
||||
</div>
|
||||
)}
|
||||
{showPromo && (
|
||||
<aside className="home-promo-bar home-promo-inline" aria-label={PARTNER_PROMO.title}>
|
||||
<div className="home-promo-bar-inner">
|
||||
<Image src={PARTNER_PROMO.image.src} alt="" width={64} height={36} className="home-promo-bar-art" unoptimized />
|
||||
<p>
|
||||
<strong>{PARTNER_PROMO.title}</strong>
|
||||
<span>{pickText(PARTNER_PROMO.bar, locale)}</span>
|
||||
<span className="home-promo-bar-days">{fill(pickText(promoDays === 1 ? PARTNER_PROMO.dayLeft : PARTNER_PROMO.daysLeft, locale), { count: promoDays })}</span>
|
||||
</p>
|
||||
<span className="home-promo-bar-tag">{pickText(PARTNER_PROMO.referralTag, locale)}</span>
|
||||
<p className="home-promo-bar-disclosure">{pickText(PARTNER_PROMO.disclosure, locale)}</p>
|
||||
<a href={PARTNER_PROMO.href} rel="sponsored noopener" className="btn btn-primary">
|
||||
{pickText(PARTNER_PROMO.cta, locale)}
|
||||
<Icon name="external" className="icon" />
|
||||
</a>
|
||||
</div>
|
||||
</aside>
|
||||
)}
|
||||
</Section>
|
||||
|
||||
<Section
|
||||
id="home-capabilities"
|
||||
className="home-section"
|
||||
title={pickText(PRODUCT_COPY.workHeading, locale)}
|
||||
link={<Link href={`/${locale}/features`} className="section-link">{chrome.navFeatures}<Icon name="arrow-right" className="icon icon-flip" /></Link>}
|
||||
>
|
||||
<dl className="home-work-patterns">
|
||||
{PRODUCT_COPY.workPatterns.slice(0, 3).map((pattern) => (
|
||||
<div key={pattern.href}>
|
||||
<dt>{pickText(pattern.title, locale)}</dt>
|
||||
<dd>
|
||||
<p>{pickText(pattern.body, locale)}</p>
|
||||
<Link className="section-link" href={pattern.href.startsWith("/") ? `/${locale}${pattern.href}` : pattern.href}><code>{pattern.command}</code></Link>
|
||||
</dd>
|
||||
</div>
|
||||
))}
|
||||
<div>
|
||||
<dt>{pickText(HOME_OVERVIEW.computerUseTitle, locale)} <span className="pill pill-quiet">{pickText(HOME_OVERVIEW.computerUseTag, locale)}</span></dt>
|
||||
<dd>
|
||||
<p>{pickText(HOME_OVERVIEW.computerUseBody, locale)}</p>
|
||||
<Link className="section-link" href={`/${locale}/computer-use`}>{getComputerUse(locale).setup}<Icon name="arrow-right" className="icon icon-flip" /></Link>
|
||||
</dd>
|
||||
</div>
|
||||
</dl>
|
||||
</Section>
|
||||
|
||||
<Section
|
||||
id="home-plugins"
|
||||
className="home-section"
|
||||
title={pickText(HOME_OVERVIEW.pluginsTitle, locale)}
|
||||
scope={pickText(HOME_OVERVIEW.pluginsBody, locale)}
|
||||
link={<Link href={`/${locale}/plugins`} className="section-link">{pickText(HOME_OVERVIEW.pluginsLink, locale)}<Icon name="arrow-right" className="icon icon-flip" /></Link>}
|
||||
/>
|
||||
|
||||
<Section
|
||||
id="home-community"
|
||||
className="home-section home-community"
|
||||
label={pickText(PROJECT_COPY.liveLabel, locale)}
|
||||
title={pickText(PROJECT_COPY.liveHeading, locale)}
|
||||
scope={pickText(PROJECT_COPY.liveScope, locale)}
|
||||
link={<Link href={`/${locale}/feed`} className="section-link">{pickText(PROJECT_COPY.liveLink, locale)}<Icon name="arrow-right" className="icon icon-flip" /></Link>}
|
||||
>
|
||||
<div className="pulse">
|
||||
<div className="pulse-side">
|
||||
<PulseStats stats={pulse.stats} contributors={facts.contributorCount} locale={locale} />
|
||||
</div>
|
||||
{pulse.merged.length > 0 ? (
|
||||
<div className="pulse-main">
|
||||
<h3 className="pulse-heading"><span className="status status-live"><span className="status-dot" aria-hidden="true" />{pickText(PROJECT_COPY.mergedHeading, locale)}</span></h3>
|
||||
<MergedList items={pulse.merged} locale={locale} />
|
||||
</div>
|
||||
) : (
|
||||
<MergedList items={pulse.merged} locale={locale} />
|
||||
)}
|
||||
) : null}
|
||||
</div>
|
||||
<FaceWall faces={pulse.faces} total={facts.contributorCount} locale={locale} />
|
||||
<div className="actions">
|
||||
<a href={`${REPO_ISSUES_URL}/new/choose`} className="btn btn-primary btn-lg">
|
||||
{pickText(PROJECT_COPY.loopFeedback, locale)}
|
||||
<Icon name="external" className="icon" />
|
||||
</a>
|
||||
<Link href={`/${locale}/contribute`} className="btn btn-ghost btn-lg">
|
||||
{pickText(PROJECT_COPY.loopHelp, locale)}
|
||||
<Icon name="arrow-right" className="icon icon-flip" />
|
||||
</Link>
|
||||
</div>
|
||||
</Section>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<section className="home-install stage sea-continues" aria-labelledby="home-install">
|
||||
<div className="home-install-inner">
|
||||
<h2 className="section-title" id="home-install">{d.startHeading}</h2>
|
||||
<div className="actions">
|
||||
<Link href={`/${locale}/install`} className="btn btn-primary btn-lg">
|
||||
{d.getCodewhale}
|
||||
<Icon name="arrow-right" className="icon icon-flip" />
|
||||
</Link>
|
||||
<Link href={`/${locale}/docs/guide`} className="btn btn-ghost btn-lg">{chrome.footerGuide}</Link>
|
||||
<section className="home-start" aria-labelledby="home-start">
|
||||
<div className="home-start-inner">
|
||||
<div className="home-text">
|
||||
<h2 id="home-start" className="home-h2">{d.sectionStartHeading}</h2>
|
||||
<p className="home-body"><InlineCode text={d.sectionStartBody} /></p>
|
||||
</div>
|
||||
{install}
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
+423
-248
@@ -1,187 +1,447 @@
|
||||
/* ------------------------------------------------------------------ */
|
||||
/* Home — paper above, sea below */
|
||||
/* Home */
|
||||
/* ------------------------------------------------------------------ */
|
||||
/*
|
||||
* Paper holds the promise, capabilities and reading sections.
|
||||
* Drawn water rises behind the live whale and real terminal capture under
|
||||
* the hero. The page ends in the sea with the install action, which runs
|
||||
* on into the footer.
|
||||
* The first screen does one job: say what Codewhale is, show it running and
|
||||
* give the install command. Proof follows in the order a person meets the
|
||||
* product, and what is not released yet is one calm section further down,
|
||||
* drawn as a route whose line style, station shape and word carry each state.
|
||||
*/
|
||||
|
||||
.home { --home-measure: 36rem; }
|
||||
.home-intro { position: relative; isolation: isolate; overflow: clip; }
|
||||
.home-water {
|
||||
--strata-haze: rgb(30 143 216 / 0.08);
|
||||
--strata-1: rgb(30 143 216 / 0.12);
|
||||
--strata-2: rgb(30 143 216 / 0.16);
|
||||
--strata-3: rgb(11 72 187 / 0.14);
|
||||
--strata-4: #cfe2f6;
|
||||
--strata-line: var(--brand-light);
|
||||
--strata-thread: var(--brand-deep);
|
||||
position: absolute;
|
||||
inset: 0 0 auto;
|
||||
height: min(64rem, 100%);
|
||||
z-index: -1;
|
||||
pointer-events: none;
|
||||
-webkit-mask-image: linear-gradient(180deg, #000 0, #000 70%, transparent 100%);
|
||||
mask-image: linear-gradient(180deg, #000 0, #000 70%, transparent 100%);
|
||||
}
|
||||
.strata { display: block; width: 100%; height: 100%; pointer-events: none; }
|
||||
:root[data-theme="dark"] .home-water {
|
||||
--strata-1: rgb(30 143 216 / 0.08);
|
||||
--strata-2: rgb(11 72 187 / 0.18);
|
||||
--strata-3: rgb(11 72 187 / 0.28);
|
||||
--strata-4: var(--ocean-sky);
|
||||
--strata-line: var(--gpui-dark-accent);
|
||||
}
|
||||
|
||||
/* ---------- paper: the hero ---------- */
|
||||
.home-hero {
|
||||
.home {
|
||||
--home-gap: clamp(5rem, 9vw, 8rem);
|
||||
--home-band: #dceef3;
|
||||
--whale-w: 4.5rem;
|
||||
--whale-rise: var(--brand-light);
|
||||
--whale-deep: var(--brand-deep);
|
||||
--whale-pouch: #dcebf7;
|
||||
--whale-pleat: var(--brand-deep);
|
||||
--whale-eye: #0b1424;
|
||||
position: relative;
|
||||
padding-block: clamp(2rem, 3vw, 2.5rem) 0;
|
||||
overflow-x: clip;
|
||||
}
|
||||
:root[data-theme="dark"] .home {
|
||||
--home-band: #071326;
|
||||
--whale-rise: #3594d8;
|
||||
--whale-deep: #2b70d5;
|
||||
--whale-pouch: #dcecf8;
|
||||
--whale-pleat: #2b70d5;
|
||||
}
|
||||
|
||||
.home-hero-inner {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(0, 1.25fr) minmax(0, 0.75fr);
|
||||
align-items: end;
|
||||
gap: var(--space-6) clamp(2rem, 6vw, 5rem);
|
||||
width: var(--container);
|
||||
margin-inline: auto;
|
||||
}
|
||||
body:has(.home) { --ground: var(--home-sky); }
|
||||
body:has(.home) .site-nav { border-bottom-color: transparent; }
|
||||
|
||||
.home-hero-copy {
|
||||
display: grid;
|
||||
gap: var(--space-4);
|
||||
padding-bottom: clamp(1.5rem, 3vw, 2rem);
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
/* Type is the hero's structure: large, tight, set in the brand face. */
|
||||
/* Measured in em, not ch, so CJK and long compound words wrap the same as
|
||||
English; hyphenation lets German compounds break at syllables. */
|
||||
/* ---------- type ---------- */
|
||||
.home-title {
|
||||
max-width: 11.25em;
|
||||
font-size: clamp(2.5rem, 1.4rem + 3.6vw, 3.75rem);
|
||||
hyphens: auto;
|
||||
font-weight: 650;
|
||||
line-height: 0.98;
|
||||
letter-spacing: -0.035em;
|
||||
margin: 0;
|
||||
font-size: clamp(2.375rem, 5.28vw, 4.75rem);
|
||||
line-height: 1.053;
|
||||
letter-spacing: -0.03em;
|
||||
text-wrap: balance;
|
||||
color: var(--text);
|
||||
}
|
||||
.home-title > span { display: block; }
|
||||
.home-title-lead { font-weight: 420; color: var(--muted); }
|
||||
.home-title-answer { font-weight: 680; color: var(--text); }
|
||||
:lang(zh) .home-title,
|
||||
:lang(ja) .home-title,
|
||||
:lang(ko) .home-title { line-height: 1.16; letter-spacing: -0.01em; line-break: strict; }
|
||||
|
||||
:lang(en) .home-title { hyphens: none; }
|
||||
|
||||
.home-lede {
|
||||
max-width: var(--home-measure);
|
||||
color: var(--muted);
|
||||
font-size: clamp(1.0625rem, 1rem + 0.2vw, 1.125rem);
|
||||
.home-subhead {
|
||||
max-width: 38rem;
|
||||
margin: 1.5rem 0 0;
|
||||
color: var(--text);
|
||||
font-size: clamp(1.0625rem, 1.39vw, 1.25rem);
|
||||
line-height: 1.55;
|
||||
text-wrap: pretty;
|
||||
}
|
||||
.home-lede strong { color: var(--text); font-weight: 600; }
|
||||
|
||||
/* CJK headlines carry more meaning per glyph: a step smaller, wider measure,
|
||||
and no breaks inside words (Korean) or mid-phrase where avoidable. */
|
||||
:lang(zh) .home-title,
|
||||
:lang(ja) .home-title,
|
||||
:lang(ko) .home-title {
|
||||
max-width: 11em;
|
||||
font-size: clamp(2.125rem, 1.2rem + 3.4vw, 4rem);
|
||||
line-height: 1.12;
|
||||
letter-spacing: -0.01em;
|
||||
word-break: keep-all;
|
||||
line-break: strict;
|
||||
.home-h2 {
|
||||
margin: 0;
|
||||
font-size: clamp(1.875rem, 3.33vw, 3rem);
|
||||
font-weight: 650;
|
||||
line-height: 1.083;
|
||||
letter-spacing: -0.02em;
|
||||
text-wrap: balance;
|
||||
}
|
||||
.home-h3 {
|
||||
margin: 0;
|
||||
font-size: clamp(1.375rem, 2vw, 1.75rem);
|
||||
font-weight: 650;
|
||||
line-height: 1.2;
|
||||
letter-spacing: -0.015em;
|
||||
}
|
||||
:lang(zh) .home-h2,
|
||||
:lang(ja) .home-h2,
|
||||
:lang(ko) .home-h2 { line-height: 1.2; letter-spacing: -0.01em; }
|
||||
.home-body {
|
||||
max-width: 36em;
|
||||
margin: 1.25rem 0 0;
|
||||
color: var(--text);
|
||||
font-size: 1.125rem;
|
||||
line-height: 1.667;
|
||||
text-wrap: pretty;
|
||||
}
|
||||
.home-caption {
|
||||
max-width: 40em;
|
||||
margin: 0;
|
||||
color: var(--muted);
|
||||
font-size: var(--text-label);
|
||||
line-height: 1.44;
|
||||
}
|
||||
|
||||
.home-hero-copy .hero-install { max-width: 35rem; margin-top: var(--space-2); }
|
||||
.home-hero-copy .actions { margin-top: calc(var(--space-2) * -1); }
|
||||
|
||||
/* The whale rests above the terminal at the waterline. */
|
||||
.home-whale {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-self: end;
|
||||
min-width: 0;
|
||||
}
|
||||
.home-whale .living-whale {
|
||||
--whale-size: clamp(18rem, 36vw, 29rem);
|
||||
width: var(--whale-size);
|
||||
margin-bottom: calc(var(--whale-size) * -0.26);
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
.living-whale {
|
||||
color: var(--accent);
|
||||
position: relative;
|
||||
aspect-ratio: 1;
|
||||
}
|
||||
.living-whale-lights,
|
||||
.living-whale-mark {
|
||||
position: absolute;
|
||||
inset: 14%;
|
||||
width: 72%;
|
||||
height: 72%;
|
||||
pointer-events: none;
|
||||
}
|
||||
.living-whale-lights { color: light-dark(#647e9b, #c8e6f3); }
|
||||
.living-whale-mark {
|
||||
background: var(--ink);
|
||||
mask: url("/brand/mark-mono.svg") center / contain no-repeat;
|
||||
opacity: 0;
|
||||
transition: opacity 960ms linear;
|
||||
}
|
||||
.living-whale[data-stage="mark"] .living-whale-mark { opacity: 1; }
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
.living-whale-mark { display: none; transition: none; }
|
||||
}
|
||||
|
||||
/* The install band ends the page in the sea: one line of light at its top,
|
||||
and water that reaches the floor colour by its own bottom edge, so it runs
|
||||
into the footer without a seam. */
|
||||
.home-install {
|
||||
position: relative;
|
||||
background: var(--sea-band), var(--ocean-floor);
|
||||
}
|
||||
.home-install::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
inset: 0 0 auto;
|
||||
height: 1px;
|
||||
background: var(--horizon-light);
|
||||
}
|
||||
|
||||
/* ---------- the product, then what backs it ---------- */
|
||||
.home-terminal {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
.home-terminal-inner {
|
||||
width: var(--container);
|
||||
margin-inline: auto;
|
||||
padding-block: clamp(2rem, 4vw, 3rem) clamp(2.5rem, 6vw, 4.5rem);
|
||||
}
|
||||
.home-shot { max-width: 64rem; margin-inline: auto; }
|
||||
.home-shot .figure-frame {
|
||||
background: var(--surface);
|
||||
border-color: color-mix(in srgb, var(--brand-light) 35%, var(--line));
|
||||
box-shadow: 0 24px 60px -28px rgb(11 72 187 / 0.3);
|
||||
}
|
||||
|
||||
/* Release, licence and the contributor count read from facts, never typed into copy. */
|
||||
.home-proof {
|
||||
/* ---------- the install box ---------- */
|
||||
.home-install { min-width: 0; }
|
||||
.home-version {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
gap: var(--space-2) var(--space-6);
|
||||
padding-top: var(--space-5);
|
||||
gap: 0.25rem 1rem;
|
||||
margin: var(--space-2) 0 0;
|
||||
padding-inline-start: 2px;
|
||||
color: var(--muted);
|
||||
font-size: var(--text-ui);
|
||||
font-variant-numeric: tabular-nums;
|
||||
font-size: var(--text-label);
|
||||
line-height: 1.44;
|
||||
}
|
||||
.home-version-link {
|
||||
color: var(--text);
|
||||
font-weight: 550;
|
||||
text-decoration: underline;
|
||||
text-decoration-color: var(--line-strong);
|
||||
text-underline-offset: 0.2em;
|
||||
}
|
||||
.home-version-link:hover { text-decoration-color: currentColor; }
|
||||
.home .hero-install {
|
||||
position: relative;
|
||||
gap: var(--space-2);
|
||||
padding: var(--space-2);
|
||||
background: var(--panel);
|
||||
border-radius: var(--radius-surface);
|
||||
text-align: start;
|
||||
}
|
||||
.home .hero-install .segmented {
|
||||
justify-self: start;
|
||||
max-width: calc(100% - 3rem);
|
||||
padding: 0;
|
||||
background: transparent;
|
||||
border-color: transparent;
|
||||
}
|
||||
.home .hero-install .segmented button { min-height: 2.25rem; white-space: nowrap; }
|
||||
.home .hero-install .code { position: static; }
|
||||
.home .hero-install .code pre.code-block {
|
||||
padding-right: var(--space-4);
|
||||
border-radius: var(--radius-control);
|
||||
}
|
||||
.home .hero-install .code .code-copy {
|
||||
position: absolute;
|
||||
top: var(--space-2);
|
||||
right: var(--space-2);
|
||||
display: inline-flex;
|
||||
min-height: 2.25rem;
|
||||
margin: 0;
|
||||
background: transparent;
|
||||
box-shadow: none;
|
||||
transform: none;
|
||||
}
|
||||
.home .hero-install .code .code-copy:hover { background: var(--hover); }
|
||||
[dir="rtl"] .home .hero-install .code .code-copy { right: auto; left: var(--space-2); }
|
||||
.home .hero-install .install-head-note { padding-inline: 2px; }
|
||||
@media (max-width: 35rem) {
|
||||
.home .hero-install { padding: 0.375rem; }
|
||||
.home .hero-install .code .code-copy { top: 0.375rem; right: 0.375rem; }
|
||||
[dir="rtl"] .home .hero-install .code .code-copy { right: auto; left: 0.375rem; }
|
||||
.home .hero-install .code pre.code-block { padding-inline: 0.375rem; font-size: 0.75rem; }
|
||||
.home .hero-install .code pre.code-block-single { padding-inline-start: calc(0.375rem + 2ch); }
|
||||
}
|
||||
|
||||
/* ---------- the first screen ---------- */
|
||||
.home-hero { width: var(--container); margin-inline: auto; padding-top: clamp(2.5rem, 6vw, 5.5rem); }
|
||||
.home-hero-shot { min-width: 0; margin-top: clamp(2.5rem, 5vw, 4rem); }
|
||||
|
||||
.home-hero[data-hero="center"] .home-hero-text { display: grid; justify-items: center; text-align: center; }
|
||||
.home-hero[data-hero="center"] .home-subhead { margin-inline: auto; }
|
||||
.home-hero[data-hero="center"] .home-install { width: min(100%, 36rem); margin-top: 2rem; }
|
||||
.home-hero[data-hero="center"] .home-version { justify-content: center; }
|
||||
.home-hero[data-hero="center"] .home-session { max-width: 60rem; margin-inline: auto; }
|
||||
.home-hero[data-hero="center"] .home-session-tabs { justify-content: center; }
|
||||
.home-hero[data-hero="center"] .home-session-caption { justify-items: center; text-align: center; }
|
||||
|
||||
/* The facts: short, linked, and every number from data. */
|
||||
.home-facts {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: center;
|
||||
gap: 0.75rem 2.5rem;
|
||||
margin: clamp(2.5rem, 5vw, 3.5rem) 0 0;
|
||||
padding: 1.25rem 0;
|
||||
list-style: none;
|
||||
border-block: 1px solid var(--line);
|
||||
}
|
||||
.home-facts a {
|
||||
display: inline-flex;
|
||||
align-items: baseline;
|
||||
gap: 0.3em;
|
||||
min-height: 2.75rem;
|
||||
align-items: center;
|
||||
color: var(--muted);
|
||||
font-size: 1rem;
|
||||
font-weight: 500;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.home-facts a:hover { color: var(--text); }
|
||||
.home-facts strong { color: var(--text); font-weight: 650; }
|
||||
|
||||
/* ---------- the terminal session ---------- */
|
||||
.home-frame {
|
||||
min-width: 0;
|
||||
overflow: hidden;
|
||||
border-radius: var(--radius-surface);
|
||||
box-shadow: 0 0 0 1px var(--line);
|
||||
}
|
||||
.home-frame .term-capture-scroll { background: none; }
|
||||
.home-frame .term-capture { padding: 1.1em 1.3em; font-size: calc(100cqi / (var(--cols, 100) * 0.61 + 2.6)); }
|
||||
.home-frame .term-capture-scroll:focus-visible { outline-offset: -3px; }
|
||||
.home-figure { display: grid; gap: var(--space-3); min-width: 0; margin: 0; }
|
||||
|
||||
.home-session { display: grid; gap: var(--space-3); min-width: 0; margin: 0; }
|
||||
.home-session-tabs { display: flex; flex-wrap: wrap; gap: 0 var(--space-1); border-bottom: 1px solid var(--line); }
|
||||
.home-session-tabs [role="tab"] {
|
||||
min-height: 2.75rem;
|
||||
margin-bottom: -1px;
|
||||
padding-inline: var(--space-3);
|
||||
color: var(--muted);
|
||||
font-size: 0.9375rem;
|
||||
font-weight: 500;
|
||||
border-bottom: 2px solid transparent;
|
||||
cursor: pointer;
|
||||
transition: color var(--dur-state) var(--ease-out);
|
||||
}
|
||||
.home-session-tabs [role="tab"]:hover { color: var(--text); }
|
||||
.home-session-tabs [role="tab"][aria-selected="true"] { color: var(--text); font-weight: 650; border-bottom-color: var(--accent); }
|
||||
.home-session-caption { display: grid; gap: var(--space-2); }
|
||||
@media (forced-colors: active) {
|
||||
.home-session-tabs [role="tab"][aria-selected="true"] { border-color: Highlight; }
|
||||
}
|
||||
|
||||
.home-only-wide { display: none; }
|
||||
|
||||
/* ---------- the sections ---------- */
|
||||
.home-sections,
|
||||
.home-direction-head,
|
||||
.home-start-inner,
|
||||
.road-tree { width: var(--container); margin-inline: auto; }
|
||||
.home-section { padding-top: var(--home-gap); }
|
||||
.home-figure,
|
||||
.home-people { margin-top: 2.5rem; }
|
||||
|
||||
/* ---------- what is on the way ---------- */
|
||||
.road { position: relative; }
|
||||
.road-stops { margin: 0; padding: 0; list-style: none; }
|
||||
.road-name { color: var(--text); font-size: 0.9375rem; font-weight: 600; line-height: 1.25; }
|
||||
.road-state {
|
||||
display: inline-flex;
|
||||
align-items: flex-start;
|
||||
gap: 0.375rem;
|
||||
color: var(--text);
|
||||
font-size: 0.8125rem;
|
||||
font-weight: 500;
|
||||
line-height: 1.3;
|
||||
}
|
||||
.road-state[data-state="released"] { color: var(--live); }
|
||||
.road-state[data-state="beingBuilt"] { color: var(--muted); }
|
||||
.road-mark { flex: none; width: 0.625rem; height: 0.625rem; margin-top: 0.2em; overflow: visible; }
|
||||
.road-mark-fill { fill: currentColor; }
|
||||
.road-mark-ring { fill: none; stroke: currentColor; stroke-width: 1.5; }
|
||||
.road-mark-dashed { stroke-dasharray: 2 2; }
|
||||
|
||||
.road-rail { position: relative; height: 1.5rem; }
|
||||
.road-drawing { position: absolute; inset: 0; width: 100%; height: 100%; overflow: visible; color: var(--text); }
|
||||
.road-line { fill: none; stroke: currentColor; stroke-width: 2; }
|
||||
.road-line[data-line="released"] { stroke-width: 3; }
|
||||
.road-line[data-line="developing"] { stroke-dasharray: 12 6; }
|
||||
.road-line[data-line="inviteOnly"] { stroke-dasharray: 4 4; }
|
||||
.road-line[data-line="beingBuilt"] { stroke-dasharray: 1 5; stroke-linecap: round; }
|
||||
.road-line[data-line="beingBuilt"],
|
||||
.road-station[data-state="beingBuilt"],
|
||||
.road-fade[data-state="beingBuilt"] { color: var(--muted); }
|
||||
.road-fade-near { stop-color: currentColor; }
|
||||
.road-fade-far { stop-color: currentColor; stop-opacity: 0; }
|
||||
[dir="rtl"] .road-drawing,
|
||||
[dir="rtl"] .road-whale { transform: scaleX(-1); }
|
||||
|
||||
.road-today {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
inset-inline-end: calc(100% - var(--today));
|
||||
width: var(--whale-w);
|
||||
transform: translateY(-73%);
|
||||
pointer-events: none;
|
||||
}
|
||||
.road-whale { display: block; width: 100%; height: auto; overflow: visible; }
|
||||
.road-whale-rise { stop-color: var(--whale-rise); }
|
||||
.road-whale-deep { stop-color: var(--whale-deep); }
|
||||
.road-whale-pouch { fill: var(--whale-pouch); }
|
||||
.road-whale-pleat-1,
|
||||
.road-whale-pleat-2 { fill: var(--whale-pleat); opacity: 0.78; }
|
||||
.road-whale-eye { fill: var(--whale-eye); transform-box: fill-box; transform-origin: center; }
|
||||
.road-whale-flukes { transform-box: view-box; transform-origin: 27.7px 18.6px; }
|
||||
.road-whale-swim { transform-box: view-box; transform-origin: 87px 31px; }
|
||||
|
||||
.home-direction {
|
||||
margin-top: var(--home-gap);
|
||||
padding-block: clamp(3.5rem, 7vw, 5.5rem);
|
||||
background: linear-gradient(180deg, var(--bg), var(--home-band) 50%, var(--bg));
|
||||
}
|
||||
.home-direction-head .home-body { max-width: 40em; font-size: 1rem; }
|
||||
.road-map { display: none; }
|
||||
.road-sentence { display: block; color: var(--text); font-size: 0.875rem; line-height: 1.6; }
|
||||
|
||||
.road-tree { margin-top: 2rem; }
|
||||
.road-tree-root { margin: 0; color: var(--text); font-size: 0.9375rem; font-weight: 650; line-height: 1.25; }
|
||||
.road-tree ul { margin: var(--space-2) 0 0; padding: 0; list-style: none; }
|
||||
.road-tree li { position: relative; padding-block: 0.625rem; padding-inline-start: 1.5rem; }
|
||||
.road-tree li::before,
|
||||
.road-tree li::after { content: ""; position: absolute; inset-inline-start: 0.3125rem; background: var(--line-strong); }
|
||||
.road-tree li::before { top: 0; bottom: 0; width: 1px; }
|
||||
.road-tree li::after { top: 1.25rem; width: 0.75rem; height: 1px; }
|
||||
.road-tree li:last-child::before { bottom: auto; height: 1.25rem; }
|
||||
.road-tree-line { display: flex; align-items: baseline; gap: var(--space-2); }
|
||||
.road-tree-line .road-name { flex: none; max-width: 60%; }
|
||||
.road-tree-line .road-state { flex: 0 1 auto; min-width: 0; text-align: end; }
|
||||
.road-tree-leader { flex: 1 1 1rem; min-width: 1rem; height: 0; border-bottom: 2px dotted var(--line-strong); }
|
||||
.road-tree .road-sentence { margin-top: var(--space-1); }
|
||||
a.road-name {
|
||||
text-decoration: underline;
|
||||
text-decoration-color: var(--line-strong);
|
||||
text-decoration-thickness: 1px;
|
||||
text-underline-offset: 0.2em;
|
||||
}
|
||||
a.road-name:hover { text-decoration-color: currentColor; }
|
||||
|
||||
/* ---------- built in the open ---------- */
|
||||
.home-count { display: flex; flex-wrap: wrap; align-items: baseline; gap: 0 var(--space-3); margin: 0; }
|
||||
.home-count-number {
|
||||
color: var(--text);
|
||||
font-size: clamp(4.5rem, 8.33vw, 7.5rem);
|
||||
font-weight: 300;
|
||||
line-height: 1;
|
||||
letter-spacing: -0.04em;
|
||||
font-variant-numeric: tabular-nums;
|
||||
}
|
||||
.home-count-label { color: var(--muted); font-size: 1.125rem; font-weight: 500; }
|
||||
.home-accounts { max-width: 30em; margin: var(--space-4) 0 0; color: var(--text); font-size: 1rem; line-height: 1.5; }
|
||||
.home-merged { margin: var(--space-5) 0 0; padding: 0; list-style: none; border-top: 1px solid var(--line); }
|
||||
.home-merged li {
|
||||
display: grid;
|
||||
grid-template-columns: auto minmax(0, 1fr);
|
||||
align-items: center;
|
||||
gap: 0 var(--space-3);
|
||||
padding-block: var(--space-2);
|
||||
border-bottom: 1px solid var(--line);
|
||||
font-family: var(--font-mono);
|
||||
font-size: var(--text-mono);
|
||||
line-height: 1.5;
|
||||
}
|
||||
.home-merged img {
|
||||
grid-row: 1 / span 2;
|
||||
width: 1.5rem;
|
||||
height: 1.5rem;
|
||||
background: var(--panel);
|
||||
border-radius: var(--radius-pill);
|
||||
box-shadow: 0 0 0 1px var(--line-strong);
|
||||
}
|
||||
.home-merged-login { min-width: 0; color: var(--muted); }
|
||||
.home-merged-title { grid-column: 2; color: var(--text); overflow-wrap: anywhere; }
|
||||
[dir="rtl"] .home-merged-login,
|
||||
[dir="rtl"] .home-merged-title { text-align: right; }
|
||||
.home-merged-title:hover { text-decoration: underline; text-underline-offset: 0.2em; }
|
||||
|
||||
/* ---------- the close ---------- */
|
||||
.home-start { padding-block: var(--home-gap); }
|
||||
.home-start .home-install { margin-top: 2rem; }
|
||||
|
||||
/* ---------- phone and tablet ---------- */
|
||||
@media (max-width: 63.99rem) {
|
||||
.home-session,
|
||||
.home-figure { max-width: 34rem; }
|
||||
.home-hero .home-session { max-width: 34rem; margin-inline: auto; }
|
||||
.home-install { max-width: 34rem; }
|
||||
}
|
||||
@media (max-width: 40rem) {
|
||||
.home-title { font-size: 2.375rem; line-height: 1.105; }
|
||||
.home-body { font-size: 1.0625rem; line-height: 1.647; }
|
||||
.home-facts { justify-content: flex-start; gap: 0 1.5rem; padding-block: 0.5rem; }
|
||||
.home-facts li { flex: 1 1 40%; }
|
||||
.home-facts a { font-size: 0.9375rem; white-space: normal; }
|
||||
.home .hero-install .code .code-copy-label {
|
||||
position: absolute;
|
||||
width: 1px;
|
||||
height: 1px;
|
||||
overflow: hidden;
|
||||
clip-path: inset(50%);
|
||||
white-space: nowrap;
|
||||
}
|
||||
.home .hero-install .code .code-copy { width: 2.5rem; min-height: 2.5rem; padding: 0; }
|
||||
.home .hero-install .segmented button { min-height: 2.5rem; padding-inline: 0.5rem; }
|
||||
.home-count-number { font-size: 4.5rem; }
|
||||
}
|
||||
|
||||
/* ---------- desktop ---------- */
|
||||
@media (min-width: 64rem) {
|
||||
.home-only-wide { display: block; }
|
||||
.home-only-narrow { display: none; }
|
||||
|
||||
.home-split,
|
||||
.home-start-inner {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(12, minmax(0, 1fr));
|
||||
column-gap: 1.5rem;
|
||||
align-items: start;
|
||||
}
|
||||
.home-split > .home-text { grid-column: 1 / span 6; }
|
||||
.home-split > .home-figure,
|
||||
.home-split > .home-people { grid-column: 8 / -1; margin-top: 0.5rem; }
|
||||
.home-split-flip > .home-text { grid-column: 8 / -1; grid-row: 1; }
|
||||
.home-split-flip > .home-figure { grid-column: 1 / span 6; grid-row: 1; max-width: 33rem; }
|
||||
|
||||
.road-tree { display: none; }
|
||||
.road-map { display: block; width: var(--container); margin: 3rem auto 0; }
|
||||
.road-map .road-stops { display: grid; grid-template-rows: auto auto auto; margin-top: 1.125rem; }
|
||||
.road-map .road-stop {
|
||||
display: grid;
|
||||
grid-row: span 3;
|
||||
grid-template-rows: subgrid;
|
||||
row-gap: 0.375rem;
|
||||
justify-items: start;
|
||||
min-width: 0;
|
||||
padding-inline-end: 1.25rem;
|
||||
}
|
||||
.road-map .road-name { align-self: end; }
|
||||
.road-map .road-rail { margin-inline-start: 0.375rem; }
|
||||
.road-map .road-sentence { margin-top: 0.25rem; }
|
||||
|
||||
.home-merged li { grid-template-columns: auto minmax(0, 9rem) minmax(0, 1fr); }
|
||||
.home-merged img { grid-row: auto; }
|
||||
.home-merged-login { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
||||
.home-merged-title { grid-column: auto; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
||||
|
||||
.home-start-inner > .home-text { grid-column: 1 / span 6; }
|
||||
.home-start-inner > .home-install { grid-column: 8 / -1; margin-top: 0.5rem; }
|
||||
}
|
||||
|
||||
/* ---------- motion ---------- */
|
||||
@keyframes road-fluke { 0%, 100% { transform: rotate(-7deg); } 50% { transform: rotate(7deg); } }
|
||||
@keyframes road-bob { 0%, 100% { transform: translateY(-0.9px) rotate(-0.6deg); } 50% { transform: translateY(0.9px) rotate(0.6deg); } }
|
||||
@keyframes road-blink { 0%, 93%, 100% { transform: scaleY(1); } 95% { transform: scaleY(0.1); } }
|
||||
|
||||
@media (prefers-reduced-motion: no-preference) {
|
||||
.road-whale-flukes { animation: road-fluke 2.4s ease-in-out infinite; }
|
||||
.road-whale-swim { animation: road-bob 4.2s ease-in-out infinite; }
|
||||
.road-whale-eye { animation: road-blink 4.6s linear infinite; }
|
||||
}
|
||||
|
||||
@media (forced-colors: active) {
|
||||
.home-direction { background: none; }
|
||||
}
|
||||
|
||||
/* ---------- release cards (the changelog's update block) ---------- */
|
||||
.release-cards {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
@@ -203,44 +463,10 @@
|
||||
.release-links a { display: inline-flex; align-items: center; min-height: 2.75rem; }
|
||||
.release-update-error { margin-top: var(--space-4); color: var(--muted); font-size: var(--text-ui); }
|
||||
.page-body > .release-updates { padding-block-start: 0; margin-block-end: var(--section-gap); }
|
||||
|
||||
/* ---------- paper: the reading sections ---------- */
|
||||
.home-body {
|
||||
width: var(--container);
|
||||
margin-inline: auto;
|
||||
padding-block: clamp(4.5rem, 9vw, 7rem);
|
||||
}
|
||||
.home-body > .home-section { padding-block: 0; }
|
||||
.home-body > .home-section + .home-section {
|
||||
margin-top: clamp(2.5rem, 6vw, 5rem);
|
||||
padding-top: clamp(2.5rem, 6vw, 5rem);
|
||||
border-top: 1px solid var(--line);
|
||||
@media (max-width: 900px) {
|
||||
.release-cards { grid-template-columns: minmax(0, 1fr); }
|
||||
}
|
||||
|
||||
.home-work-patterns {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(4, minmax(0, 1fr));
|
||||
gap: var(--space-6);
|
||||
margin-top: var(--space-7);
|
||||
}
|
||||
.home-work-patterns dt { font-size: var(--text-heading); font-weight: 600; }
|
||||
.home-work-patterns dd { display: grid; gap: var(--space-4); margin: var(--space-3) 0 0; color: var(--muted); line-height: 1.6; }
|
||||
.home-work-patterns code { font-size: var(--text-ui); overflow-wrap: anywhere; }
|
||||
.home-work-patterns dt .pill { margin-inline-start: var(--space-2); vertical-align: middle; }
|
||||
|
||||
.home-makers { display: flex; flex-wrap: wrap; gap: var(--space-2); margin-top: var(--space-6); }
|
||||
.home-makers .pill { padding: 0.3rem 0.75rem; font-size: var(--text-ui); }
|
||||
.pill-quiet { color: var(--muted); background: transparent; border-style: dashed; }
|
||||
.home-newest { margin-top: var(--space-7); }
|
||||
.home-newest-title { margin-bottom: var(--space-3); color: var(--muted); font-size: var(--text-label); font-weight: 600; }
|
||||
.home-newest-list { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 0 var(--space-6); border-top: 1px solid var(--line-strong); }
|
||||
.home-newest-list li { display: flex; justify-content: space-between; gap: var(--space-3); padding-block: var(--space-3); border-bottom: 1px solid var(--line); font-size: var(--text-ui); }
|
||||
.home-newest-list code { color: var(--text); overflow-wrap: anywhere; }
|
||||
.home-newest-list span { color: var(--muted); white-space: nowrap; }
|
||||
.home-proof-stars { display: inline-flex; align-items: center; gap: var(--space-1); }
|
||||
.home-proof-date { color: var(--muted); }
|
||||
|
||||
/* ---------- partner offer: a dated strip in the models section (English only, §46) ---------- */
|
||||
.home-promo-bar { border-bottom: 1px solid var(--line); background: var(--surface); }
|
||||
.home-promo-inline { margin-top: var(--space-7); border: 1px solid var(--line); border-radius: var(--radius-surface); }
|
||||
.home-promo-inline .home-promo-bar-inner { width: auto; padding: var(--space-4) var(--space-5); }
|
||||
@@ -265,54 +491,3 @@
|
||||
.home-promo-bar-inner { gap: var(--space-2) var(--space-3); padding-block: var(--space-2); }
|
||||
.home-promo-bar p { flex-basis: 100%; }
|
||||
}
|
||||
|
||||
/* Terms read as words, not as code: the brand face, a little weight. */
|
||||
.home-body .ruled-list dt {
|
||||
font-family: var(--font-body);
|
||||
font-size: var(--text-body);
|
||||
font-weight: 600;
|
||||
letter-spacing: 0;
|
||||
color: var(--text);
|
||||
}
|
||||
|
||||
.product-start-links { display: flex; flex-wrap: wrap; gap: var(--space-3) var(--space-5); margin-top: var(--space-5); }
|
||||
|
||||
/* ---------- the sea: install, into the footer ---------- */
|
||||
.home-install-inner {
|
||||
display: grid;
|
||||
gap: var(--space-6);
|
||||
grid-template-columns: minmax(0, 1fr);
|
||||
align-items: center;
|
||||
width: var(--container);
|
||||
margin-inline: auto;
|
||||
padding-block: clamp(4.5rem, 9vw, 7rem) clamp(2.5rem, 5vw, 4rem);
|
||||
}
|
||||
@media (min-width: 900px) {
|
||||
.home-install-inner { grid-template-columns: minmax(0, 1fr) auto; }
|
||||
}
|
||||
|
||||
@media (min-width: 901px) and (max-width: 1200px) {
|
||||
.home-work-patterns { grid-template-columns: repeat(2, minmax(0, 1fr)); }
|
||||
.home-newest-list { grid-template-columns: repeat(2, minmax(0, 1fr)); }
|
||||
}
|
||||
|
||||
/* ---------- narrow screens ---------- */
|
||||
@media (max-width: 900px) {
|
||||
.home-hero-inner { grid-template-columns: minmax(0, 1fr); }
|
||||
.home-hero-copy { padding-bottom: var(--space-4); }
|
||||
.home-whale { justify-content: flex-end; }
|
||||
.home-whale .living-whale { --whale-size: clamp(11rem, 48vw, 16rem); }
|
||||
.home-work-patterns,
|
||||
.home-newest-list,
|
||||
.release-cards { grid-template-columns: minmax(0, 1fr); }
|
||||
.home-work-patterns > div + div { padding-top: var(--space-5); border-top: 1px solid var(--line); }
|
||||
}
|
||||
|
||||
@media (forced-colors: active) {
|
||||
.home-install::before { background: CanvasText; }
|
||||
.home-water { display: none; }
|
||||
}
|
||||
|
||||
.home-community .actions { margin-top: var(--space-7); }
|
||||
.home-install .actions { margin: 0; }
|
||||
|
||||
@@ -938,21 +938,6 @@ pre.gs-step-commands { margin-top: var(--space-1); font-size: var(--text-caption
|
||||
.session-media-caption strong { color: var(--text); font-size: var(--text-body); }
|
||||
.session-media-caption a { color: var(--accent); font-size: var(--text-caption); }
|
||||
|
||||
/* ---------- the horizon ---------- */
|
||||
/* One calm, empty horizon per page: a straight line where the sky meets the
|
||||
sea. Nothing stands on it but the whale. The sea below is the stage. */
|
||||
.horizon {
|
||||
position: relative;
|
||||
height: 1px;
|
||||
background: var(--horizon-light);
|
||||
}
|
||||
|
||||
.sea { background: var(--sea), var(--ocean-abyss); }
|
||||
|
||||
@media (forced-colors: active) {
|
||||
.horizon { background: CanvasText; }
|
||||
}
|
||||
|
||||
/* ---------- search field ---------- */
|
||||
.search-field { position: relative; }
|
||||
.search-field .search-input { padding-inline: 2.5rem 3rem; }
|
||||
|
||||
+48
-47
@@ -1,5 +1,5 @@
|
||||
/* ------------------------------------------------------------------ */
|
||||
/* Site chrome — the header, the mobile sheet, the footer sea */
|
||||
/* Site chrome — the header, the mobile sheet, the footer */
|
||||
/* ------------------------------------------------------------------ */
|
||||
|
||||
/* ---------- header ---------- */
|
||||
@@ -62,7 +62,6 @@
|
||||
-webkit-mask: url("/brand/mark-mono.svg") no-repeat center / contain;
|
||||
mask: url("/brand/mark-mono.svg") no-repeat center / contain;
|
||||
}
|
||||
.site-wordmark-footer .brand-mark { width: 2.5rem; height: 2.5rem; }
|
||||
|
||||
.paper-wordmark-logo,
|
||||
.wordmark {
|
||||
@@ -234,35 +233,14 @@
|
||||
}
|
||||
.mm-action.paper-install-cta { margin-top: 1.5rem; }
|
||||
|
||||
/* ---------- footer — the sea below the horizon ---------- */
|
||||
/* Every page ends on one horizon: a straight line of light, and below it
|
||||
the sea, settling into deep water within a few rows, in both appearances. */
|
||||
/* ---------- footer ---------- */
|
||||
/* The footer sits on the page's own ground under one hairline, in both
|
||||
appearances: four short link columns, folded into four disclosures on a
|
||||
phone. */
|
||||
.site-footer {
|
||||
position: relative;
|
||||
color: var(--muted);
|
||||
}
|
||||
/* A page whose main already ends in the sea (home) runs straight on: no
|
||||
second horizon, and the water continues from its floor colour. */
|
||||
main:has(.sea-continues:last-child) + .site-footer .site-footer-sea > .horizon { display: none; }
|
||||
main:has(.sea-continues:last-child) + .site-footer .site-footer-sea { background: var(--ocean-floor); }
|
||||
.site-footer-sea {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
background: var(--sea), var(--ocean-floor);
|
||||
}
|
||||
.site-footer-sea > .horizon { position: absolute; inset: 0 0 auto; z-index: 1; }
|
||||
.sea-texture {
|
||||
position: absolute;
|
||||
inset: 0 0 auto;
|
||||
height: min(22rem, 100%);
|
||||
pointer-events: none;
|
||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='210' height='18'%3E%3Cg stroke='%2390b9ff' stroke-width='1' stroke-linecap='round'%3E%3Cpath d='M4 5h30M60 5h14M112 5h40M170 5h8' stroke-opacity='.12'/%3E%3Cpath d='M30 14h22M88 14h36M140 14h10M186 14h20' stroke-opacity='.08'/%3E%3C/g%3E%3C/svg%3E");
|
||||
background-size: 210px 18px;
|
||||
-webkit-mask-image: linear-gradient(180deg, #000, transparent);
|
||||
mask-image: linear-gradient(180deg, #000, transparent);
|
||||
}
|
||||
@media (forced-colors: active) {
|
||||
.sea-texture { display: none; }
|
||||
border-top: 1px solid var(--line);
|
||||
}
|
||||
|
||||
.site-footer-main,
|
||||
@@ -274,31 +252,26 @@ main:has(.sea-continues:last-child) + .site-footer .site-footer-sea { background
|
||||
|
||||
.site-footer-main {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(14rem, 1fr) minmax(0, 2fr);
|
||||
grid-template-columns: minmax(12rem, 1fr) minmax(0, 2.5fr);
|
||||
gap: var(--space-7);
|
||||
padding-block: 5.5rem var(--space-7);
|
||||
padding-block: var(--space-8) var(--space-7);
|
||||
}
|
||||
|
||||
.site-footer-brand { display: grid; gap: var(--space-4); align-content: start; }
|
||||
.site-footer-brand p {
|
||||
max-width: 26rem;
|
||||
color: var(--muted);
|
||||
font-size: var(--text-ui);
|
||||
line-height: 1.6;
|
||||
}
|
||||
.site-wordmark-footer { color: var(--text); justify-self: start; gap: var(--space-3); }
|
||||
.site-footer-brand { display: grid; align-content: start; }
|
||||
.site-wordmark-footer { color: var(--text); justify-self: start; }
|
||||
|
||||
.site-footer-links {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(10rem, 1fr));
|
||||
grid-template-columns: repeat(4, minmax(0, 1fr));
|
||||
gap: var(--space-5);
|
||||
}
|
||||
.site-footer-folds { display: none; }
|
||||
|
||||
.site-footer-group { display: grid; gap: var(--space-2); align-content: start; }
|
||||
|
||||
.site-footer-label {
|
||||
color: var(--muted);
|
||||
font-size: var(--text-caption);
|
||||
font-size: var(--text-label);
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
@@ -317,6 +290,34 @@ main:has(.sea-continues:last-child) + .site-footer .site-footer-sea { background
|
||||
}
|
||||
.site-footer-list a:hover { background: var(--hover); }
|
||||
|
||||
.site-footer-fold { border-bottom: 1px solid var(--line); }
|
||||
.site-footer-fold:first-child { border-top: 1px solid var(--line); }
|
||||
.site-footer-fold summary {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
min-height: 3rem;
|
||||
color: var(--text);
|
||||
font-size: var(--text-body);
|
||||
font-weight: 500;
|
||||
list-style: none;
|
||||
cursor: pointer;
|
||||
}
|
||||
.site-footer-fold summary::-webkit-details-marker { display: none; }
|
||||
.site-footer-fold summary::after {
|
||||
content: "";
|
||||
width: 0.5rem;
|
||||
height: 0.5rem;
|
||||
margin-inline-end: 0.25rem;
|
||||
border-inline-end: 1.5px solid currentColor;
|
||||
border-block-end: 1.5px solid currentColor;
|
||||
transform: translateY(-25%) rotate(45deg);
|
||||
transition: transform var(--dur-state) var(--ease-out);
|
||||
}
|
||||
.site-footer-fold[open] summary::after { transform: translateY(25%) rotate(-135deg); }
|
||||
.site-footer-fold .site-footer-list { padding-bottom: var(--space-3); }
|
||||
.site-footer-fold .site-footer-list a { min-height: 2.75rem; font-size: var(--text-body); }
|
||||
|
||||
.site-footer-meta {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
@@ -326,9 +327,8 @@ main:has(.sea-continues:last-child) + .site-footer .site-footer-sea { background
|
||||
padding-block: var(--space-4) var(--space-6);
|
||||
border-top: 1px solid var(--line);
|
||||
color: var(--muted);
|
||||
font-size: var(--text-caption);
|
||||
font-size: var(--text-label);
|
||||
}
|
||||
.site-footer-meta p { min-width: 0; max-width: 70ch; overflow-wrap: anywhere; }
|
||||
.site-footer-meta a {
|
||||
color: var(--text);
|
||||
text-decoration: underline;
|
||||
@@ -336,17 +336,18 @@ main:has(.sea-continues:last-child) + .site-footer .site-footer-sea { background
|
||||
text-underline-offset: 0.2em;
|
||||
}
|
||||
.site-footer-meta a:hover { text-decoration-color: currentColor; }
|
||||
.site-footer-meta-links { display: flex; flex-wrap: wrap; gap: var(--space-2) var(--space-4); }
|
||||
|
||||
@media (max-width: 900px) {
|
||||
.site-footer-main { grid-template-columns: 1fr; gap: var(--space-6); }
|
||||
.site-footer-main { grid-template-columns: 1fr; gap: var(--space-6); padding-block: var(--space-7) var(--space-6); }
|
||||
}
|
||||
@media (max-width: 640px) {
|
||||
.site-footer-links { display: none; }
|
||||
.site-footer-folds { display: block; }
|
||||
.site-footer-meta { border-top: 0; padding-top: 0; }
|
||||
}
|
||||
|
||||
@media print {
|
||||
.site-footer { color: inherit; border-top: 1px solid currentColor; }
|
||||
.site-footer-sea { background: none; }
|
||||
.site-footer > .waterline,
|
||||
.site-footer-sea > .horizon { display: none; }
|
||||
}
|
||||
|
||||
/* ---------- usage counting: the privacy page's control ---------- */
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
* importing desktop colors.
|
||||
*
|
||||
* Filled actions use the primary blue; links, focus and selection carry
|
||||
* the accent. The sea below the horizon stays navy in both appearances.
|
||||
* the accent. The footer sits on the page's own ground in both appearances.
|
||||
*
|
||||
* Depth is elevation. --panel is raised (nearer the surface, lighter);
|
||||
* --bg is the floor; --surface is recessed (code, rails, fields).
|
||||
@@ -31,30 +31,17 @@
|
||||
--kit-mark-navy: #142352;
|
||||
--kit-abyss: #0f1e2d;
|
||||
--kit-deep-navy: #142638;
|
||||
--kit-ocean-depth: #173b50;
|
||||
--kit-night-surface: #20374e;
|
||||
--kit-night-border: #31495e;
|
||||
--ocean-abyss: var(--kit-abyss);
|
||||
--ocean-floor: var(--kit-deep-navy);
|
||||
--ocean-raised: var(--kit-ocean-depth);
|
||||
--ocean-hover: var(--kit-night-surface);
|
||||
--ocean-selected: var(--kit-night-border);
|
||||
--ocean-line: var(--kit-night-border);
|
||||
--ocean-line-strong: var(--gpui-dark-border-strong);
|
||||
--ocean-sky: var(--kit-mark-navy);
|
||||
--brand-light: var(--kit-current-blue);
|
||||
--brand-deep: var(--kit-whale-blue);
|
||||
--brand-mid: #1877c9;
|
||||
--brand-fill: linear-gradient(135deg, var(--brand-mid), var(--brand-deep));
|
||||
--brand-fill-hover: linear-gradient(135deg, var(--brand-deep), var(--kit-mark-navy));
|
||||
--on-brand: var(--gpui-dark-on-primary);
|
||||
--sea-surface: var(--brand-deep);
|
||||
--sea-shallow: #0f3991;
|
||||
--sea-mid: var(--kit-mark-navy);
|
||||
--sea: linear-gradient(180deg, var(--sea-shallow) 0, var(--sea-mid) 5rem, var(--ocean-floor) 22rem);
|
||||
--sea-band: linear-gradient(180deg, var(--sea-shallow) 0, var(--sea-mid) 40%, var(--ocean-floor) 100%);
|
||||
--horizon-light: color-mix(in srgb, var(--gpui-dark-accent) 85%, transparent);
|
||||
--horizon-glow: var(--horizon-light);
|
||||
--ombre: var(--gpui-ombre);
|
||||
|
||||
--font-body: var(--font-geist-sans), var(--gpui-font-fallbacks), ui-sans-serif, system-ui,
|
||||
@@ -157,6 +144,7 @@
|
||||
--diff-add: var(--gpui-light-diff-add);
|
||||
--diff-del: var(--gpui-light-diff-remove);
|
||||
--ground: var(--gpui-water-light) top center / 100% 38rem no-repeat, var(--bg);
|
||||
--home-sky: var(--gpui-surface-light-light) top center / 100% 38rem no-repeat, var(--ground);
|
||||
--glow: 0 0 0 3px color-mix(in srgb, currentColor 16%, transparent);
|
||||
--glow-focus: 0 0 0 4px color-mix(in srgb, var(--gpui-light-accent) 16%, transparent);
|
||||
--shadow-popover: var(--gpui-elevation-popover-light);
|
||||
@@ -190,6 +178,7 @@
|
||||
--diff-add: var(--gpui-dark-diff-add);
|
||||
--diff-del: var(--gpui-dark-diff-remove);
|
||||
--ground: var(--gpui-water-dark) top center / 100% 38rem no-repeat, var(--bg);
|
||||
--home-sky: var(--gpui-surface-light-dark) top center / 100% 38rem no-repeat, var(--ground);
|
||||
--glow: 0 0 0 3px color-mix(in srgb, currentColor 20%, transparent), 0 0 12px 1px color-mix(in srgb, currentColor 45%, transparent);
|
||||
--glow-focus: 0 0 0 4px color-mix(in srgb, var(--gpui-dark-accent) 30%, transparent);
|
||||
--shadow-popover: var(--gpui-elevation-popover-dark);
|
||||
@@ -197,67 +186,10 @@
|
||||
color-scheme: dark;
|
||||
}
|
||||
|
||||
/* ---------- the stage — below the horizon, in both appearances ---------- */
|
||||
/* The footer and the home install band. Legacy aliases are repeated here
|
||||
because a custom property that references another is computed where it is
|
||||
declared. */
|
||||
.stage,
|
||||
.site-footer {
|
||||
--bg: var(--ocean-floor);
|
||||
--surface: var(--ocean-abyss);
|
||||
--panel: var(--ocean-raised);
|
||||
--text: var(--gpui-dark-text);
|
||||
--muted: var(--gpui-dark-text-muted);
|
||||
--line: var(--ocean-line);
|
||||
--line-strong: var(--ocean-line-strong);
|
||||
--accent: var(--gpui-dark-accent);
|
||||
--accent-hover: color-mix(in srgb, var(--gpui-dark-accent) calc(var(--gpui-primary-hover-opacity) * 100%), transparent);
|
||||
--on-accent: var(--gpui-dark-bg);
|
||||
|
||||
--hover: var(--ocean-hover);
|
||||
--selected: var(--ocean-selected);
|
||||
--selection: color-mix(in srgb, var(--gpui-dark-accent) calc(var(--gpui-selection-opacity) * 100%), transparent);
|
||||
--ring: var(--gpui-dark-accent);
|
||||
--live: var(--gpui-dark-live);
|
||||
--success: var(--gpui-dark-live);
|
||||
--attention: var(--gpui-dark-attention);
|
||||
--danger: var(--gpui-dark-danger);
|
||||
--mark-ink: var(--gpui-dark-text);
|
||||
--ombre-ink: linear-gradient(135deg, var(--gpui-dark-accent), var(--brand-light));
|
||||
--diff-add: var(--gpui-dark-diff-add);
|
||||
--diff-del: var(--gpui-dark-diff-remove);
|
||||
--glow: 0 0 0 3px color-mix(in srgb, currentColor 20%, transparent), 0 0 12px 1px color-mix(in srgb, currentColor 45%, transparent);
|
||||
--glow-focus: 0 0 0 4px color-mix(in srgb, var(--gpui-dark-accent) 30%, transparent);
|
||||
--shadow-popover: var(--gpui-elevation-popover-dark);
|
||||
|
||||
--paper: var(--bg);
|
||||
--paper-deep: var(--surface);
|
||||
--paper-card: var(--panel);
|
||||
--paper-edge: var(--line);
|
||||
--paper-line: var(--line);
|
||||
--paper-line-soft: var(--line);
|
||||
--hairline: var(--line);
|
||||
--ink: var(--text);
|
||||
--ink-soft: var(--muted);
|
||||
--ink-mute: var(--muted);
|
||||
--indigo: var(--accent);
|
||||
--indigo-deep: var(--accent-hover);
|
||||
--indigo-pale: color-mix(in srgb, var(--accent) 14%, transparent);
|
||||
--docs-accent: var(--accent);
|
||||
--code-bg: var(--surface);
|
||||
--code-fg: var(--text);
|
||||
color-scheme: dark;
|
||||
color: var(--text);
|
||||
}
|
||||
|
||||
/* The page's own ground, resolved here on :root so it survives into regions
|
||||
(the footer, the stage) that re-declare --bg. */
|
||||
:root { --page-ground: var(--bg); }
|
||||
|
||||
/* ---------- legacy names ---------- */
|
||||
/* Pages and partials that have not moved to the roles (the docs) read these.
|
||||
Declared once on :root, they resolve against whichever scheme block wins
|
||||
there; `.stage` repeats them. Removed in the post-renovation sweep. */
|
||||
there. Removed in the post-renovation sweep. */
|
||||
:root {
|
||||
--paper: var(--bg);
|
||||
--paper-deep: var(--surface);
|
||||
@@ -286,5 +218,4 @@
|
||||
--code-bg: var(--surface);
|
||||
--code-fg: var(--text);
|
||||
--stage-line: var(--line);
|
||||
--stage-dim: var(--muted);
|
||||
}
|
||||
@@ -4,8 +4,7 @@
|
||||
|
||||
/* ---------- focus + selection ---------- */
|
||||
/* The one focus treatment on the site: a 2px ring at a 3px offset in the
|
||||
accent of whatever depth the control sits in (the stage re-declares
|
||||
--ring). Components may add the bioluminescent halo (--glow-focus) as a
|
||||
accent. Components may add the bioluminescent halo (--glow-focus) as a
|
||||
box-shadow; none restyles or removes the ring. */
|
||||
::selection { background: var(--selection); }
|
||||
:focus-visible { outline: var(--gpui-focus-width) solid var(--ring); outline-offset: var(--gpui-focus-offset); }
|
||||
|
||||
@@ -390,6 +390,8 @@
|
||||
--gpui-breakpoint-wide: 1200px;
|
||||
--gpui-water-dark: linear-gradient(180deg, #0b1e3a 0%, #071326 35%, #04090f 75%, #030508 100%);
|
||||
--gpui-water-light: linear-gradient(180deg, #bfe1f2 0%, #dceef3 30%, #e7f0f6 62%, #f1f5f9 100%);
|
||||
--gpui-surface-light-dark: radial-gradient(ellipse at 55% 0%, rgb(40 150 230 / 0.32) 0%, rgb(11 72 187 / 0.1) 45%, rgb(11 72 187 / 0) 75%);
|
||||
--gpui-surface-light-light: radial-gradient(ellipse at 55% 0%, rgb(255 255 255 / 0.85) 0%, rgb(255 255 255 / 0.25) 40%, rgb(255 255 255 / 0) 72%);
|
||||
--gpui-ombre: linear-gradient(135deg, #1775c9, #0b48bb);
|
||||
--gpui-font-fallbacks: "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", "Noto Sans CJK SC", "Noto Sans SC", "Source Han Sans SC";
|
||||
--gpui-mono-size: 0.8125rem;
|
||||
|
||||
+47
-54
@@ -1,81 +1,74 @@
|
||||
import Link from "next/link";
|
||||
import { GITEE_ENABLED, type Locale } from "@/lib/i18n/config";
|
||||
import type { Locale } from "@/lib/i18n/config";
|
||||
import { getChrome } from "@/lib/i18n/dictionaries";
|
||||
import {
|
||||
footerDocsLinks,
|
||||
footerLegalLinks,
|
||||
footerProductLinks,
|
||||
footerProjectLinks,
|
||||
type ChromeLink,
|
||||
} from "@/lib/i18n/links";
|
||||
import { SITE_CONTACT_EMAIL } from "@/lib/page-meta";
|
||||
import { reviewState } from "@/lib/i18n/review-state";
|
||||
import { USAGE_COUNTING_COPY } from "@/lib/content/usage-counting";
|
||||
import { pickText } from "@/lib/i18n/dictionaries";
|
||||
|
||||
function FooterList({ links }: { links: ChromeLink[] }) {
|
||||
return (
|
||||
<div className="site-footer-list">
|
||||
{links.map((item) => <Link key={item.href} href={item.href}>{item.label}</Link>)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Site footer: the sea below one horizon line, in both appearances.
|
||||
* One dictionary path for every routed locale; four short link columns
|
||||
* (Product, Docs, Project, Legal) everywhere.
|
||||
* Site footer: the page's own ground under one hairline, in both
|
||||
* appearances. One dictionary path for every routed locale; four short link
|
||||
* columns (Product, Docs, Project, Legal), which fold into four disclosures
|
||||
* on a phone.
|
||||
*/
|
||||
export function Footer({ locale = "en" }: { locale?: Locale }) {
|
||||
const chrome = getChrome(locale);
|
||||
const homeHref = `/${locale}`;
|
||||
const product = footerProductLinks(locale, chrome);
|
||||
const docs = footerDocsLinks(locale, chrome);
|
||||
const project = footerProjectLinks(locale, chrome);
|
||||
const legal = footerLegalLinks(locale, chrome, pickText(USAGE_COUNTING_COPY.footerLink, locale));
|
||||
const groups: { label: string; links: ChromeLink[] }[] = [
|
||||
{ label: chrome.footerProduct, links: footerProductLinks(locale, chrome) },
|
||||
{ label: chrome.footerDocs, links: footerDocsLinks(locale, chrome) },
|
||||
{ label: chrome.footerProject, links: footerProjectLinks(locale, chrome) },
|
||||
{ label: chrome.footerLegal, links: footerLegalLinks(locale, chrome, pickText(USAGE_COUNTING_COPY.footerLink, locale)) },
|
||||
];
|
||||
|
||||
return (
|
||||
<footer className="site-footer">
|
||||
<div className="site-footer-sea">
|
||||
<div className="horizon" aria-hidden="true" />
|
||||
<div className="sea-texture" aria-hidden="true" />
|
||||
<div className="site-footer-main">
|
||||
<div className="site-footer-brand">
|
||||
<Link href={homeHref} className="site-wordmark site-wordmark-footer" aria-label="Codewhale">
|
||||
<span className="brand-mark" aria-hidden="true" />
|
||||
<span className="wordmark" aria-hidden="true" />
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
<div className="site-footer-links">
|
||||
<div className="site-footer-group">
|
||||
<span className="site-footer-label">{chrome.footerProduct}</span>
|
||||
<div className="site-footer-list">
|
||||
{product.map((item) => <Link key={item.href} href={item.href}>{item.label}</Link>)}
|
||||
</div>
|
||||
</div>
|
||||
<div className="site-footer-group">
|
||||
<span className="site-footer-label">{chrome.footerDocs}</span>
|
||||
<div className="site-footer-list">
|
||||
{docs.map((item) => <Link key={item.href} href={item.href}>{item.label}</Link>)}
|
||||
</div>
|
||||
</div>
|
||||
<div className="site-footer-group">
|
||||
<span className="site-footer-label">{chrome.footerProject}</span>
|
||||
<div className="site-footer-list">
|
||||
{project.map((item) => <Link key={item.href} href={item.href}>{item.label}</Link>)}
|
||||
</div>
|
||||
</div>
|
||||
<div className="site-footer-group">
|
||||
<span className="site-footer-label">{chrome.footerLegal}</span>
|
||||
<div className="site-footer-list">
|
||||
{legal.map((item) => <Link key={item.href} href={item.href}>{item.label}</Link>)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="site-footer-main">
|
||||
<div className="site-footer-brand">
|
||||
<Link href={homeHref} className="site-wordmark site-wordmark-footer" aria-label="Codewhale">
|
||||
<span className="brand-mark" aria-hidden="true" />
|
||||
<span className="wordmark" aria-hidden="true" />
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
<div className="site-footer-meta">
|
||||
<div className="site-footer-meta-links">
|
||||
{GITEE_ENABLED && <a href="https://gitee.com/codewhale-hq/CodeWhale">Gitee</a>}
|
||||
<a href="https://cnb.cool/codewhale.net/codewhale">CNB</a>
|
||||
<a href="https://npmmirror.com/package/codewhale">npmmirror</a>
|
||||
<a href={`mailto:${SITE_CONTACT_EMAIL}`}>{SITE_CONTACT_EMAIL}</a>
|
||||
</div>
|
||||
<span>© {new Date().getFullYear()} Codewhale</span>
|
||||
{reviewState(locale) === "machine" && <span className="site-footer-review">{chrome.translationMachineDraft}</span>}
|
||||
<div className="site-footer-links">
|
||||
{groups.map((group) => (
|
||||
<div key={group.label} className="site-footer-group">
|
||||
<span className="site-footer-label">{group.label}</span>
|
||||
<FooterList links={group.links} />
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
<div className="site-footer-folds">
|
||||
{groups.map((group) => (
|
||||
<details key={group.label} className="site-footer-fold" name="site-footer">
|
||||
<summary>{group.label}</summary>
|
||||
<FooterList links={group.links} />
|
||||
</details>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="site-footer-meta">
|
||||
<a href={`mailto:${SITE_CONTACT_EMAIL}`}>{SITE_CONTACT_EMAIL}</a>
|
||||
<span>© {new Date().getFullYear()} Codewhale</span>
|
||||
{reviewState(locale) === "machine" && <span className="site-footer-review">{chrome.translationMachineDraft}</span>}
|
||||
</div>
|
||||
</footer>
|
||||
);
|
||||
|
||||
@@ -11,42 +11,3 @@ export function LiveStarCount({ initial, numberLocale, asOf }: { initial?: numbe
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
||||
export interface PulseRow { key: "stars" | "contributors" | "pulls" | "issues"; label: string; href: string; value?: string; asOf?: string }
|
||||
|
||||
export function LiveStarLink({ initial, label, href, numberLocale, asOf }: { initial?: number; label: string; href: string; numberLocale: string; asOf?: string }) {
|
||||
if (initial === undefined) return null;
|
||||
const formatted = new Intl.NumberFormat(numberLocale).format(initial);
|
||||
return (
|
||||
<a href={href} className="section-link home-proof-stars" title={asOf} aria-label={asOf ? `${formatted} ${label}, ${asOf}` : undefined}>
|
||||
<span aria-hidden="true">★</span>
|
||||
<span className="tabular">{formatted}</span>
|
||||
<span>{label}</span>
|
||||
</a>
|
||||
);
|
||||
}
|
||||
|
||||
export function LivePulseStats({ rows }: { rows: PulseRow[]; numberLocale?: string }) {
|
||||
return (
|
||||
<dl className="pulse-stats">
|
||||
{rows.map((row) => {
|
||||
if (row.value === undefined) return null;
|
||||
return (
|
||||
<div key={row.key}>
|
||||
<dt>{row.label}</dt>
|
||||
<dd>
|
||||
<a
|
||||
href={row.href}
|
||||
className="tabular"
|
||||
title={row.asOf}
|
||||
aria-label={row.asOf ? `${row.value} ${row.label}, ${row.asOf}` : undefined}
|
||||
>
|
||||
{row.value}
|
||||
</a>
|
||||
</dd>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</dl>
|
||||
);
|
||||
}
|
||||
@@ -1,182 +0,0 @@
|
||||
"use client";
|
||||
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
import drones from "@/vendor/whale-lights/whale-drones.json";
|
||||
|
||||
type Stage = "mark" | "assembling" | "rest";
|
||||
const MARK_MS = 650;
|
||||
const ASSEMBLE_MS = 960;
|
||||
const REPEAT_MS = 720;
|
||||
const FRAME_MS = 1000 / 15;
|
||||
const SEEN = "codewhale.living-mark";
|
||||
const TAU = 2 * Math.PI;
|
||||
|
||||
export function LivingHeroWhale({ locale = "en" }: { locale?: string }) {
|
||||
const [stage, setStage] = useState<Stage>("mark");
|
||||
const root = useRef<HTMLDivElement>(null);
|
||||
const canvas = useRef<HTMLCanvasElement>(null);
|
||||
const cue = useRef(-Infinity);
|
||||
|
||||
useEffect(() => {
|
||||
const element = root.current;
|
||||
const surface = canvas.current;
|
||||
const context = surface?.getContext("2d");
|
||||
if (!element || !surface || !context) return;
|
||||
const motion = window.matchMedia("(prefers-reduced-motion: reduce)");
|
||||
let seen = false;
|
||||
try { seen = sessionStorage.getItem(SEEN) === "1"; } catch {}
|
||||
let current: Stage = motion.matches ? "rest" : seen ? "assembling" : "mark";
|
||||
setStage(current);
|
||||
let elapsed = motion.matches ? MARK_MS + ASSEMBLE_MS : 0;
|
||||
let previous = 0;
|
||||
let painted = 0;
|
||||
let frame = 0;
|
||||
let visible = true;
|
||||
let focused = document.hasFocus();
|
||||
let blurAt = focused ? Infinity : performance.now();
|
||||
let blurTimer: ReturnType<typeof setTimeout> | undefined;
|
||||
let density = drones.launch[0];
|
||||
let size = 0;
|
||||
let body = "";
|
||||
let throat = "";
|
||||
const points: { x: number; y: number; kind: string; spine: number }[] = [];
|
||||
const hold = seen ? 0 : MARK_MS;
|
||||
const duration = seen ? REPEAT_MS : ASSEMBLE_MS;
|
||||
const updateStage = (next: Stage) => {
|
||||
if (next === current) return;
|
||||
if (next === "rest") {
|
||||
try { sessionStorage.setItem(SEEN, "1"); } catch {}
|
||||
}
|
||||
current = next;
|
||||
setStage(next);
|
||||
};
|
||||
const paint = () => {
|
||||
context.clearRect(0, 0, size, size);
|
||||
if (current === "mark") return;
|
||||
const still = motion.matches;
|
||||
const progress = still ? 1 : Math.min(1, Math.max(0, (elapsed - hold) / duration));
|
||||
const time = still ? 0 : elapsed / 1000;
|
||||
const glow = still ? 1 : 0.87 + 0.13 * Math.sin(TAU * time / 4.2);
|
||||
const hovered = !still && elapsed - cue.current < 300;
|
||||
const crest = !still && elapsed - cue.current < 1200 ? (elapsed - cue.current) / 1200 : (time / 6.1) % 1;
|
||||
const scale = size / 124;
|
||||
const radius = density.pitch * drones.radius * scale;
|
||||
for (let index = 0; index < points.length; index += 1) {
|
||||
const point = points[index];
|
||||
const sweep = seen ? Math.min(1, Math.hypot(point.x, point.y) / 60) : point.spine;
|
||||
const lit = Math.max(0, Math.min(1, (progress - sweep * 0.85) / 0.15));
|
||||
if (!lit) continue;
|
||||
const shine = still ? 0 : Math.max(0, 1 - Math.abs(point.spine - crest) / 0.12);
|
||||
const hover = still ? 0 : density.pitch * 0.12;
|
||||
const dx = hover * Math.sin(time + index * 1.7);
|
||||
const dy = hover * Math.cos(time * 0.8 + index * 2.3);
|
||||
context.globalAlpha = lit * Math.min(1, (0.7 + 0.2 * shine + (hovered ? 0.1 : 0)) * glow);
|
||||
context.fillStyle = point.kind === "t" || point.kind === "p" ? throat : body;
|
||||
context.beginPath();
|
||||
context.arc((point.x + dx + 62) * scale, (point.y + dy + 62) * scale, radius, 0, TAU);
|
||||
context.fill();
|
||||
}
|
||||
context.globalAlpha = 1;
|
||||
};
|
||||
const active = () => visible && !document.hidden && (focused || performance.now() - blurAt < 2500);
|
||||
const tick = (now: number) => {
|
||||
frame = 0;
|
||||
if (!active()) { previous = 0; return; }
|
||||
if (previous) elapsed += now - previous;
|
||||
previous = now;
|
||||
if (motion.matches) {
|
||||
updateStage("rest");
|
||||
paint();
|
||||
previous = 0;
|
||||
return;
|
||||
}
|
||||
updateStage(elapsed < hold ? "mark" : elapsed < hold + duration ? "assembling" : "rest");
|
||||
if (current === "assembling" || now - painted >= FRAME_MS) {
|
||||
painted = now;
|
||||
paint();
|
||||
}
|
||||
frame = requestAnimationFrame(tick);
|
||||
};
|
||||
const wake = () => {
|
||||
if (!frame && active()) frame = requestAnimationFrame(tick);
|
||||
};
|
||||
const resize = () => {
|
||||
const bounds = surface.getBoundingClientRect();
|
||||
const dpr = window.devicePixelRatio || 1;
|
||||
size = Math.round(bounds.width * dpr);
|
||||
surface.width = size;
|
||||
surface.height = size;
|
||||
density = drones.launch[0];
|
||||
for (const candidate of drones.launch) {
|
||||
if (candidate.pitch * size / 124 >= drones.minPitchPx) density = candidate;
|
||||
}
|
||||
points.length = 0;
|
||||
density.rows.forEach((row, y) => [...row].forEach((kind, x) => {
|
||||
if (kind === "." || kind === "e") return;
|
||||
const px = density.origin[0] + (x + 0.5) * density.pitch;
|
||||
const py = density.origin[1] + (y + 0.5) * density.pitch;
|
||||
const angle = (Math.atan2(py, px) * 180 / Math.PI + 360) % 360;
|
||||
points.push({ x: px, y: py, kind, spine: Math.min(1, ((angle - 340 + 360) % 360) / 335) });
|
||||
}));
|
||||
const style = getComputedStyle(element);
|
||||
body = style.color;
|
||||
throat = getComputedStyle(surface).color;
|
||||
paint();
|
||||
wake();
|
||||
};
|
||||
const focus = () => { focused = true; clearTimeout(blurTimer); previous = 0; wake(); };
|
||||
const blur = () => {
|
||||
focused = false;
|
||||
blurAt = performance.now();
|
||||
blurTimer = setTimeout(() => { if (frame) cancelAnimationFrame(frame); frame = 0; previous = 0; }, 2500);
|
||||
};
|
||||
const visibility = () => {
|
||||
if (document.hidden) { if (frame) cancelAnimationFrame(frame); frame = 0; previous = 0; }
|
||||
else wake();
|
||||
};
|
||||
const preference = () => {
|
||||
if (motion.matches) { elapsed = Math.max(elapsed, hold + duration); updateStage("rest"); }
|
||||
paint();
|
||||
previous = 0;
|
||||
wake();
|
||||
};
|
||||
const intersection = new IntersectionObserver((entries) => {
|
||||
visible = entries.some(entry => entry.isIntersecting);
|
||||
if (!visible) { if (frame) cancelAnimationFrame(frame); frame = 0; previous = 0; }
|
||||
else wake();
|
||||
});
|
||||
const observer = new ResizeObserver(resize);
|
||||
const appearance = new MutationObserver(resize);
|
||||
intersection.observe(element);
|
||||
observer.observe(surface);
|
||||
appearance.observe(document.documentElement, { attributes: true, attributeFilter: ["class", "data-theme", "style"] });
|
||||
window.addEventListener("focus", focus);
|
||||
window.addEventListener("blur", blur);
|
||||
document.addEventListener("visibilitychange", visibility);
|
||||
motion.addEventListener("change", preference);
|
||||
const interaction = () => { cue.current = elapsed; wake(); };
|
||||
element.addEventListener("pointerenter", interaction);
|
||||
element.addEventListener("focus", interaction);
|
||||
resize();
|
||||
return () => {
|
||||
if (frame) cancelAnimationFrame(frame);
|
||||
clearTimeout(blurTimer);
|
||||
observer.disconnect();
|
||||
intersection.disconnect();
|
||||
appearance.disconnect();
|
||||
window.removeEventListener("focus", focus);
|
||||
window.removeEventListener("blur", blur);
|
||||
document.removeEventListener("visibilitychange", visibility);
|
||||
motion.removeEventListener("change", preference);
|
||||
element.removeEventListener("pointerenter", interaction);
|
||||
element.removeEventListener("focus", interaction);
|
||||
};
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div ref={root} className="living-whale" data-stage={stage} lang={locale} aria-hidden="true">
|
||||
<span className="living-whale-mark" />
|
||||
<canvas ref={canvas} className="living-whale-lights" />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,8 +1,8 @@
|
||||
import { Icon } from "./icon";
|
||||
import { LivePulseStats, LiveStarCount, type PulseRow } from "./github-live";
|
||||
import { LiveStarCount } from "./github-live";
|
||||
import { GITHUB_STAR_COPY } from "@/lib/content/github-star";
|
||||
import { PROJECT_COPY } from "@/lib/content/project";
|
||||
import { formatStars, relativeAge, type Face } from "@/lib/github";
|
||||
import { relativeAge, type Face } from "@/lib/github";
|
||||
import { fill, getChrome, pickText } from "@/lib/i18n/dictionaries";
|
||||
import { REPO_URL } from "@/lib/i18n/links";
|
||||
import type { FeedItem, RepoStats } from "@/lib/types";
|
||||
@@ -24,17 +24,6 @@ export function statsAsOf(stats: RepoStats | null, locale: string): string | und
|
||||
return readAsOf(stats?.fetchedAt, locale);
|
||||
}
|
||||
|
||||
export function PulseStats({ stats, contributors, locale }: { stats: RepoStats | null; contributors: number; locale: string }) {
|
||||
const asOf = statsAsOf(stats, locale);
|
||||
const rows: PulseRow[] = [
|
||||
{ key: "stars", label: pickText(PROJECT_COPY.statStars, locale), href: REPO_URL, value: stats?.stars != null ? formatStars(stats.stars) : undefined, asOf },
|
||||
{ key: "contributors", label: pickText(PROJECT_COPY.statContributors, locale), href: `${REPO_URL}/blob/main/docs/CONTRIBUTORS.md`, value: String(contributors) },
|
||||
{ key: "pulls", label: pickText(PROJECT_COPY.statPulls, locale), href: `${REPO_URL}/pulls`, value: stats?.openPulls != null ? String(stats.openPulls) : undefined, asOf },
|
||||
{ key: "issues", label: pickText(PROJECT_COPY.statIssues, locale), href: `${REPO_URL}/issues`, value: stats?.openIssues != null ? String(stats.openIssues) : undefined, asOf },
|
||||
];
|
||||
return <LivePulseStats rows={rows} numberLocale={getChrome(locale).dateLocale} />;
|
||||
}
|
||||
|
||||
export function MergedList({ items, locale }: { items: FeedItem[]; locale: string; limit?: number }) {
|
||||
if (items.length === 0) return null;
|
||||
return (
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
import { Fragment } from "react";
|
||||
|
||||
export function InlineCode({ text }: { text: string }) {
|
||||
return (
|
||||
<>
|
||||
{text.split("`").map((part, index) =>
|
||||
index % 2 === 1 ? (
|
||||
<code key={index} className="inline" dir="ltr">
|
||||
{part}
|
||||
</code>
|
||||
) : (
|
||||
<Fragment key={index}>{part}</Fragment>
|
||||
),
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,200 @@
|
||||
import Link from "next/link";
|
||||
import { useId, type CSSProperties, type ReactNode } from "react";
|
||||
import type { ProductMapId, ProductMapState } from "@/lib/content/product-map";
|
||||
import { InlineCode } from "./inline-code";
|
||||
import { RouteWhale } from "./route-whale";
|
||||
|
||||
export interface RoutePart {
|
||||
id: ProductMapId;
|
||||
state: ProductMapState;
|
||||
name: string;
|
||||
stateWord: string;
|
||||
sentence: string | null;
|
||||
href: string | null;
|
||||
}
|
||||
|
||||
type MarkState = ProductMapState | "engine";
|
||||
|
||||
interface Geometry {
|
||||
stops: { x: number; state: MarkState }[];
|
||||
today: number;
|
||||
solidEnd: number;
|
||||
}
|
||||
|
||||
const pct = (value: number) => `${(value * 100).toFixed(3)}%`;
|
||||
const vars = (values: Record<string, string | number>) => values as CSSProperties;
|
||||
|
||||
function releasedCount(parts: readonly RoutePart[]): number {
|
||||
const first = parts.findIndex((part) => part.state !== "released");
|
||||
return first < 0 ? parts.length : first;
|
||||
}
|
||||
|
||||
function geometryFrom(xs: readonly number[], parts: readonly RoutePart[], split: number): Geometry {
|
||||
const today = releasedCount(parts);
|
||||
const next = today + 1 < xs.length ? xs[today + 1] : 1;
|
||||
const solidEnd = xs[today] + (next - xs[today]) * split;
|
||||
return {
|
||||
stops: xs.map((x, index) => ({ x, state: index === 0 ? "engine" : parts[index - 1].state })),
|
||||
today: solidEnd,
|
||||
solidEnd: today === parts.length ? 1 : solidEnd,
|
||||
};
|
||||
}
|
||||
|
||||
function mapGeometry(parts: readonly RoutePart[]): Geometry {
|
||||
const count = parts.length + 1;
|
||||
return geometryFrom(Array.from({ length: count }, (_, index) => index / count), parts, 0.5);
|
||||
}
|
||||
|
||||
function StateMark({ state }: { state: MarkState }) {
|
||||
return (
|
||||
<svg className="road-mark" data-state={state} viewBox="-6 -6 12 12" aria-hidden="true" focusable="false">
|
||||
<MarkShape state={state} />
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
function MarkShape({ state }: { state: MarkState }) {
|
||||
if (state === "engine" || state === "released") return <circle r="5" className="road-mark-fill" />;
|
||||
if (state === "developing") {
|
||||
return (
|
||||
<>
|
||||
<circle r="4.25" className="road-mark-ring" />
|
||||
<path d="M0 -4.25a4.25 4.25 0 0 0 0 8.5z" className="road-mark-fill" />
|
||||
</>
|
||||
);
|
||||
}
|
||||
if (state === "inviteOnly") {
|
||||
return (
|
||||
<>
|
||||
<circle r="4.25" className="road-mark-ring" />
|
||||
<circle r="1.75" className="road-mark-fill" />
|
||||
</>
|
||||
);
|
||||
}
|
||||
return <circle r="4.25" className="road-mark-ring road-mark-dashed" />;
|
||||
}
|
||||
|
||||
function Rail({ geometry }: { geometry: Geometry }) {
|
||||
const id = useId().replace(/[^a-z0-9]/gi, "");
|
||||
const mask = `road-mask-${id}`;
|
||||
const fade = `road-fade-${id}`;
|
||||
const { stops, solidEnd } = geometry;
|
||||
const tail = stops[stops.length - 1];
|
||||
const lines: ReactNode[] = [];
|
||||
stops.forEach((stop, index) => {
|
||||
const end = index + 1 < stops.length ? stops[index + 1].x : 1;
|
||||
const into: MarkState = index + 1 < stops.length ? stops[index + 1].state : stop.state;
|
||||
const line = (from: number, to: number, state: MarkState, key: string) => (
|
||||
<line
|
||||
key={key}
|
||||
className="road-line"
|
||||
data-line={state === "engine" ? "released" : state}
|
||||
x1={pct(from)}
|
||||
x2={pct(to)}
|
||||
y1="50%"
|
||||
y2="50%"
|
||||
pathLength={state === "released" || state === "engine" ? 1 : undefined}
|
||||
style={vars(to === 1 ? { "--i": index, stroke: `url(#${fade})` } : { "--i": index })}
|
||||
/>
|
||||
);
|
||||
if (end <= solidEnd) lines.push(line(stop.x, end, "released", `s${index}`));
|
||||
else if (stop.x < solidEnd) {
|
||||
lines.push(line(stop.x, solidEnd, "released", `s${index}`));
|
||||
lines.push(line(solidEnd, end, into, `l${index}`));
|
||||
} else lines.push(line(stop.x, end, into, `l${index}`));
|
||||
});
|
||||
return (
|
||||
<div className="road-rail" aria-hidden="true" style={vars({ "--today": pct(geometry.today) })}>
|
||||
<svg className="road-drawing" focusable="false">
|
||||
<linearGradient id={fade} className="road-fade" data-state={tail.state} gradientUnits="userSpaceOnUse" x1={pct(Math.max(tail.x, 0.92))} x2="100%">
|
||||
<stop className="road-fade-near" />
|
||||
<stop className="road-fade-far" offset="1" />
|
||||
</linearGradient>
|
||||
<mask id={mask} maskUnits="userSpaceOnUse" x="0" y="0" width="100%" height="100%">
|
||||
<rect width="100%" height="100%" fill="#fff" />
|
||||
{stops.map((stop, index) =>
|
||||
stop.state === "engine" || stop.state === "released" ? null : <circle key={index} cx={pct(stop.x)} cy="50%" r="7.5" fill="#000" />,
|
||||
)}
|
||||
</mask>
|
||||
<g mask={`url(#${mask})`}>{lines}</g>
|
||||
{stops.map((stop, index) => (
|
||||
<svg key={index} className="road-station" data-state={stop.state} x={pct(stop.x)} y="50%" width="1" height="1" overflow="visible" style={vars({ "--i": index })}>
|
||||
<g transform="scale(1.2)">
|
||||
<MarkShape state={stop.state} />
|
||||
</g>
|
||||
</svg>
|
||||
))}
|
||||
</svg>
|
||||
<span className="road-today">
|
||||
<RouteWhale />
|
||||
</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function StateWord({ part }: { part: RoutePart }) {
|
||||
return (
|
||||
<span className="road-state" data-state={part.state}>
|
||||
<StateMark state={part.state} />
|
||||
{part.stateWord}
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
||||
function PartName({ part, locale }: { part: RoutePart; locale: string }) {
|
||||
return part.href ? (
|
||||
<Link className="road-name" href={`/${locale}${part.href}`}>
|
||||
{part.name}
|
||||
</Link>
|
||||
) : (
|
||||
<span className="road-name">{part.name}</span>
|
||||
);
|
||||
}
|
||||
|
||||
export function RouteMap({ parts, engine, label, locale }: { parts: RoutePart[]; engine: string; label: string; locale: string }) {
|
||||
const treeRoot = useId();
|
||||
return (
|
||||
<>
|
||||
<div className="road road-map">
|
||||
<Rail geometry={mapGeometry(parts)} />
|
||||
<ol className="road-stops" aria-label={label} style={{ gridTemplateColumns: `repeat(${parts.length + 1}, minmax(0, 1fr))` }}>
|
||||
<li className="road-stop" data-state="engine">
|
||||
<span className="road-name">{engine}</span>
|
||||
</li>
|
||||
{parts.map((part) => (
|
||||
<li key={part.id} className="road-stop" data-state={part.state}>
|
||||
<PartName part={part} locale={locale} />{" "}
|
||||
<StateWord part={part} />{" "}
|
||||
{part.sentence ? (
|
||||
<span className="road-sentence">
|
||||
<InlineCode text={part.sentence} />
|
||||
</span>
|
||||
) : null}
|
||||
</li>
|
||||
))}
|
||||
</ol>
|
||||
</div>
|
||||
<div className="road-tree">
|
||||
<p className="road-tree-root" id={treeRoot}>
|
||||
{engine}
|
||||
</p>
|
||||
<ul aria-labelledby={treeRoot}>
|
||||
{parts.map((part) => (
|
||||
<li key={part.id} data-state={part.state}>
|
||||
<span className="road-tree-line">
|
||||
<PartName part={part} locale={locale} />
|
||||
<span className="road-tree-leader" aria-hidden="true" />
|
||||
<StateWord part={part} />
|
||||
</span>
|
||||
{part.sentence ? (
|
||||
<span className="road-sentence">
|
||||
<InlineCode text={part.sentence} />
|
||||
</span>
|
||||
) : null}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
import { useId } from "react";
|
||||
import whale from "@/vendor/whale-b/whale-b.json";
|
||||
|
||||
const GRADIENT_PARTS = new Set(["flukes", "dorsal", "body", "flipper"]);
|
||||
|
||||
export function RouteWhale() {
|
||||
const gradient = `road-whale-${useId().replace(/[^a-z0-9]/gi, "")}`;
|
||||
const { x1, y1, x2, y2 } = whale.gradient;
|
||||
return (
|
||||
<svg className="road-whale" viewBox={whale.viewBox} aria-hidden="true" focusable="false">
|
||||
<defs>
|
||||
<linearGradient id={gradient} gradientUnits="userSpaceOnUse" x1={x1} y1={y1} x2={x2} y2={y2}>
|
||||
<stop className="road-whale-rise" />
|
||||
<stop className="road-whale-deep" offset="1" />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<g className="road-whale-swim">
|
||||
{whale.paths.map((path) => (
|
||||
<path
|
||||
key={path.id}
|
||||
d={path.d}
|
||||
className={`road-whale-${path.id}`}
|
||||
fill={GRADIENT_PARTS.has(path.id) ? `url(#${gradient})` : undefined}
|
||||
/>
|
||||
))}
|
||||
</g>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
"use client";
|
||||
|
||||
import { useId, useRef, useState, type KeyboardEvent, type ReactNode } from "react";
|
||||
|
||||
export interface SessionFrame {
|
||||
key: string;
|
||||
name: string;
|
||||
wide: ReactNode;
|
||||
narrow: ReactNode;
|
||||
note: boolean;
|
||||
}
|
||||
|
||||
export function SessionFrames({
|
||||
frames,
|
||||
initial,
|
||||
label,
|
||||
caption,
|
||||
note,
|
||||
}: {
|
||||
frames: SessionFrame[];
|
||||
initial: number;
|
||||
label: string;
|
||||
caption: string | null;
|
||||
note: string;
|
||||
}) {
|
||||
const [selected, setSelected] = useState(initial);
|
||||
const tabs = useRef<(HTMLButtonElement | null)[]>([]);
|
||||
const id = useId();
|
||||
|
||||
const move = (event: KeyboardEvent<HTMLDivElement>) => {
|
||||
const rtl = getComputedStyle(event.currentTarget).direction === "rtl";
|
||||
const last = frames.length - 1;
|
||||
const step: Record<string, number> = {
|
||||
ArrowRight: rtl ? -1 : 1,
|
||||
ArrowLeft: rtl ? 1 : -1,
|
||||
ArrowDown: 1,
|
||||
ArrowUp: -1,
|
||||
};
|
||||
let next: number;
|
||||
if (event.key === "Home") next = 0;
|
||||
else if (event.key === "End") next = last;
|
||||
else if (event.key in step) next = (selected + step[event.key] + frames.length) % frames.length;
|
||||
else return;
|
||||
event.preventDefault();
|
||||
setSelected(next);
|
||||
tabs.current[next]?.focus();
|
||||
};
|
||||
|
||||
return (
|
||||
<figure className="home-session">
|
||||
<div className="home-session-tabs" role="tablist" aria-label={label} onKeyDown={move}>
|
||||
{frames.map((frame, index) => (
|
||||
<button
|
||||
key={frame.key}
|
||||
ref={(node) => {
|
||||
tabs.current[index] = node;
|
||||
}}
|
||||
type="button"
|
||||
role="tab"
|
||||
id={`${id}-tab-${index}`}
|
||||
aria-selected={index === selected}
|
||||
aria-controls={`${id}-panel-${index}`}
|
||||
tabIndex={index === selected ? 0 : -1}
|
||||
onClick={() => setSelected(index)}
|
||||
>
|
||||
{frame.name}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
{frames.map((frame, index) => (
|
||||
<div
|
||||
key={frame.key}
|
||||
className="home-frame home-session-panel"
|
||||
role="tabpanel"
|
||||
id={`${id}-panel-${index}`}
|
||||
aria-labelledby={`${id}-tab-${index}`}
|
||||
hidden={index !== selected}
|
||||
>
|
||||
<div className="home-only-wide">{frame.wide}</div>
|
||||
<div className="home-only-narrow">{frame.narrow}</div>
|
||||
</div>
|
||||
))}
|
||||
<figcaption className="home-session-caption">
|
||||
{caption ? <p className="home-caption">{caption}</p> : null}
|
||||
<p className="home-caption" hidden={!frames[selected].note}>
|
||||
{note}
|
||||
</p>
|
||||
</figcaption>
|
||||
</figure>
|
||||
);
|
||||
}
|
||||
@@ -1,62 +0,0 @@
|
||||
import { useId } from "react";
|
||||
|
||||
function Filters({ id }: { id: string }) {
|
||||
return (
|
||||
<defs>
|
||||
<filter id={`${id}-wide`} x="-20%" y="-40%" width="140%" height="180%" colorInterpolationFilters="sRGB">
|
||||
<feGaussianBlur stdDeviation="34" />
|
||||
</filter>
|
||||
<filter id={`${id}-mid`} x="-20%" y="-40%" width="140%" height="180%" colorInterpolationFilters="sRGB">
|
||||
<feGaussianBlur stdDeviation="20" />
|
||||
</filter>
|
||||
<filter id={`${id}-tight`} x="-20%" y="-40%" width="140%" height="180%" colorInterpolationFilters="sRGB">
|
||||
<feGaussianBlur stdDeviation="11" />
|
||||
</filter>
|
||||
<filter id={`${id}-edge`} x="-20%" y="-40%" width="140%" height="180%" colorInterpolationFilters="sRGB">
|
||||
<feGaussianBlur stdDeviation="5" />
|
||||
</filter>
|
||||
</defs>
|
||||
);
|
||||
}
|
||||
|
||||
export function Strata() {
|
||||
const id = `strata-${useId()}`;
|
||||
return (
|
||||
<svg
|
||||
className="strata strata-hero"
|
||||
viewBox="0 0 1440 1000"
|
||||
preserveAspectRatio="xMidYMax slice"
|
||||
aria-hidden="true"
|
||||
focusable="false"
|
||||
>
|
||||
<Filters id={id} />
|
||||
<ellipse cx="1180" cy="160" rx="520" ry="200" fill="var(--strata-haze)" filter={`url(#${id}-wide)`} />
|
||||
<path
|
||||
d="M -240 690 C 160 640, 420 720, 660 570 C 880 430, 1040 280, 1240 210 C 1340 175, 1440 150, 1700 110 L 1700 1120 L -240 1120 Z"
|
||||
fill="var(--strata-1)"
|
||||
filter={`url(#${id}-wide)`}
|
||||
/>
|
||||
<path
|
||||
d="M -240 800 C 140 770, 400 830, 620 700 C 830 575, 1000 440, 1200 370 C 1330 325, 1500 280, 1700 250 L 1700 1120 L -240 1120 Z"
|
||||
fill="var(--strata-2)"
|
||||
filter={`url(#${id}-mid)`}
|
||||
/>
|
||||
<path
|
||||
d="M -240 890 C 200 860, 430 905, 650 810 C 870 715, 1060 580, 1260 520 C 1400 478, 1540 445, 1700 410 L 1700 1120 L -240 1120 Z"
|
||||
fill="var(--strata-3)"
|
||||
filter={`url(#${id}-tight)`}
|
||||
/>
|
||||
<path
|
||||
d="M -240 965 C 220 945, 470 975, 710 910 C 910 855, 1110 740, 1310 680 C 1430 645, 1560 615, 1700 600 L 1700 1120 L -240 1120 Z"
|
||||
fill="var(--strata-4)"
|
||||
filter={`url(#${id}-edge)`}
|
||||
/>
|
||||
<g fill="none" strokeLinecap="round">
|
||||
<path d="M 980 330 C 1100 285, 1200 255, 1340 215 C 1420 192, 1520 170, 1700 140" stroke="var(--strata-line)" strokeOpacity="0.5" strokeWidth="1.1" />
|
||||
<path d="M 760 640 C 900 560, 1040 470, 1200 405 C 1330 352, 1480 310, 1700 275" stroke="var(--strata-line)" strokeOpacity="0.36" strokeWidth="1" />
|
||||
<path d="M 620 860 C 840 780, 1040 660, 1240 590 C 1400 535, 1560 500, 1700 470" stroke="var(--strata-line)" strokeOpacity="0.3" strokeWidth="1" />
|
||||
<path d="M 420 960 C 640 930, 860 860, 1060 780 C 1240 708, 1420 650, 1700 600" stroke="var(--strata-thread)" strokeOpacity="0.45" strokeWidth="0.9" />
|
||||
</g>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
+25
-15
@@ -314,37 +314,54 @@
|
||||
},
|
||||
"home": {
|
||||
"metaTitle": "Codewhale · The model can be anyone's. The agent should be yours.",
|
||||
"metaDescription": "Codewhale is becoming the open environment where you build software with many agents, on any model, on your own computer and in the cloud. It starts today in your terminal and your browser, open source and yours to read.",
|
||||
"metaDescription": "Codewhale is an open-source coding agent for your terminal, written in Rust. It works with any model, can split one task across several agents, and every line of it is yours to read.",
|
||||
"heroTitle": "The model can be anyone's. The agent should be yours.",
|
||||
"heroTitleLead": "The model can be anyone's.",
|
||||
"heroIntro": "{brand} is becoming the open environment where you build software with many agents, on any model, on your own computer and in the cloud. It starts today in your terminal and your browser, open source and yours to read.",
|
||||
"heroIntro": "{brand} is an open-source coding agent for your terminal, written in Rust. It works with any model, can split one task across several agents, and every line of it is yours to read.",
|
||||
"readTheCode": "Read the code",
|
||||
"installNote": "Version {version} is released, and it needs no account.",
|
||||
"captionDevelopment": "A development build in a real terminal: the edits and tests ran for real, and the model is a scripted demo.",
|
||||
"captionReleased": "A real terminal session: the edits and tests ran for real, and the model is a scripted demo.",
|
||||
"captureFrameNoPosture": "The model name and the Ask setting are shown on the frames before and after this one.",
|
||||
"captureFrameNoPosture": "The Prompt and Result frames show the model name and the Ask setting.",
|
||||
"frameLabelPrompt": "Prompt",
|
||||
"frameLabelEdit": "Edit",
|
||||
"frameLabelApproval": "Approval",
|
||||
"frameLabelResult": "Result",
|
||||
"frameLabelReceipt": "Receipt",
|
||||
"frameSummaryPrompt": "Codewhale in a terminal. The request “Add a --json flag to ls.py so the tests pass, then run the tests.” has been sent, and the status line shows Ask and the model scripted-demo.",
|
||||
"frameSummaryEdit": "Codewhale in a terminal. It read ls.py and changed it, the diff shows the added lines, and a card asks whether to run python3 -m unittest -q.",
|
||||
"frameSummaryApproval": "Codewhale in a terminal. The change to ls.py is shown, and the command python3 -m unittest -q waits for approval. The card asks “Do you want to proceed?” and offers to allow it once, allow it for this conversation or not allow it.",
|
||||
"frameSummaryResult": "Codewhale in a terminal. After approval the command ran 2 tests and printed OK. Codewhale says it added --json to ls.py and both tests pass, and that this session uses a scripted local model.",
|
||||
"frameSummaryProvider": "Codewhale in a terminal. The Custom provider form for an OpenAI-compatible endpoint, with the name scripted_local, a base URL on this computer, the default model scripted-demo and an optional API key variable.",
|
||||
"frameSummaryReceipt": "Codewhale in a terminal. The receipt of the session: 1 file changed and 1 command run, the command approved by you and the file change made without asking under Ask, then a list of what the receipt does not record.",
|
||||
"getCodewhale": "Install Codewhale",
|
||||
"heroInstallAria": "Install command",
|
||||
"shotPreview": "Local terminal demo",
|
||||
"screenshotAlt": "Codewhale v{version}: a captured terminal view with the conversation, message composer and session controls.",
|
||||
"belief": "The model you use is usually someone else's. You rent it, and it changes under you. The agent is different. It reads your files, runs your commands, holds what you are working on and acts in your name, so it should belong to you. Codewhale is that agent. It is open source, so you can read every line of it, or ask it to read itself to you. It works with any model you choose, you decide what it may do without asking, and it leaves a record you can read. We are building it into one place where you and many agents make software together, from your terminal to a computer in the cloud, and it stays yours the whole way.",
|
||||
"sectionModelHeading": "Any model can work through your agent.",
|
||||
"sectionModelBody": "Use a built-in provider, any OpenAI-compatible endpoint or a model on your own machine, and change models between turns. Codewhale runs in your repository with no account, and `codewhale web` lets you work with it in your browser on your own machine.",
|
||||
"sectionControlHeading": "Your agent answers to you.",
|
||||
"sectionControlBody": "You decide what Codewhale may do without asking, and its commands can run in a sandbox on macOS and Linux. Every session leaves a receipt of the files it changed, the commands it ran and who approved each step.",
|
||||
"sectionAgentsHeading": "Several agents can share one task with you.",
|
||||
"sectionAgentsBody": "Codewhale splits a job across agents with roles such as explorer, implementer and reviewer, and you can watch, inspect and stop each one. A Fleet keeps a ledger, so a run can pick up again after a restart.",
|
||||
"sectionDirectionHeading": "Here is where your agent is going.",
|
||||
"sectionDirectionBody": "Codewhale is becoming one environment across your terminal, a desktop app, the browser and cloud computers, all run by one engine, so your work can move between them and stay yours. Each part below says where it stands today.",
|
||||
"sectionOpenHeading": "Codewhale is built in the open by the people who use it.",
|
||||
"sectionOpenBody": "It began as an open-source terminal tool for one model and became a project that hundreds of people in many languages build together. You can read its code, change it, or ask it to explain itself.",
|
||||
"sectionStartHeading": "Make your agent yours today.",
|
||||
"sectionStartBody": "Install Codewhale and run it in a repository with a model key you already have or a model on your own computer. You need no account, and `codewhale update` keeps it current.",
|
||||
"contributorsCount": {
|
||||
"one": "{n} contributor",
|
||||
"other": "{n} contributors"
|
||||
},
|
||||
"factsLabel": "Codewhale at a glance",
|
||||
"factRust": "Written in Rust",
|
||||
"factLicense": "Open source, {license} license",
|
||||
"factProviders": {
|
||||
"one": "{n} provider route",
|
||||
"other": "{n} provider routes"
|
||||
},
|
||||
"factModels": {
|
||||
"one": "{n} model",
|
||||
"other": "{n} models"
|
||||
},
|
||||
"mapHeading": "One engine",
|
||||
"mapStateReleased": "Released",
|
||||
"mapStateDeveloping": "In development, no download",
|
||||
@@ -362,15 +379,8 @@
|
||||
"mapCloudSentence": "Cloud computers are invite-only, and we are building them so a session can keep working on a machine in the cloud.",
|
||||
"mapOneSession": "One session across them",
|
||||
"mapOneSessionSentence": "We are building one session that goes with you from the terminal to the desktop, the browser and the cloud, and stays yours the whole way.",
|
||||
"proofContributors": "{count} contributors",
|
||||
"sourceCandidate": "Unreleased",
|
||||
"publishedRelease": "released",
|
||||
"modelsHeading": "Use the models you choose",
|
||||
"modelsLink": "Browse models and providers",
|
||||
"startHeading": "Start with one useful task",
|
||||
"copy": "Copy",
|
||||
"copied": "Copied ✓",
|
||||
"contribute": "Contribute"
|
||||
"copied": "Copied ✓"
|
||||
},
|
||||
"docs-guide": {
|
||||
"metaTitle": "Start your first task · Codewhale Docs",
|
||||
|
||||
+21
-14
@@ -314,46 +314,53 @@
|
||||
},
|
||||
"home": {
|
||||
"metaTitle": "Codewhale · 模型可以是任何人的。智能体应当属于你。",
|
||||
"metaDescription": "Codewhale 正在成为一个开放的环境:你可以在自己的电脑上和云端,使用任意模型,与许多智能体一起构建软件。今天,它从你的终端和浏览器开始,开源,代码任你阅读。",
|
||||
"metaDescription": "Codewhale 是一个运行在终端里的开源编程智能体,用 Rust 编写。它支持任意模型,可以把一项任务分给多个智能体,而且它的每一行代码都任你阅读。",
|
||||
"heroTitle": "模型可以是任何人的。智能体应当属于你。",
|
||||
"heroTitleLead": "模型可以是任何人的。",
|
||||
"heroIntro": "{brand} 正在成为一个开放的环境:你可以在自己的电脑上和云端,使用任意模型,与许多智能体一起构建软件。今天,它从你的终端和浏览器开始,开源,代码任你阅读。",
|
||||
"heroIntro": "{brand} 是一个运行在终端里的开源编程智能体,用 Rust 编写。它支持任意模型,可以把一项任务分给多个智能体,而且它的每一行代码都任你阅读。",
|
||||
"readTheCode": "阅读代码",
|
||||
"installNote": "版本 {version} 已发布,无需账号。",
|
||||
"captionDevelopment": "开发版本在真实终端中的运行:编辑和测试都是真实执行的,模型是脚本化的演示。",
|
||||
"captionReleased": "真实的终端会话:编辑和测试都是真实执行的,模型是脚本化的演示。",
|
||||
"captureFrameNoPosture": "模型名称和 Ask 设置显示在这一帧之前和之后的画面中。",
|
||||
"captureFrameNoPosture": "“提示”和“结果”两帧显示了模型名称和 Ask 设置。",
|
||||
"frameLabelPrompt": "提示",
|
||||
"frameLabelEdit": "编辑",
|
||||
"frameLabelApproval": "审批",
|
||||
"frameLabelResult": "结果",
|
||||
"frameLabelReceipt": "回执",
|
||||
"frameSummaryPrompt": "终端中的 Codewhale。请求“Add a --json flag to ls.py so the tests pass, then run the tests.”已发送,状态栏显示 Ask 和模型 scripted-demo。",
|
||||
"frameSummaryEdit": "终端中的 Codewhale。它读取并修改了 ls.py,差异中显示了新增的行,一张卡片询问是否运行 python3 -m unittest -q。",
|
||||
"frameSummaryApproval": "终端中的 Codewhale。画面显示了对 ls.py 的修改,命令 python3 -m unittest -q 正在等待批准。卡片询问“Do you want to proceed?”,并提供允许一次、在本次对话中允许或不允许三个选项。",
|
||||
"frameSummaryResult": "终端中的 Codewhale。批准后,命令运行了 2 个测试并输出 OK。Codewhale 说明它已为 ls.py 添加 --json,两个测试都通过,并说明本次会话使用的是脚本化的本地模型。",
|
||||
"frameSummaryProvider": "终端中的 Codewhale。用于 OpenAI 兼容端点的自定义提供方表单,名称为 scripted_local,基础 URL 指向本机,默认模型为 scripted-demo,API 密钥变量为可选项。",
|
||||
"frameSummaryReceipt": "终端中的 Codewhale。本次会话的回执:修改了 1 个文件,运行了 1 条命令,命令由你批准,文件修改在 Ask 设置下未经询问完成,随后列出回执未记录的内容。",
|
||||
"getCodewhale": "安装 Codewhale",
|
||||
"heroInstallAria": "安装命令",
|
||||
"shotPreview": "本地终端演示",
|
||||
"screenshotAlt": "Codewhale v{version} 的终端实录画面,包含对话、消息输入框与会话控制。",
|
||||
"proofContributors": "{count} 位贡献者",
|
||||
"sourceCandidate": "未发布",
|
||||
"publishedRelease": "已发布",
|
||||
"modelsHeading": "使用你选择的模型",
|
||||
"modelsLink": "浏览模型与提供商",
|
||||
"startHeading": "从一件有用的任务开始",
|
||||
"copy": "复制",
|
||||
"copied": "已复制 ✓",
|
||||
"contribute": "参与贡献",
|
||||
"belief": "你使用的模型通常是别人的。你租用它,它会在你不知情时发生变化。智能体则不同。它读取你的文件,运行你的命令,记住你正在做的工作,并以你的名义行事,所以它应当属于你。Codewhale 就是这样的智能体。它是开源的,所以你可以阅读它的每一行代码,也可以让它把自己读给你听。它支持你选择的任意模型,由你决定它可以在不询问的情况下做什么,并且它会留下一份你能读懂的记录。我们正在把它建设成一个地方,让你和许多智能体在这里一起开发软件,从你的终端一直到云端的一台计算机,而且它自始至终都属于你。",
|
||||
"sectionModelHeading": "任何模型都可以通过你的智能体工作。",
|
||||
"sectionModelBody": "使用内置提供商、任意 OpenAI 兼容端点,或你自己机器上的模型,并且可以在两轮对话之间更换模型。Codewhale 在你的仓库中运行,无需账号,而 `codewhale web` 让你在自己机器上的浏览器里使用它。",
|
||||
"sectionControlHeading": "你的智能体对你负责。",
|
||||
"sectionControlBody": "由你决定 Codewhale 可以在不询问的情况下做什么,它的命令可以在 macOS 和 Linux 上的沙箱中运行。每次会话都会留下一份回执,记录它修改的文件、运行的命令,以及每一步由谁批准。",
|
||||
"sectionAgentsHeading": "多个智能体可以与你共同完成一项任务。",
|
||||
"sectionAgentsBody": "Codewhale 把一项工作分给多个智能体,各有探索者、实施者和审查者等角色,你可以查看、检查并停止其中每一个。Fleet 会保存一份账本,所以重启之后,运行可以接着继续。",
|
||||
"sectionDirectionHeading": "这是你的智能体将要去的地方。",
|
||||
"sectionDirectionBody": "Codewhale 正在成为横跨你的终端、桌面应用、浏览器和云端计算机的统一环境,全部由同一个引擎驱动,所以你的工作可以在它们之间转移,并始终属于你。下面每个部分都说明了它今天所处的状态。",
|
||||
"sectionOpenHeading": "Codewhale 由使用它的人公开构建。",
|
||||
"sectionOpenBody": "它起初是一个只支持一种模型的开源终端工具,后来成为数以百计、使用多种语言的人共同构建的项目。你可以阅读它的代码、修改它,或让它自己解释自己。",
|
||||
"sectionStartHeading": "今天就让你的智能体属于你。",
|
||||
"sectionStartBody": "安装 Codewhale,并在一个仓库中使用你已有的模型密钥,或你自己电脑上的模型来运行它。你无需账号,`codewhale update` 会让它保持最新。",
|
||||
"contributorsCount": {
|
||||
"other": "{n} 位贡献者"
|
||||
},
|
||||
"factsLabel": "Codewhale 一览",
|
||||
"factRust": "用 Rust 编写",
|
||||
"factLicense": "开源,{license} 许可证",
|
||||
"factProviders": {
|
||||
"other": "{n} 条提供商路由"
|
||||
},
|
||||
"factModels": {
|
||||
"other": "{n} 个模型"
|
||||
},
|
||||
"mapHeading": "一个引擎",
|
||||
"mapStateReleased": "已发布",
|
||||
"mapStateDeveloping": "开发中,暂无下载",
|
||||
|
||||
@@ -22,7 +22,6 @@ import { FEATURE_CATALOGUE, type Feature } from "./features";
|
||||
import { FOUNDER_SWITCHES, FOUNDER_SWITCH_VALUES } from "./founder-switches";
|
||||
import { PRODUCT_COPY } from "./content/product";
|
||||
import ModelsPage from "../app/[locale]/models/page";
|
||||
import HomePage from "../app/[locale]/page";
|
||||
import { buildLlmsTxt } from "./llms-txt";
|
||||
|
||||
vi.mock("next/navigation", () => ({ redirect: vi.fn(), notFound: vi.fn(), usePathname: () => "/en", useRouter: () => ({}) }));
|
||||
@@ -211,23 +210,15 @@ describe("counts are the counts at the published release tag", () => {
|
||||
expect(releaseView({ latestPublishedRelease: null })).toEqual({ providers: [], models: [], counts: null });
|
||||
});
|
||||
|
||||
it("leave main-only models out of the models page and the home page", async () => {
|
||||
it("leave main-only models out of the models page", async () => {
|
||||
const tagged = new Set(RELEASE_BASELINE!.models.map((model) => model.id));
|
||||
const mainOnly = FACTS.models.filter((model) => !tagged.has(model.id));
|
||||
expect(mainOnly.length).toBeGreaterThan(0);
|
||||
const props = { params: Promise.resolve({ locale: "en" }), searchParams: Promise.resolve({}) } as never;
|
||||
const models = renderToStaticMarkup((await ModelsPage(props)) as never);
|
||||
vi.stubGlobal("fetch", vi.fn(async () => { throw new Error("network down"); }));
|
||||
vi.spyOn(console, "error").mockImplementation(() => {});
|
||||
const home = renderToStaticMarkup((await HomePage(props)) as never);
|
||||
for (const model of mainOnly) {
|
||||
expect(models, model.id).not.toContain(`>${model.id}<`);
|
||||
expect(home, model.id).not.toContain(`>${model.id}<`);
|
||||
}
|
||||
for (const model of mainOnly) expect(models, model.id).not.toContain(`>${model.id}<`);
|
||||
expect(models).toContain(`Show all ${view.counts!.providerRoutes} provider routes`);
|
||||
expect(models).not.toMatch(/Show all \d+ providers/);
|
||||
expect(home).toContain(`${view.counts!.providerRoutes} provider routes and ${view.counts!.models} catalog models`);
|
||||
expect(home).not.toMatch(/\d+ providers and/);
|
||||
});
|
||||
|
||||
it("print provider routes in llms.txt at the tag", () => {
|
||||
|
||||
@@ -1,20 +0,0 @@
|
||||
import type { LocalizedText } from "./vocabulary";
|
||||
|
||||
export const HOME_OVERVIEW = {
|
||||
modelsCount: { en: "{version} has {routes} provider routes and {models} catalog models.", zh: "{version} 包含 {routes} 条提供商路由和 {models} 个目录模型。" },
|
||||
modelsAny: { en: "Any OpenAI-compatible endpoint", zh: "任何 OpenAI 兼容端点" },
|
||||
modelsNewest: { en: "Newest in the catalog", zh: "目录中最新的模型" },
|
||||
computerUseTitle: { en: "Computer Use", zh: "Computer Use" },
|
||||
computerUseBody: {
|
||||
en: "Let an agent work in Mac apps in the background while you keep using your computer.",
|
||||
zh: "让智能体在后台操作 Mac 应用,你可以继续使用电脑。",
|
||||
},
|
||||
computerUseTag: { en: "macOS beta", zh: "macOS 测试版" },
|
||||
pluginsTitle: { en: "Everything is a plugin", zh: "一切皆插件" },
|
||||
pluginsBody: {
|
||||
en: "Skills, commands, agent profiles, hooks and connectors to the apps you already use all arrive the same way: as a bundle you review, trust and enable.",
|
||||
zh: "技能、命令、智能体配置、钩子,以及连接你常用应用的连接器,都以同一种方式加入:作为你审查、信任并启用的插件包。",
|
||||
},
|
||||
pluginsLink: { en: "Browse plugins and connectors", zh: "浏览插件和连接器" },
|
||||
releaseLabel: { en: "Latest release {version}", zh: "最新版本 {version}" },
|
||||
} satisfies Record<string, LocalizedText>;
|
||||
@@ -30,9 +30,6 @@ describe("design grammar contract", () => {
|
||||
expect(resolveWhale("var(--gpui-focus-offset)")).toBe("3px");
|
||||
expect(CSS).not.toMatch(/outline:\s*none/);
|
||||
expect(CSS).toMatch(/::selection \{ background: var\(--selection\); \}/);
|
||||
// The light ring on the dark stage would fall under 3:1, so the stage
|
||||
// re-declares it.
|
||||
expect(CSS).toMatch(/\.stage,\s*\.site-footer\s*\{[^}]*--ring: var\(--gpui-dark-accent\);/);
|
||||
});
|
||||
|
||||
it("paints every primary fill with the logo gradient and white text", () => {
|
||||
|
||||
@@ -360,9 +360,9 @@ describe("navigation parity and accessibility", () => {
|
||||
describe("homepage integration", () => {
|
||||
const homepage = webText("app/[locale]/page.tsx");
|
||||
|
||||
it("keeps the published release separate from the source candidate", () => {
|
||||
it("names the published release and never the source version", () => {
|
||||
expect(homepage).toContain("facts.latestPublishedRelease");
|
||||
expect(homepage).toContain("d.sourceCandidate");
|
||||
expect(getHome("en").sourceCandidate).toBe("Unreleased");
|
||||
expect(homepage).not.toContain("facts.version");
|
||||
expect(getHome("en").installNote).toContain("{version}");
|
||||
});
|
||||
});
|
||||
@@ -1,8 +0,0 @@
|
||||
export function isActiveAt(endsAt: string, now: number = Date.now()): boolean {
|
||||
const end = Date.parse(endsAt);
|
||||
return Number.isFinite(end) && end > now;
|
||||
}
|
||||
|
||||
export function daysLeft(endsAt: string, now: number = Date.now()): number {
|
||||
return Math.max(1, Math.ceil((Date.parse(endsAt) - now) / 86_400_000));
|
||||
}
|
||||
@@ -8,7 +8,6 @@ import { buildLlmsTxt } from "./llms-txt";
|
||||
import { buildSoftwareApplicationJsonLd } from "./software-application-schema";
|
||||
import { AVAILABILITY_ROWS } from "./content/availability";
|
||||
import { APP_COPY } from "./content/app-page";
|
||||
import { PulseStats } from "../components/project-pulse";
|
||||
import sitemap from "../app/sitemap";
|
||||
import HomePage from "../app/[locale]/page";
|
||||
import ContributePage from "../app/[locale]/contribute/page";
|
||||
@@ -44,14 +43,16 @@ describe("the home page and /contribute with every GitHub call failing", () => {
|
||||
expect(html).toContain("<h1");
|
||||
const numbers = [...html.matchAll(/class="[^"]*\btabular\b[^"]*"[^>]*>([^<]*)</g)].map((match) => match[1].trim());
|
||||
expect(numbers).not.toContain("0");
|
||||
expect(html).toContain(new Intl.NumberFormat(locale === "zh" ? "zh-CN" : "en-US").format(snapshot.stars));
|
||||
}
|
||||
const numberLocale = locale === "zh" ? "zh-CN" : "en-US";
|
||||
expect(await render(ContributePage as Page, locale)).toContain(new Intl.NumberFormat(numberLocale).format(snapshot.stars));
|
||||
expect(await render(HomePage as Page, locale)).toContain(`>${new Intl.NumberFormat(numberLocale).format(FACTS.contributorCount)}<`);
|
||||
});
|
||||
|
||||
it("states the published release, not the source version, on the home page", async () => {
|
||||
const html = await render(HomePage as Page);
|
||||
expect(html).toContain(`Latest release v${published.version}`);
|
||||
if (FACTS.version !== published.version) expect(html).not.toContain(`Latest release v${FACTS.version}`);
|
||||
expect(html).toContain(`Version ${published.version} is released, and it needs no account.`);
|
||||
if (FACTS.version !== published.version) expect(html).not.toContain(`Version ${FACTS.version} is released`);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -84,13 +85,6 @@ describe("a live number that cannot be read", () => {
|
||||
expect(readRepoStatsSnapshot(bad)).toEqual(unknown);
|
||||
}
|
||||
});
|
||||
|
||||
it("renders no cell for an unknown number", () => {
|
||||
const html = renderToStaticMarkup(PulseStats({ stats: { ...unknown, source: "unknown" } as never, contributors: 12, locale: "en" }));
|
||||
expect(html).toContain("12");
|
||||
expect(html).not.toMatch(/>0</);
|
||||
expect(html.match(/<dd>/g)).toHaveLength(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe("the version a visitor gets on install", () => {
|
||||
@@ -113,8 +107,8 @@ describe("the version a visitor gets on install", () => {
|
||||
|
||||
it("keeps the source version out of the installed-version copy", () => {
|
||||
const home = readFileSync(new URL("../app/[locale]/page.tsx", import.meta.url), "utf8");
|
||||
expect(home).toContain("version: `v${publishedRelease.version}`");
|
||||
expect(home).not.toMatch(/releaseLabel[^\n]*sourceVersion/);
|
||||
expect(home).toContain("fill(d.installNote, { version: release.version })");
|
||||
expect(home).not.toContain("facts.version");
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { readFileSync } from "node:fs";
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { resolveWhale } from "./whale-tokens";
|
||||
import { siteCss } from "./site-css";
|
||||
@@ -51,11 +52,10 @@ function contrast(a: string, b: string): number {
|
||||
|
||||
const light = () => roleBlock(":root");
|
||||
const pinnedDark = () => roleBlock(':root[data-theme="dark"]');
|
||||
const stage = () => roleBlock(".stage,\n.site-footer");
|
||||
|
||||
describe("role tokens", () => {
|
||||
it("defines every role in light, pinned-dark and the stage", () => {
|
||||
for (const [name, scheme] of [["light", light()], ["pinned", pinnedDark()], ["stage", stage()]] as const) {
|
||||
it("defines every role in light and pinned-dark", () => {
|
||||
for (const [name, scheme] of [["light", light()], ["pinned", pinnedDark()]] as const) {
|
||||
for (const role of ROLES) expect(scheme, `${name}.${role}`).toHaveProperty(role);
|
||||
}
|
||||
// Light is the site's appearance: dark is a reader's pin, never the OS.
|
||||
@@ -74,17 +74,19 @@ describe("role tokens", () => {
|
||||
expect(hex(scheme[role]), `${mode}.${role}`).toBe(gpui(mode, name));
|
||||
}
|
||||
}
|
||||
for (const role of ["text", "muted", "accent", "ring", "live", "attention", "danger"]) {
|
||||
expect(stage()[role], `stage.${role}`).toMatch(/^var\(--gpui-dark-[\w-]+\)$/);
|
||||
}
|
||||
expect(hex(light().bg)).toBe("#f1f5f9");
|
||||
expect(hex(pinnedDark().bg)).toBe("#030508");
|
||||
expect(CSS).not.toMatch(/--kit-(warm-paper|foam|mist|bioluminescence|ink|slate)/);
|
||||
for (const role of ["bg", "surface", "panel"]) expect(stage()[role], role).toMatch(/^var\(--ocean-[\w-]+\)$/);
|
||||
});
|
||||
|
||||
it("re-declares no role for the footer: it sits on the page's own ground", () => {
|
||||
expect(CSS).not.toMatch(/\.stage\b/);
|
||||
expect(CSS).not.toMatch(/\.site-footer\s*\{[^}]*--(bg|text|muted):/);
|
||||
expect(CSS).not.toMatch(/--sea(-[\w-]+)?:/);
|
||||
});
|
||||
|
||||
it("keeps every text ink at WCAG AA on every ground, in every scheme", () => {
|
||||
for (const [name, scheme] of [["light", light()], ["dark", pinnedDark()], ["stage", stage()]] as const) {
|
||||
for (const [name, scheme] of [["light", light()], ["dark", pinnedDark()]] as const) {
|
||||
for (const ground of GROUNDS) {
|
||||
for (const ink of TEXT_INKS) {
|
||||
expect(contrast(hex(scheme[ink]), hex(scheme[ground])), `${name}: ${ink} on ${ground}`).toBeGreaterThanOrEqual(4.5);
|
||||
@@ -107,18 +109,20 @@ describe("role tokens", () => {
|
||||
expect(hex("var(--brand-light)")).toBe("#1e8fd8");
|
||||
});
|
||||
|
||||
it("keeps stage text readable across the sea", () => {
|
||||
const stops = (token: string): string[] => {
|
||||
const ramp = CSS.match(new RegExp(`--${token}:\\s*linear-gradient\\(([^;]+)\\);`))?.[1] ?? "";
|
||||
return [...ramp.matchAll(/#[0-9a-f]{6}|var\(--[\w-]+\)/gi)].map((m) => hex(m[0]));
|
||||
};
|
||||
for (const token of ["sea", "sea-band"]) {
|
||||
const ramp = stops(token);
|
||||
expect(ramp.length, token).toBeGreaterThanOrEqual(3);
|
||||
for (const stop of ramp) {
|
||||
expect(contrast(hex(stage().muted), stop), `${token}: muted on ${stop}`).toBeGreaterThanOrEqual(4.5);
|
||||
expect(contrast(hex(stage().text), stop), `${token}: text on ${stop}`).toBeGreaterThanOrEqual(4.5);
|
||||
it("keeps the home's text readable across the water, in both schemes", () => {
|
||||
const tokens = readFileSync(new URL("../app/tokens.css", import.meta.url), "utf8");
|
||||
for (const [mode, scheme] of [["light", light()], ["dark", pinnedDark()]] as const) {
|
||||
expect(scheme["home-sky"], mode).toContain(`var(--gpui-surface-light-${mode})`);
|
||||
const ramp = tokens.match(new RegExp(`--gpui-water-${mode}:\\s*linear-gradient\\(([^;]+)\\);`))?.[1] ?? "";
|
||||
const stops = [...ramp.matchAll(/#[0-9a-f]{6}/gi)].map((m) => m[0].toLowerCase());
|
||||
expect(stops.length, mode).toBeGreaterThanOrEqual(3);
|
||||
for (const stop of stops) {
|
||||
for (const ink of ["text", "muted", "live"]) {
|
||||
expect(contrast(hex(scheme[ink]), stop), `${mode}: ${ink} on ${stop}`).toBeGreaterThanOrEqual(4.5);
|
||||
}
|
||||
}
|
||||
const band = [...CSS.matchAll(/--home-band: (#[0-9a-f]{6});/g)].map((m) => m[1])[mode === "light" ? 0 : 1];
|
||||
expect(stops, `${mode} band`).toContain(band);
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -112,13 +112,12 @@ const HOME_PROSE_KEYS = [
|
||||
"captionDevelopment",
|
||||
"captionReleased",
|
||||
"captureFrameNoPosture",
|
||||
"belief",
|
||||
"sectionModelHeading",
|
||||
"sectionModelBody",
|
||||
"sectionControlHeading",
|
||||
"sectionControlBody",
|
||||
"sectionAgentsHeading",
|
||||
"sectionAgentsBody",
|
||||
"factRust",
|
||||
"factLicense",
|
||||
"sectionDirectionHeading",
|
||||
"sectionDirectionBody",
|
||||
"sectionOpenHeading",
|
||||
@@ -146,9 +145,12 @@ const HOME_PROSE_KEYS = [
|
||||
"heroInstallAria",
|
||||
"shotPreview",
|
||||
"screenshotAlt",
|
||||
"modelsHeading",
|
||||
"modelsLink",
|
||||
"startHeading",
|
||||
"frameSummaryPrompt",
|
||||
"frameSummaryEdit",
|
||||
"frameSummaryApproval",
|
||||
"frameSummaryResult",
|
||||
"frameSummaryProvider",
|
||||
"frameSummaryReceipt",
|
||||
] as const satisfies readonly (keyof HomeDict)[];
|
||||
|
||||
function templateTokens(value: string): string[] {
|
||||
|
||||
@@ -4,43 +4,37 @@ import type { HomeDict } from "../types";
|
||||
|
||||
export const home: HomeDict = {
|
||||
metaTitle: "Codewhale · يمكن أن يكون النموذج ملكًا لأي أحد. أما الوكيل فينبغي أن يكون ملكًا لك.",
|
||||
metaDescription: "Codewhale يتحول إلى البيئة المفتوحة التي تبني فيها البرمجيات مع وكلاء كثيرين، على أي نموذج، على حاسوبك وفي السحابة. يبدأ اليوم في الطرفية والمتصفح لديك، مفتوح المصدر ومتاح لك لتقرأه.",
|
||||
metaDescription: "Codewhale وكيل برمجة مفتوح المصدر يعمل في الطرفية لديك، ومكتوب بلغة Rust. يعمل مع أي نموذج، ويستطيع توزيع مهمة واحدة على عدة وكلاء، وكل سطر فيه متاح لك لتقرأه.",
|
||||
heroTitle: "يمكن أن يكون النموذج ملكًا لأي أحد. أما الوكيل فينبغي أن يكون ملكًا لك.",
|
||||
heroTitleLead: "يمكن أن يكون النموذج ملكًا لأي أحد.",
|
||||
heroIntro: "{brand} يتحول إلى البيئة المفتوحة التي تبني فيها البرمجيات مع وكلاء كثيرين، على أي نموذج، على حاسوبك وفي السحابة. يبدأ اليوم في الطرفية والمتصفح لديك، مفتوح المصدر ومتاح لك لتقرأه.",
|
||||
heroIntro: "{brand} وكيل برمجة مفتوح المصدر يعمل في الطرفية لديك، ومكتوب بلغة Rust. يعمل مع أي نموذج، ويستطيع توزيع مهمة واحدة على عدة وكلاء، وكل سطر فيه متاح لك لتقرأه.",
|
||||
readTheCode: "اقرأ الشيفرة",
|
||||
installNote: "الإصدار {version} منشور، ولا يحتاج إلى حساب.",
|
||||
captionDevelopment: "نسخة تطوير في طرفية حقيقية: نُفِّذت التعديلات والاختبارات فعلًا، والنموذج عرض توضيحي مكتوب بنص مسبق.",
|
||||
captionReleased: "جلسة طرفية حقيقية: نُفِّذت التعديلات والاختبارات فعلًا، والنموذج عرض توضيحي مكتوب بنص مسبق.",
|
||||
captureFrameNoPosture: "اسم النموذج وإعداد Ask معروضان في اللقطتين السابقة واللاحقة لهذه اللقطة.",
|
||||
captureFrameNoPosture: "تعرض لقطتا «الطلب» و«النتيجة» اسم النموذج وإعداد Ask.",
|
||||
frameLabelPrompt: "الطلب",
|
||||
frameLabelEdit: "التعديل",
|
||||
frameLabelApproval: "الموافقة",
|
||||
frameLabelResult: "النتيجة",
|
||||
frameLabelReceipt: "الإيصال",
|
||||
frameSummaryPrompt: "Codewhale في طرفية. أُرسل الطلب «Add a --json flag to ls.py so the tests pass, then run the tests.»، ويعرض سطر الحالة الإعداد Ask والنموذج scripted-demo.",
|
||||
frameSummaryEdit: "Codewhale في طرفية. قرأ ls.py وعدّله، ويعرض الفرق الأسطر المضافة، وتسأل بطاقة هل يُشغَّل python3 -m unittest -q.",
|
||||
frameSummaryApproval: "Codewhale في طرفية. يظهر التعديل على ls.py، والأمر python3 -m unittest -q ينتظر الموافقة. تسأل البطاقة «Do you want to proceed?» وتعرض السماح به مرة واحدة، أو السماح به في هذه المحادثة، أو عدم السماح به.",
|
||||
frameSummaryResult: "Codewhale في طرفية. بعد الموافقة شغّل الأمر اختبارين وطبع OK. يقول Codewhale إنه أضاف --json إلى ls.py وإن الاختبارين نجحا، وإن هذه الجلسة تستخدم نموذجًا محليًا مكتوبًا بنص مسبق.",
|
||||
frameSummaryProvider: "Codewhale في طرفية. نموذج المزوّد المخصص لنقطة نهاية متوافقة مع OpenAI، بالاسم scripted_local وعنوان URL أساسي على هذا الحاسوب والنموذج الافتراضي scripted-demo ومتغير اختياري لمفتاح API.",
|
||||
frameSummaryReceipt: "Codewhale في طرفية. إيصال الجلسة: تغيّر ملف واحد ونُفّذ أمر واحد، وافقتَ أنت على الأمر وجرى تعديل الملف دون سؤال في الإعداد Ask، ثم قائمة بما لا يسجله الإيصال.",
|
||||
getCodewhale: "ثبّت Codewhale",
|
||||
heroInstallAria: "أمر التثبيت",
|
||||
shotPreview: "معاينة الطرفية",
|
||||
screenshotAlt:
|
||||
"Codewhale v{version}: لقطة طرفية تعرض المحادثة وحقل الرسالة وعناصر التحكم في الجلسة.",
|
||||
proofContributors: "{count} مساهمًا",
|
||||
sourceCandidate: "غير منشور",
|
||||
publishedRelease: "منشور",
|
||||
modelsHeading: "استخدم النماذج التي تختارها",
|
||||
modelsLink: "تصفّح النماذج والمزوّدين",
|
||||
startHeading: "ابدأ الاستخدام",
|
||||
copy: "انسخ",
|
||||
copied: "نُسخ ✓",
|
||||
contribute: "ساهم على GitHub",
|
||||
|
||||
belief: "النموذج الذي تستخدمه ملك لغيرك في الغالب. أنت تستأجره، ويتغير من تحتك. أما الوكيل فمختلف. إنه يقرأ ملفاتك، وينفذ أوامرك، ويحتفظ بما تعمل عليه، ويتصرف باسمك، لذلك ينبغي أن يكون ملكًا لك. Codewhale هو هذا الوكيل. إنه مفتوح المصدر، فيمكنك قراءة كل سطر فيه، أو أن تطلب منه أن يقرأ نفسه لك. يعمل مع أي نموذج تختاره، وأنت من يقرر ما يجوز له فعله دون أن يسأل، ويترك سجلًا يمكنك قراءته. نحن نبنيه ليصبح مكانًا واحدًا تصنع فيه أنت وعدة وكلاء البرمجيات معًا، من طرفيتك إلى حاسوب في السحابة، ويبقى ملكًا لك طوال الطريق.",
|
||||
|
||||
sectionModelHeading: "يمكن لأي نموذج أن يعمل عبر وكيلك.",
|
||||
sectionModelBody: "استخدم مزوّدًا مدمجًا، أو أي نقطة نهاية متوافقة مع OpenAI، أو نموذجًا على جهازك، وبدّل النماذج بين الأدوار. يعمل Codewhale في مستودعك دون حساب، ويتيح لك `codewhale web` العمل معه في متصفحك على جهازك.",
|
||||
sectionControlHeading: "وكيلك مسؤول أمامك.",
|
||||
sectionControlBody: "أنت من يقرر ما يجوز لـ Codewhale فعله دون أن يسأل، ويمكن تشغيل أوامره داخل صندوق عزل على macOS وLinux. تترك كل جلسة إيصالًا بالملفات التي غيّرها والأوامر التي نفذها ومن وافق على كل خطوة.",
|
||||
sectionAgentsHeading: "يمكن لعدة وكلاء أن يتقاسموا مهمة واحدة معك.",
|
||||
sectionAgentsBody: "يوزّع Codewhale العمل على وكلاء بأدوار مثل المستكشف والمنفّذ والمراجع، ويمكنك مراقبة كل واحد منهم وفحصه وإيقافه. يحتفظ Fleet بسجل، فيمكن أن يستأنف التشغيل بعد إعادة التشغيل.",
|
||||
sectionDirectionHeading: "إلى هنا يتجه وكيلك.",
|
||||
sectionDirectionBody: "يتحول Codewhale إلى بيئة واحدة تمتد عبر طرفيتك وتطبيق سطح المكتب والمتصفح وحواسيب السحابة، يديرها كلها محرك واحد، ليتنقل عملك بينها ويبقى ملكًا لك. يبيّن كل جزء أدناه أين يقف اليوم.",
|
||||
sectionOpenHeading: "يبني Codewhale في العلن الأشخاص الذين يستخدمونه.",
|
||||
@@ -48,6 +42,34 @@ export const home: HomeDict = {
|
||||
sectionStartHeading: "اجعل وكيلك ملكًا لك اليوم.",
|
||||
sectionStartBody: "ثبّت Codewhale وشغّله في مستودع بمفتاح نموذج لديك بالفعل أو بنموذج على حاسوبك. لا تحتاج إلى حساب، و`codewhale update` يبقيه محدّثًا.",
|
||||
|
||||
contributorsCount: {
|
||||
zero: "{n} مساهم",
|
||||
one: "{n} مساهم",
|
||||
two: "{n} مساهمان",
|
||||
few: "{n} مساهمين",
|
||||
many: "{n} مساهمًا",
|
||||
other: "{n} مساهم",
|
||||
},
|
||||
factsLabel: "Codewhale في لمحة",
|
||||
factRust: "مكتوب بلغة Rust",
|
||||
factLicense: "مفتوح المصدر، رخصة {license}",
|
||||
factProviders: {
|
||||
zero: "{n} مسار مزوّد",
|
||||
one: "مسار مزوّد واحد ({n})",
|
||||
two: "مساران لمزوّدين ({n})",
|
||||
few: "{n} مسارات لمزوّدين",
|
||||
many: "{n} مسارًا لمزوّدين",
|
||||
other: "{n} مسار مزوّد",
|
||||
},
|
||||
factModels: {
|
||||
zero: "{n} نموذج",
|
||||
one: "نموذج واحد ({n})",
|
||||
two: "نموذجان ({n})",
|
||||
few: "{n} نماذج",
|
||||
many: "{n} نموذجًا",
|
||||
other: "{n} نموذج",
|
||||
},
|
||||
|
||||
mapHeading: "محرك واحد",
|
||||
mapStateReleased: "منشور",
|
||||
mapStateDeveloping: "قيد التطوير، دون تنزيل",
|
||||
|
||||
@@ -4,43 +4,37 @@ import type { HomeDict } from "../types";
|
||||
|
||||
export const home: HomeDict = {
|
||||
metaTitle: "Codewhale · El model pot ser de qualsevol. L’agent hauria de ser teu.",
|
||||
metaDescription: "Codewhale s’està convertint en l’entorn obert on construeixes programari amb molts agents, amb qualsevol model, al teu propi ordinador i al núvol. Avui comença al teu terminal i al teu navegador, de codi obert i teu per llegir.",
|
||||
metaDescription: "Codewhale és un agent de programació de codi obert per al teu terminal, escrit en Rust. Funciona amb qualsevol model, pot repartir una tasca entre diversos agents i en pots llegir cada línia.",
|
||||
heroTitle: "El model pot ser de qualsevol. L’agent hauria de ser teu.",
|
||||
heroTitleLead: "El model pot ser de qualsevol.",
|
||||
heroIntro: "{brand} s’està convertint en l’entorn obert on construeixes programari amb molts agents, amb qualsevol model, al teu propi ordinador i al núvol. Avui comença al teu terminal i al teu navegador, de codi obert i teu per llegir.",
|
||||
heroIntro: "{brand} és un agent de programació de codi obert per al teu terminal, escrit en Rust. Funciona amb qualsevol model, pot repartir una tasca entre diversos agents i en pots llegir cada línia.",
|
||||
readTheCode: "Llegeix el codi",
|
||||
installNote: "La versió {version} està publicada i no necessita cap compte.",
|
||||
captionDevelopment: "Una versió de desenvolupament en un terminal real: les edicions i les proves es van executar de debò, i el model és una demostració amb guió.",
|
||||
captionReleased: "Una sessió real de terminal: les edicions i les proves es van executar de debò, i el model és una demostració amb guió.",
|
||||
captureFrameNoPosture: "El nom del model i el paràmetre Ask es mostren als fotogrames anterior i posterior a aquest.",
|
||||
captureFrameNoPosture: "Els fotogrames «Instrucció» i «Resultat» mostren el nom del model i el paràmetre Ask.",
|
||||
frameLabelPrompt: "Instrucció",
|
||||
frameLabelEdit: "Edició",
|
||||
frameLabelApproval: "Aprovació",
|
||||
frameLabelResult: "Resultat",
|
||||
frameLabelReceipt: "Rebut",
|
||||
frameSummaryPrompt: "Codewhale en un terminal. S’ha enviat la petició «Add a --json flag to ls.py so the tests pass, then run the tests.», i la línia d’estat mostra Ask i el model scripted-demo.",
|
||||
frameSummaryEdit: "Codewhale en un terminal. Ha llegit ls.py i l’ha canviat, el diff mostra les línies afegides i una targeta pregunta si s’ha d’executar python3 -m unittest -q.",
|
||||
frameSummaryApproval: "Codewhale en un terminal. Es veu el canvi a ls.py, i l’ordre python3 -m unittest -q espera aprovació. La targeta pregunta «Do you want to proceed?» i ofereix permetre-la una vegada, permetre-la en aquesta conversa o no permetre-la.",
|
||||
frameSummaryResult: "Codewhale en un terminal. Després de l’aprovació, l’ordre ha executat 2 proves i ha imprès OK. Codewhale diu que ha afegit --json a ls.py i que les dues proves passen, i que aquesta sessió fa servir un model local amb guió.",
|
||||
frameSummaryProvider: "Codewhale en un terminal. El formulari de proveïdor personalitzat per a un endpoint compatible amb OpenAI, amb el nom scripted_local, un URL base en aquest ordinador, el model per defecte scripted-demo i una variable opcional per a la clau d’API.",
|
||||
frameSummaryReceipt: "Codewhale en un terminal. El rebut de la sessió: 1 fitxer canviat i 1 ordre executada, l’ordre aprovada per tu i el canvi del fitxer fet sense preguntar a Ask, i després una llista del que el rebut no registra.",
|
||||
getCodewhale: "Instal·lar Codewhale",
|
||||
heroInstallAria: "Ordre d'instal·lació",
|
||||
shotPreview: "Vista prèvia del terminal",
|
||||
screenshotAlt:
|
||||
"Codewhale v{version}: captura del terminal amb la conversa, el camp de missatge i els controls de sessió.",
|
||||
proofContributors: "{count} col·laboradors",
|
||||
sourceCandidate: "Sense publicar",
|
||||
publishedRelease: "publicada",
|
||||
modelsHeading: "Fes servir els models que triïs",
|
||||
modelsLink: "Consulta els models i els proveïdors",
|
||||
startHeading: "Primers passos",
|
||||
copy: "Copia",
|
||||
copied: "Copiat ✓",
|
||||
contribute: "Contribueix a GitHub",
|
||||
|
||||
belief: "El model que fas servir sol ser d’algú altre. El lloges, i canvia sota els teus peus. L’agent és diferent. Llegeix els teus fitxers, executa les teves ordres, guarda el que estàs fent i actua en nom teu, així que hauria de pertànyer-te. Codewhale és aquest agent. És de codi obert, de manera que pots llegir-ne cada línia, o demanar-li que es llegeixi a si mateix per a tu. Funciona amb qualsevol model que triïs, tu decideixes què pot fer sense preguntar, i deixa un registre que pots llegir. Estem construint-lo com un sol lloc on tu i molts agents feu programari junts, des del teu terminal fins a un ordinador al núvol, i continua sent teu tot el camí.",
|
||||
|
||||
sectionModelHeading: "Qualsevol model pot treballar a través del teu agent.",
|
||||
sectionModelBody: "Fes servir un proveïdor integrat, qualsevol punt d’accés compatible amb OpenAI o un model a la teva pròpia màquina, i canvia de model entre torns. Codewhale s’executa al teu repositori sense compte, i `codewhale web` et permet treballar-hi des del navegador, a la teva pròpia màquina.",
|
||||
sectionControlHeading: "El teu agent respon davant teu.",
|
||||
sectionControlBody: "Tu decideixes què pot fer Codewhale sense preguntar, i les seves ordres poden executar-se en un entorn aïllat a macOS i Linux. Cada sessió deixa un rebut dels fitxers que ha canviat, de les ordres que ha executat i de qui ha aprovat cada pas.",
|
||||
sectionAgentsHeading: "Diversos agents poden compartir una tasca amb tu.",
|
||||
sectionAgentsBody: "Codewhale reparteix una feina entre agents amb rols com explorador, implementador i revisor, i pots observar, inspeccionar i aturar cadascun. Una Fleet manté un registre, de manera que una execució pot continuar després d’un reinici.",
|
||||
sectionDirectionHeading: "Cap aquí va el teu agent.",
|
||||
sectionDirectionBody: "Codewhale s’està convertint en un únic entorn que abasta el teu terminal, una aplicació d’escriptori, el navegador i ordinadors al núvol, tot mogut per un sol motor, perquè la teva feina pugui passar de l’un a l’altre i continuar sent teva. Cada part de sota diu on es troba avui.",
|
||||
sectionOpenHeading: "Codewhale es construeix en obert per les persones que l’utilitzen.",
|
||||
@@ -48,6 +42,24 @@ export const home: HomeDict = {
|
||||
sectionStartHeading: "Fes teu el teu agent avui.",
|
||||
sectionStartBody: "Instal·la Codewhale i executa’l en un repositori amb una clau de model que ja tinguis o un model al teu propi ordinador. No necessites cap compte, i `codewhale update` el manté al dia.",
|
||||
|
||||
contributorsCount: {
|
||||
one: "{n} col·laborador",
|
||||
other: "{n} col·laboradors",
|
||||
},
|
||||
factsLabel: "Codewhale d’un cop d’ull",
|
||||
factRust: "Escrit en Rust",
|
||||
factLicense: "Codi obert, llicència {license}",
|
||||
factProviders: {
|
||||
one: "{n} ruta de proveïdor",
|
||||
many: "{n} de rutes de proveïdor",
|
||||
other: "{n} rutes de proveïdor",
|
||||
},
|
||||
factModels: {
|
||||
one: "{n} model",
|
||||
many: "{n} de models",
|
||||
other: "{n} models",
|
||||
},
|
||||
|
||||
mapHeading: "Un sol motor",
|
||||
mapStateReleased: "Publicat",
|
||||
mapStateDeveloping: "En desenvolupament, sense descàrrega",
|
||||
|
||||
@@ -4,43 +4,37 @@ import type { HomeDict } from "../types";
|
||||
|
||||
export const home: HomeDict = {
|
||||
metaTitle: "Codewhale · Das Modell kann jedem gehören. Der Agent sollte dir gehören.",
|
||||
metaDescription: "Codewhale wird zu der offenen Umgebung, in der du Software mit vielen Agenten baust, mit jedem Modell, auf deinem eigenen Computer und in der Cloud. Heute beginnt es in deinem Terminal und in deinem Browser, quelloffen und für dich zum Lesen.",
|
||||
metaDescription: "Codewhale ist ein quelloffener Coding-Agent für dein Terminal, geschrieben in Rust. Er arbeitet mit jedem Modell, kann eine Aufgabe auf mehrere Agenten verteilen, und du kannst jede Zeile davon lesen.",
|
||||
heroTitle: "Das Modell kann jedem gehören. Der Agent sollte dir gehören.",
|
||||
heroTitleLead: "Das Modell kann jedem gehören.",
|
||||
heroIntro: "{brand} wird zu der offenen Umgebung, in der du Software mit vielen Agenten baust, mit jedem Modell, auf deinem eigenen Computer und in der Cloud. Heute beginnt es in deinem Terminal und in deinem Browser, quelloffen und für dich zum Lesen.",
|
||||
heroIntro: "{brand} ist ein quelloffener Coding-Agent für dein Terminal, geschrieben in Rust. Er arbeitet mit jedem Modell, kann eine Aufgabe auf mehrere Agenten verteilen, und du kannst jede Zeile davon lesen.",
|
||||
readTheCode: "Code lesen",
|
||||
installNote: "Version {version} ist veröffentlicht und braucht kein Konto.",
|
||||
captionDevelopment: "Ein Entwicklungs-Build in einem echten Terminal: Die Änderungen und Tests wurden wirklich ausgeführt, und das Modell ist eine Demo nach Drehbuch.",
|
||||
captionReleased: "Eine echte Terminalsitzung: Die Änderungen und Tests wurden wirklich ausgeführt, und das Modell ist eine Demo nach Drehbuch.",
|
||||
captureFrameNoPosture: "Der Modellname und die Ask-Einstellung sind auf den Bildern vor und nach diesem zu sehen.",
|
||||
captureFrameNoPosture: "Die Bilder „Eingabe“ und „Ergebnis“ zeigen den Modellnamen und die Ask-Einstellung.",
|
||||
frameLabelPrompt: "Eingabe",
|
||||
frameLabelEdit: "Änderung",
|
||||
frameLabelApproval: "Freigabe",
|
||||
frameLabelResult: "Ergebnis",
|
||||
frameLabelReceipt: "Beleg",
|
||||
frameSummaryPrompt: "Codewhale in einem Terminal. Die Anfrage „Add a --json flag to ls.py so the tests pass, then run the tests.“ wurde gesendet, und die Statuszeile zeigt Ask und das Modell scripted-demo.",
|
||||
frameSummaryEdit: "Codewhale in einem Terminal. Es hat ls.py gelesen und geändert, der Diff zeigt die hinzugefügten Zeilen, und eine Karte fragt, ob python3 -m unittest -q ausgeführt werden soll.",
|
||||
frameSummaryApproval: "Codewhale in einem Terminal. Die Änderung an ls.py ist zu sehen, und der Befehl python3 -m unittest -q wartet auf Freigabe. Die Karte fragt „Do you want to proceed?“ und bietet an, ihn einmal zu erlauben, für diese Unterhaltung zu erlauben oder nicht zu erlauben.",
|
||||
frameSummaryResult: "Codewhale in einem Terminal. Nach der Freigabe hat der Befehl 2 Tests ausgeführt und OK ausgegeben. Codewhale meldet, dass es --json zu ls.py hinzugefügt hat und beide Tests bestehen, und dass diese Sitzung ein lokales Modell nach Drehbuch nutzt.",
|
||||
frameSummaryProvider: "Codewhale in einem Terminal. Das Formular für einen eigenen Anbieter mit OpenAI-kompatiblem Endpunkt, mit dem Namen scripted_local, einer Basis-URL auf diesem Computer, dem Standardmodell scripted-demo und einer optionalen Variable für den API-Schlüssel.",
|
||||
frameSummaryReceipt: "Codewhale in einem Terminal. Der Beleg der Sitzung: 1 Datei geändert und 1 Befehl ausgeführt, der Befehl von dir freigegeben und die Dateiänderung unter Ask ohne Nachfrage gemacht, danach eine Liste dessen, was der Beleg nicht festhält.",
|
||||
getCodewhale: "Codewhale installieren",
|
||||
heroInstallAria: "Installationsbefehl",
|
||||
shotPreview: "Terminal-Vorschau",
|
||||
screenshotAlt:
|
||||
"Codewhale v{version}: eine Terminalaufnahme mit Gespräch, Nachrichteneingabe und Sitzungssteuerung.",
|
||||
proofContributors: "{count} Mitwirkende",
|
||||
sourceCandidate: "Unveröffentlicht",
|
||||
publishedRelease: "veröffentlicht",
|
||||
modelsHeading: "Nutze die Modelle deiner Wahl",
|
||||
modelsLink: "Modelle und Anbieter durchsuchen",
|
||||
startHeading: "Erste Schritte",
|
||||
copy: "Kopieren",
|
||||
copied: "Kopiert ✓",
|
||||
contribute: "Auf GitHub mitwirken",
|
||||
|
||||
belief: "Das Modell, das du nutzt, gehört meist jemand anderem. Du mietest es, und es verändert sich unter dir. Der Agent ist anders. Er liest deine Dateien, führt deine Befehle aus, hält fest, woran du arbeitest, und handelt in deinem Namen, also sollte er dir gehören. Codewhale ist dieser Agent. Er ist quelloffen, sodass du jede Zeile von ihm lesen oder ihn bitten kannst, sich selbst vorzulesen. Er arbeitet mit jedem Modell, das du wählst, du entscheidest, was er ohne Nachfrage tun darf, und er hinterlässt eine Aufzeichnung, die du lesen kannst. Wir bauen ihn zu einem Ort aus, an dem du und viele Agenten gemeinsam Software entwickeln, von deinem Terminal bis zu einem Computer in der Cloud, und er bleibt auf dem ganzen Weg deiner.",
|
||||
|
||||
sectionModelHeading: "Jedes Modell kann über deinen Agenten arbeiten.",
|
||||
sectionModelBody: "Nutze einen eingebauten Anbieter, einen beliebigen OpenAI-kompatiblen Endpunkt oder ein Modell auf deinem eigenen Rechner, und wechsle zwischen den Zügen das Modell. Codewhale läuft ohne Konto in deinem Repository, und mit `codewhale web` arbeitest du im Browser auf deinem eigenen Rechner damit.",
|
||||
sectionControlHeading: "Dein Agent steht dir Rede und Antwort.",
|
||||
sectionControlBody: "Du entscheidest, was Codewhale ohne Nachfrage tun darf, und seine Befehle können unter macOS und Linux in einer Sandbox laufen. Jede Sitzung hinterlässt einen Beleg über die geänderten Dateien, die ausgeführten Befehle und darüber, wer jeden Schritt freigegeben hat.",
|
||||
sectionAgentsHeading: "Mehrere Agenten können sich mit dir eine Aufgabe teilen.",
|
||||
sectionAgentsBody: "Codewhale verteilt eine Arbeit auf Agenten mit Rollen wie Erkunder, Umsetzer und Prüfer, und du kannst jeden beobachten, untersuchen und stoppen. Eine Fleet führt ein Protokollbuch, sodass ein Lauf nach einem Neustart wieder aufgenommen werden kann.",
|
||||
sectionDirectionHeading: "Hier geht dein Agent hin.",
|
||||
sectionDirectionBody: "Codewhale wird zu einer einzigen Umgebung über dein Terminal, eine Desktop-App, den Browser und Cloud-Computer hinweg, alles von einer Engine betrieben, damit deine Arbeit zwischen ihnen wechseln kann und deine bleibt. Jeder Teil unten sagt, wo er heute steht.",
|
||||
sectionOpenHeading: "Codewhale wird offen von den Menschen gebaut, die es nutzen.",
|
||||
@@ -48,6 +42,22 @@ export const home: HomeDict = {
|
||||
sectionStartHeading: "Mach deinen Agenten heute zu deinem.",
|
||||
sectionStartBody: "Installiere Codewhale und führe es in einem Repository mit einem Modellschlüssel aus, den du schon hast, oder mit einem Modell auf deinem eigenen Computer. Du brauchst kein Konto, und `codewhale update` hält es aktuell.",
|
||||
|
||||
contributorsCount: {
|
||||
one: "{n} mitwirkende Person",
|
||||
other: "{n} Mitwirkende",
|
||||
},
|
||||
factsLabel: "Codewhale auf einen Blick",
|
||||
factRust: "Geschrieben in Rust",
|
||||
factLicense: "Quelloffen, {license}-Lizenz",
|
||||
factProviders: {
|
||||
one: "{n} Anbieter-Route",
|
||||
other: "{n} Anbieter-Routen",
|
||||
},
|
||||
factModels: {
|
||||
one: "{n} Modell",
|
||||
other: "{n} Modelle",
|
||||
},
|
||||
|
||||
mapHeading: "Eine Engine",
|
||||
mapStateReleased: "Veröffentlicht",
|
||||
mapStateDeveloping: "In Entwicklung, kein Download",
|
||||
|
||||
@@ -3,35 +3,36 @@ import type { HomeDict } from "../types";
|
||||
/** English reference home copy: useful work with chosen models and connected tools. */
|
||||
export const home: HomeDict = {
|
||||
metaTitle: "Codewhale · The model can be anyone's. The agent should be yours.",
|
||||
metaDescription: "Codewhale is becoming the open environment where you build software with many agents, on any model, on your own computer and in the cloud. It starts today in your terminal and your browser, open source and yours to read.",
|
||||
metaDescription: "Codewhale is an open-source coding agent for your terminal, written in Rust. It works with any model, can split one task across several agents, and every line of it is yours to read.",
|
||||
|
||||
heroTitle: "The model can be anyone's. The agent should be yours.",
|
||||
heroTitleLead: "The model can be anyone's.",
|
||||
heroIntro: "{brand} is becoming the open environment where you build software with many agents, on any model, on your own computer and in the cloud. It starts today in your terminal and your browser, open source and yours to read.",
|
||||
heroIntro: "{brand} is an open-source coding agent for your terminal, written in Rust. It works with any model, can split one task across several agents, and every line of it is yours to read.",
|
||||
readTheCode: "Read the code",
|
||||
installNote: "Version {version} is released, and it needs no account.",
|
||||
captionDevelopment: "A development build in a real terminal: the edits and tests ran for real, and the model is a scripted demo.",
|
||||
captionReleased: "A real terminal session: the edits and tests ran for real, and the model is a scripted demo.",
|
||||
captureFrameNoPosture: "The model name and the Ask setting are shown on the frames before and after this one.",
|
||||
captureFrameNoPosture: "The Prompt and Result frames show the model name and the Ask setting.",
|
||||
frameLabelPrompt: "Prompt",
|
||||
frameLabelEdit: "Edit",
|
||||
frameLabelApproval: "Approval",
|
||||
frameLabelResult: "Result",
|
||||
frameLabelReceipt: "Receipt",
|
||||
frameSummaryPrompt: "Codewhale in a terminal. The request “Add a --json flag to ls.py so the tests pass, then run the tests.” has been sent, and the status line shows Ask and the model scripted-demo.",
|
||||
frameSummaryEdit: "Codewhale in a terminal. It read ls.py and changed it, the diff shows the added lines, and a card asks whether to run python3 -m unittest -q.",
|
||||
frameSummaryApproval: "Codewhale in a terminal. The change to ls.py is shown, and the command python3 -m unittest -q waits for approval. The card asks “Do you want to proceed?” and offers to allow it once, allow it for this conversation or not allow it.",
|
||||
frameSummaryResult: "Codewhale in a terminal. After approval the command ran 2 tests and printed OK. Codewhale says it added --json to ls.py and both tests pass, and that this session uses a scripted local model.",
|
||||
frameSummaryProvider: "Codewhale in a terminal. The Custom provider form for an OpenAI-compatible endpoint, with the name scripted_local, a base URL on this computer, the default model scripted-demo and an optional API key variable.",
|
||||
frameSummaryReceipt: "Codewhale in a terminal. The receipt of the session: 1 file changed and 1 command run, the command approved by you and the file change made without asking under Ask, then a list of what the receipt does not record.",
|
||||
getCodewhale: "Install Codewhale",
|
||||
heroInstallAria: "Install command",
|
||||
shotPreview: "Local terminal demo",
|
||||
screenshotAlt:
|
||||
"Codewhale v{version}: a captured terminal view with the conversation, message composer and session controls.",
|
||||
|
||||
belief: "The model you use is usually someone else's. You rent it, and it changes under you. The agent is different. It reads your files, runs your commands, holds what you are working on and acts in your name, so it should belong to you. Codewhale is that agent. It is open source, so you can read every line of it, or ask it to read itself to you. It works with any model you choose, you decide what it may do without asking, and it leaves a record you can read. We are building it into one place where you and many agents make software together, from your terminal to a computer in the cloud, and it stays yours the whole way.",
|
||||
|
||||
sectionModelHeading: "Any model can work through your agent.",
|
||||
sectionModelBody: "Use a built-in provider, any OpenAI-compatible endpoint or a model on your own machine, and change models between turns. Codewhale runs in your repository with no account, and `codewhale web` lets you work with it in your browser on your own machine.",
|
||||
sectionControlHeading: "Your agent answers to you.",
|
||||
sectionControlBody: "You decide what Codewhale may do without asking, and its commands can run in a sandbox on macOS and Linux. Every session leaves a receipt of the files it changed, the commands it ran and who approved each step.",
|
||||
sectionAgentsHeading: "Several agents can share one task with you.",
|
||||
sectionAgentsBody: "Codewhale splits a job across agents with roles such as explorer, implementer and reviewer, and you can watch, inspect and stop each one. A Fleet keeps a ledger, so a run can pick up again after a restart.",
|
||||
sectionDirectionHeading: "Here is where your agent is going.",
|
||||
sectionDirectionBody: "Codewhale is becoming one environment across your terminal, a desktop app, the browser and cloud computers, all run by one engine, so your work can move between them and stay yours. Each part below says where it stands today.",
|
||||
sectionOpenHeading: "Codewhale is built in the open by the people who use it.",
|
||||
@@ -39,6 +40,22 @@ export const home: HomeDict = {
|
||||
sectionStartHeading: "Make your agent yours today.",
|
||||
sectionStartBody: "Install Codewhale and run it in a repository with a model key you already have or a model on your own computer. You need no account, and `codewhale update` keeps it current.",
|
||||
|
||||
contributorsCount: {
|
||||
one: "{n} contributor",
|
||||
other: "{n} contributors",
|
||||
},
|
||||
factsLabel: "Codewhale at a glance",
|
||||
factRust: "Written in Rust",
|
||||
factLicense: "Open source, {license} license",
|
||||
factProviders: {
|
||||
one: "{n} provider route",
|
||||
other: "{n} provider routes",
|
||||
},
|
||||
factModels: {
|
||||
one: "{n} model",
|
||||
other: "{n} models",
|
||||
},
|
||||
|
||||
mapHeading: "One engine",
|
||||
mapStateReleased: "Released",
|
||||
mapStateDeveloping: "In development, no download",
|
||||
@@ -57,15 +74,6 @@ export const home: HomeDict = {
|
||||
mapOneSession: "One session across them",
|
||||
mapOneSessionSentence: "We are building one session that goes with you from the terminal to the desktop, the browser and the cloud, and stays yours the whole way.",
|
||||
|
||||
proofContributors: "{count} contributors",
|
||||
sourceCandidate: "Unreleased",
|
||||
publishedRelease: "released",
|
||||
|
||||
modelsHeading: "Use the models you choose",
|
||||
modelsLink: "Browse models and providers",
|
||||
|
||||
startHeading: "Start with one useful task",
|
||||
copy: "Copy",
|
||||
copied: "Copied ✓",
|
||||
contribute: "Contribute",
|
||||
};
|
||||
@@ -4,43 +4,37 @@ import type { HomeDict } from "../types";
|
||||
|
||||
export const home: HomeDict = {
|
||||
metaTitle: "Codewhale · El modelo puede ser de cualquiera. El agente debería ser tuyo.",
|
||||
metaDescription: "Codewhale se está convirtiendo en el entorno abierto donde construyes software con muchos agentes, con cualquier modelo, en tu propio ordenador y en la nube. Empieza hoy en tu terminal y en tu navegador, de código abierto y tuyo para leer.",
|
||||
metaDescription: "Codewhale es un agente de programación de código abierto para tu terminal, escrito en Rust. Funciona con cualquier modelo, puede repartir una tarea entre varios agentes y puedes leer cada línea de su código.",
|
||||
heroTitle: "El modelo puede ser de cualquiera. El agente debería ser tuyo.",
|
||||
heroTitleLead: "El modelo puede ser de cualquiera.",
|
||||
heroIntro: "{brand} se está convirtiendo en el entorno abierto donde construyes software con muchos agentes, con cualquier modelo, en tu propio ordenador y en la nube. Empieza hoy en tu terminal y en tu navegador, de código abierto y tuyo para leer.",
|
||||
heroIntro: "{brand} es un agente de programación de código abierto para tu terminal, escrito en Rust. Funciona con cualquier modelo, puede repartir una tarea entre varios agentes y puedes leer cada línea de su código.",
|
||||
readTheCode: "Lee el código",
|
||||
installNote: "La versión {version} está publicada y no necesita cuenta.",
|
||||
captionDevelopment: "Una versión de desarrollo en un terminal real: las ediciones y las pruebas se ejecutaron de verdad, y el modelo es una demostración con guion.",
|
||||
captionReleased: "Una sesión real de terminal: las ediciones y las pruebas se ejecutaron de verdad, y el modelo es una demostración con guion.",
|
||||
captureFrameNoPosture: "El nombre del modelo y el ajuste Ask se muestran en los fotogramas anterior y posterior a este.",
|
||||
captureFrameNoPosture: "Los fotogramas «Instrucción» y «Resultado» muestran el nombre del modelo y el ajuste Ask.",
|
||||
frameLabelPrompt: "Instrucción",
|
||||
frameLabelEdit: "Edición",
|
||||
frameLabelApproval: "Aprobación",
|
||||
frameLabelResult: "Resultado",
|
||||
frameLabelReceipt: "Recibo",
|
||||
frameSummaryPrompt: "Codewhale en una terminal. Se ha enviado la petición «Add a --json flag to ls.py so the tests pass, then run the tests.», y la línea de estado muestra Ask y el modelo scripted-demo.",
|
||||
frameSummaryEdit: "Codewhale en una terminal. Ha leído ls.py y lo ha cambiado, el diff muestra las líneas añadidas y una tarjeta pregunta si se ejecuta python3 -m unittest -q.",
|
||||
frameSummaryApproval: "Codewhale en una terminal. Se ve el cambio en ls.py, y el comando python3 -m unittest -q espera aprobación. La tarjeta pregunta «Do you want to proceed?» y ofrece permitirlo una vez, permitirlo en esta conversación o no permitirlo.",
|
||||
frameSummaryResult: "Codewhale en una terminal. Tras la aprobación, el comando ejecutó 2 pruebas e imprimió OK. Codewhale dice que añadió --json a ls.py y que las dos pruebas pasan, y que esta sesión usa un modelo local con guion.",
|
||||
frameSummaryProvider: "Codewhale en una terminal. El formulario de proveedor personalizado para un endpoint compatible con OpenAI, con el nombre scripted_local, una URL base en este ordenador, el modelo predeterminado scripted-demo y una variable opcional para la clave de API.",
|
||||
frameSummaryReceipt: "Codewhale en una terminal. El recibo de la sesión: 1 archivo cambiado y 1 comando ejecutado, el comando aprobado por ti y el cambio del archivo hecho sin preguntar en Ask, y después una lista de lo que el recibo no registra.",
|
||||
getCodewhale: "Instalar Codewhale",
|
||||
heroInstallAria: "Comando de instalación",
|
||||
shotPreview: "Vista previa de la terminal",
|
||||
screenshotAlt:
|
||||
"Codewhale v{version}: captura del terminal con la conversación, el campo de mensaje y los controles de sesión.",
|
||||
proofContributors: "{count} colaboradores",
|
||||
sourceCandidate: "Sin publicar",
|
||||
publishedRelease: "publicado",
|
||||
modelsHeading: "Usa los modelos que elijas",
|
||||
modelsLink: "Ver modelos y proveedores",
|
||||
startHeading: "Primeros pasos",
|
||||
copy: "Copiar",
|
||||
copied: "Copiado ✓",
|
||||
contribute: "Contribuye en GitHub",
|
||||
|
||||
belief: "El modelo que usas suele ser de otra persona. Lo alquilas y cambia sin que lo decidas. El agente es distinto. Lee tus archivos, ejecuta tus comandos, guarda lo que estás haciendo y actúa en tu nombre, así que debería pertenecerte. Codewhale es ese agente. Es de código abierto, así que puedes leer cada línea, o pedirle que se lea a sí mismo para ti. Funciona con cualquier modelo que elijas, tú decides lo que puede hacer sin preguntar, y deja un registro que puedes leer. Lo estamos construyendo para que sea un solo lugar donde tú y muchos agentes hacéis software juntos, desde tu terminal hasta un ordenador en la nube, y siga siendo tuyo en todo el camino.",
|
||||
|
||||
sectionModelHeading: "Cualquier modelo puede trabajar a través de tu agente.",
|
||||
sectionModelBody: "Usa un proveedor integrado, cualquier endpoint compatible con OpenAI o un modelo en tu propia máquina, y cambia de modelo entre turnos. Codewhale se ejecuta en tu repositorio sin cuenta, y `codewhale web` te permite trabajar con él en tu navegador, en tu propia máquina.",
|
||||
sectionControlHeading: "Tu agente responde ante ti.",
|
||||
sectionControlBody: "Tú decides lo que Codewhale puede hacer sin preguntar, y sus comandos pueden ejecutarse en un sandbox en macOS y Linux. Cada sesión deja un recibo con los archivos que cambió, los comandos que ejecutó y quién aprobó cada paso.",
|
||||
sectionAgentsHeading: "Varios agentes pueden compartir una tarea contigo.",
|
||||
sectionAgentsBody: "Codewhale reparte un trabajo entre agentes con roles como explorador, implementador y revisor, y puedes observar, inspeccionar y detener a cada uno. Una Fleet mantiene un registro, de modo que una ejecución puede continuar tras un reinicio.",
|
||||
sectionDirectionHeading: "Hacia aquí va tu agente.",
|
||||
sectionDirectionBody: "Codewhale se está convirtiendo en un único entorno que abarca tu terminal, una aplicación de escritorio, el navegador y ordenadores en la nube, todo gobernado por un solo motor, para que tu trabajo pueda moverse entre ellos y seguir siendo tuyo. Cada parte de abajo indica en qué punto está hoy.",
|
||||
sectionOpenHeading: "Codewhale se construye en abierto por las personas que lo usan.",
|
||||
@@ -48,6 +42,24 @@ export const home: HomeDict = {
|
||||
sectionStartHeading: "Haz tuyo tu agente hoy.",
|
||||
sectionStartBody: "Instala Codewhale y ejecútalo en un repositorio con una clave de modelo que ya tengas o con un modelo en tu propio ordenador. No necesitas cuenta, y `codewhale update` lo mantiene al día.",
|
||||
|
||||
contributorsCount: {
|
||||
one: "{n} colaborador",
|
||||
other: "{n} colaboradores",
|
||||
},
|
||||
factsLabel: "Codewhale de un vistazo",
|
||||
factRust: "Escrito en Rust",
|
||||
factLicense: "Código abierto, licencia {license}",
|
||||
factProviders: {
|
||||
one: "{n} ruta de proveedor",
|
||||
many: "{n} de rutas de proveedor",
|
||||
other: "{n} rutas de proveedor",
|
||||
},
|
||||
factModels: {
|
||||
one: "{n} modelo",
|
||||
many: "{n} de modelos",
|
||||
other: "{n} modelos",
|
||||
},
|
||||
|
||||
mapHeading: "Un solo motor",
|
||||
mapStateReleased: "Publicado",
|
||||
mapStateDeveloping: "En desarrollo, sin descarga",
|
||||
|
||||
@@ -4,43 +4,37 @@ import type { HomeDict } from "../types";
|
||||
|
||||
export const home: HomeDict = {
|
||||
metaTitle: "Codewhale · Le modèle peut être à n’importe qui. L’agent devrait être à vous.",
|
||||
metaDescription: "Codewhale devient l’environnement ouvert où vous créez des logiciels avec de nombreux agents, sur n’importe quel modèle, sur votre propre ordinateur et dans le cloud. Il commence aujourd’hui dans votre terminal et votre navigateur, open source et à lire par vous-même.",
|
||||
metaDescription: "Codewhale est un agent de code open source pour votre terminal, écrit en Rust. Il fonctionne avec n’importe quel modèle, peut répartir une tâche entre plusieurs agents, et chaque ligne de son code est à lire par vous.",
|
||||
heroTitle: "Le modèle peut être à n’importe qui. L’agent devrait être à vous.",
|
||||
heroTitleLead: "Le modèle peut être à n’importe qui.",
|
||||
heroIntro: "{brand} devient l’environnement ouvert où vous créez des logiciels avec de nombreux agents, sur n’importe quel modèle, sur votre propre ordinateur et dans le cloud. Il commence aujourd’hui dans votre terminal et votre navigateur, open source et à lire par vous-même.",
|
||||
heroIntro: "{brand} est un agent de code open source pour votre terminal, écrit en Rust. Il fonctionne avec n’importe quel modèle, peut répartir une tâche entre plusieurs agents, et chaque ligne de son code est à lire par vous.",
|
||||
readTheCode: "Lire le code",
|
||||
installNote: "La version {version} est publiée et ne demande aucun compte.",
|
||||
captionDevelopment: "Une version de développement dans un vrai terminal : les modifications et les tests ont réellement été exécutés, et le modèle est une démonstration scénarisée.",
|
||||
captionReleased: "Une vraie session de terminal : les modifications et les tests ont réellement été exécutés, et le modèle est une démonstration scénarisée.",
|
||||
captureFrameNoPosture: "Le nom du modèle et le réglage Ask sont visibles sur les images qui précèdent et suivent celle-ci.",
|
||||
captureFrameNoPosture: "Les images « Requête » et « Résultat » montrent le nom du modèle et le réglage Ask.",
|
||||
frameLabelPrompt: "Requête",
|
||||
frameLabelEdit: "Modification",
|
||||
frameLabelApproval: "Approbation",
|
||||
frameLabelResult: "Résultat",
|
||||
frameLabelReceipt: "Reçu",
|
||||
frameSummaryPrompt: "Codewhale dans un terminal. La demande « Add a --json flag to ls.py so the tests pass, then run the tests. » a été envoyée, et la ligne d’état affiche Ask et le modèle scripted-demo.",
|
||||
frameSummaryEdit: "Codewhale dans un terminal. Il a lu ls.py et l’a modifié, le diff montre les lignes ajoutées, et une carte demande s’il faut exécuter python3 -m unittest -q.",
|
||||
frameSummaryApproval: "Codewhale dans un terminal. La modification de ls.py est affichée, et la commande python3 -m unittest -q attend une approbation. La carte demande « Do you want to proceed? » et propose de l’autoriser une fois, de l’autoriser pour cette conversation ou de ne pas l’autoriser.",
|
||||
frameSummaryResult: "Codewhale dans un terminal. Après approbation, la commande a exécuté 2 tests et affiché OK. Codewhale indique qu’il a ajouté --json à ls.py et que les deux tests passent, et que cette session utilise un modèle local scénarisé.",
|
||||
frameSummaryProvider: "Codewhale dans un terminal. Le formulaire de fournisseur personnalisé pour un point d’accès compatible OpenAI, avec le nom scripted_local, une URL de base sur cet ordinateur, le modèle par défaut scripted-demo et une variable facultative pour la clé d’API.",
|
||||
frameSummaryReceipt: "Codewhale dans un terminal. Le reçu de la session : 1 fichier modifié et 1 commande exécutée, la commande approuvée par vous et la modification du fichier faite sans demander sous Ask, puis la liste de ce que le reçu n’enregistre pas.",
|
||||
getCodewhale: "Installer Codewhale",
|
||||
heroInstallAria: "Commande d'installation",
|
||||
shotPreview: "Aperçu du terminal",
|
||||
screenshotAlt:
|
||||
"Codewhale v{version} : capture du terminal avec la conversation, le champ de message et les commandes de session.",
|
||||
proofContributors: "{count} contributeurs",
|
||||
sourceCandidate: "Non publiée",
|
||||
publishedRelease: "publiée",
|
||||
modelsHeading: "Utilisez les modèles de votre choix",
|
||||
modelsLink: "Parcourir les modèles et les fournisseurs",
|
||||
startHeading: "Premiers pas",
|
||||
copy: "Copier",
|
||||
copied: "Copié ✓",
|
||||
contribute: "Contribuer sur GitHub",
|
||||
|
||||
belief: "Le modèle que vous utilisez appartient le plus souvent à quelqu’un d’autre. Vous le louez, et il change sans que vous l’ayez décidé. L’agent, lui, est différent. Il lit vos fichiers, exécute vos commandes, garde ce sur quoi vous travaillez et agit en votre nom : il doit donc vous appartenir. Codewhale est cet agent. Il est open source, vous pouvez donc en lire chaque ligne, ou lui demander de se lire lui-même pour vous. Il fonctionne avec n’importe quel modèle de votre choix, vous décidez ce qu’il peut faire sans demander, et il laisse une trace que vous pouvez lire. Nous le construisons pour qu’il devienne un lieu unique où vous et de nombreux agents créez des logiciels ensemble, de votre terminal jusqu’à un ordinateur dans le cloud, et il reste à vous tout au long du chemin.",
|
||||
|
||||
sectionModelHeading: "N’importe quel modèle peut travailler par l’intermédiaire de votre agent.",
|
||||
sectionModelBody: "Utilisez un fournisseur intégré, n’importe quel point d’accès compatible OpenAI ou un modèle sur votre propre machine, et changez de modèle entre deux tours. Codewhale s’exécute dans votre dépôt sans compte, et `codewhale web` vous permet de travailler avec lui dans votre navigateur, sur votre propre machine.",
|
||||
sectionControlHeading: "Votre agent vous répond.",
|
||||
sectionControlBody: "Vous décidez de ce que Codewhale peut faire sans demander, et ses commandes peuvent s’exécuter dans un bac à sable sous macOS et Linux. Chaque session laisse un reçu des fichiers modifiés, des commandes exécutées et de la personne qui a approuvé chaque étape.",
|
||||
sectionAgentsHeading: "Plusieurs agents peuvent partager une tâche avec vous.",
|
||||
sectionAgentsBody: "Codewhale répartit un travail entre des agents ayant des rôles tels qu’explorateur, implémenteur et relecteur, et vous pouvez suivre, inspecter et arrêter chacun d’eux. Une Fleet tient un registre, de sorte qu’une exécution peut reprendre après un redémarrage.",
|
||||
sectionDirectionHeading: "Voici où va votre agent.",
|
||||
sectionDirectionBody: "Codewhale devient un seul environnement qui couvre votre terminal, une application de bureau, le navigateur et des ordinateurs dans le cloud, le tout animé par un seul moteur, pour que votre travail puisse passer de l’un à l’autre et rester à vous. Chaque partie ci-dessous indique où elle en est aujourd’hui.",
|
||||
sectionOpenHeading: "Codewhale est construit au grand jour par les personnes qui l’utilisent.",
|
||||
@@ -48,6 +42,24 @@ export const home: HomeDict = {
|
||||
sectionStartHeading: "Faites de votre agent le vôtre dès aujourd’hui.",
|
||||
sectionStartBody: "Installez Codewhale et lancez-le dans un dépôt avec une clé de modèle que vous avez déjà ou un modèle sur votre propre ordinateur. Vous n’avez besoin d’aucun compte, et `codewhale update` le tient à jour.",
|
||||
|
||||
contributorsCount: {
|
||||
one: "{n} contributeur",
|
||||
other: "{n} contributeurs",
|
||||
},
|
||||
factsLabel: "Codewhale en bref",
|
||||
factRust: "Écrit en Rust",
|
||||
factLicense: "Open source, licence {license}",
|
||||
factProviders: {
|
||||
one: "{n} route de fournisseur",
|
||||
many: "{n} de routes de fournisseurs",
|
||||
other: "{n} routes de fournisseurs",
|
||||
},
|
||||
factModels: {
|
||||
one: "{n} modèle",
|
||||
many: "{n} de modèles",
|
||||
other: "{n} modèles",
|
||||
},
|
||||
|
||||
mapHeading: "Un seul moteur",
|
||||
mapStateReleased: "Publié",
|
||||
mapStateDeveloping: "En développement, sans téléchargement",
|
||||
|
||||
@@ -4,43 +4,37 @@ import type { HomeDict } from "../types";
|
||||
|
||||
export const home: HomeDict = {
|
||||
metaTitle: "Codewhale · मॉडल किसी का भी हो सकता है। एजेंट आपका होना चाहिए।",
|
||||
metaDescription: "Codewhale वह खुला वातावरण बन रहा है जहाँ आप कई एजेंटों के साथ, किसी भी मॉडल पर, अपने कंप्यूटर और क्लाउड में सॉफ़्टवेयर बनाते हैं। यह आज आपके टर्मिनल और ब्राउज़र में शुरू होता है, ओपन सोर्स है और इसे पढ़ना आपके हाथ में है।",
|
||||
metaDescription: "Codewhale आपके टर्मिनल के लिए एक ओपन सोर्स कोडिंग एजेंट है, जो Rust में लिखा गया है। यह किसी भी मॉडल के साथ काम करता है, एक काम को कई एजेंटों में बाँट सकता है, और इसकी हर पंक्ति आप पढ़ सकते हैं।",
|
||||
heroTitle: "मॉडल किसी का भी हो सकता है। एजेंट आपका होना चाहिए।",
|
||||
heroTitleLead: "मॉडल किसी का भी हो सकता है।",
|
||||
heroIntro: "{brand} वह खुला वातावरण बन रहा है जहाँ आप कई एजेंटों के साथ, किसी भी मॉडल पर, अपने कंप्यूटर और क्लाउड में सॉफ़्टवेयर बनाते हैं। यह आज आपके टर्मिनल और ब्राउज़र में शुरू होता है, ओपन सोर्स है और इसे पढ़ना आपके हाथ में है।",
|
||||
heroIntro: "{brand} आपके टर्मिनल के लिए एक ओपन सोर्स कोडिंग एजेंट है, जो Rust में लिखा गया है। यह किसी भी मॉडल के साथ काम करता है, एक काम को कई एजेंटों में बाँट सकता है, और इसकी हर पंक्ति आप पढ़ सकते हैं।",
|
||||
readTheCode: "कोड पढ़ें",
|
||||
installNote: "संस्करण {version} जारी हो चुका है, और इसके लिए खाते की ज़रूरत नहीं है।",
|
||||
captionDevelopment: "असली टर्मिनल में एक डेवलपमेंट बिल्ड: संपादन और परीक्षण सचमुच चले, और मॉडल एक स्क्रिप्ट किया हुआ डेमो है।",
|
||||
captionReleased: "एक असली टर्मिनल सत्र: संपादन और परीक्षण सचमुच चले, और मॉडल एक स्क्रिप्ट किया हुआ डेमो है।",
|
||||
captureFrameNoPosture: "मॉडल का नाम और Ask सेटिंग इस फ़्रेम से पहले और बाद के फ़्रेम में दिखाई गई हैं।",
|
||||
captureFrameNoPosture: "“प्रॉम्प्ट” और “परिणाम” फ़्रेम में मॉडल का नाम और Ask सेटिंग दिखाई गई हैं।",
|
||||
frameLabelPrompt: "प्रॉम्प्ट",
|
||||
frameLabelEdit: "संपादन",
|
||||
frameLabelApproval: "स्वीकृति",
|
||||
frameLabelResult: "परिणाम",
|
||||
frameLabelReceipt: "रसीद",
|
||||
frameSummaryPrompt: "टर्मिनल में Codewhale। अनुरोध “Add a --json flag to ls.py so the tests pass, then run the tests.” भेजा जा चुका है, और स्थिति पंक्ति में Ask और मॉडल scripted-demo दिख रहे हैं।",
|
||||
frameSummaryEdit: "टर्मिनल में Codewhale। इसने ls.py पढ़कर उसे बदला, diff में जोड़ी गई पंक्तियाँ दिख रही हैं, और एक कार्ड पूछ रहा है कि python3 -m unittest -q चलाया जाए या नहीं।",
|
||||
frameSummaryApproval: "टर्मिनल में Codewhale। ls.py का बदलाव दिख रहा है, और कमांड python3 -m unittest -q स्वीकृति की प्रतीक्षा में है। कार्ड पूछता है “Do you want to proceed?” और इसे एक बार अनुमति देने, इस बातचीत के लिए अनुमति देने या अनुमति न देने के विकल्प देता है।",
|
||||
frameSummaryResult: "टर्मिनल में Codewhale। स्वीकृति के बाद कमांड ने 2 परीक्षण चलाए और OK छापा। Codewhale बताता है कि उसने ls.py में --json जोड़ा और दोनों परीक्षण पास हैं, और यह सत्र एक स्क्रिप्ट किए हुए स्थानीय मॉडल का उपयोग करता है।",
|
||||
frameSummaryProvider: "टर्मिनल में Codewhale। OpenAI-संगत एंडपॉइंट के लिए कस्टम प्रदाता फ़ॉर्म, जिसमें नाम scripted_local, इसी कंप्यूटर का बेस URL, डिफ़ॉल्ट मॉडल scripted-demo और API कुंजी के लिए एक वैकल्पिक वेरिएबल है।",
|
||||
frameSummaryReceipt: "टर्मिनल में Codewhale। सत्र की रसीद: 1 फ़ाइल बदली और 1 कमांड चला, कमांड को आपने मंज़ूरी दी और फ़ाइल का बदलाव Ask में बिना पूछे हुआ, फिर उन बातों की सूची जो रसीद दर्ज नहीं करती।",
|
||||
getCodewhale: "Codewhale इंस्टॉल करें",
|
||||
heroInstallAria: "इंस्टॉल कमांड",
|
||||
shotPreview: "टर्मिनल पूर्वावलोकन",
|
||||
screenshotAlt:
|
||||
"Codewhale v{version}: बातचीत, संदेश बॉक्स और सत्र नियंत्रण दिखाने वाला टर्मिनल कैप्चर।",
|
||||
proofContributors: "{count} योगदानकर्ता",
|
||||
sourceCandidate: "अप्रकाशित",
|
||||
publishedRelease: "प्रकाशित",
|
||||
modelsHeading: "अपनी पसंद के मॉडल इस्तेमाल करें",
|
||||
modelsLink: "मॉडल और प्रदाता ब्राउज़ करें",
|
||||
startHeading: "शुरुआत करें",
|
||||
copy: "कॉपी करें",
|
||||
copied: "कॉपी हो गया ✓",
|
||||
contribute: "GitHub पर योगदान दें",
|
||||
|
||||
belief: "जो मॉडल आप इस्तेमाल करते हैं, वह आम तौर पर किसी और का होता है। आप उसे किराये पर लेते हैं, और वह आपके नीचे से बदल जाता है। एजेंट अलग है। वह आपकी फ़ाइलें पढ़ता है, आपके कमांड चलाता है, जिस पर आप काम कर रहे हैं उसे सँभालकर रखता है और आपके नाम से काम करता है, इसलिए उसे आपका होना चाहिए। Codewhale वही एजेंट है। यह ओपन सोर्स है, इसलिए आप इसकी हर पंक्ति पढ़ सकते हैं, या इससे कह सकते हैं कि यह खुद को आपके लिए पढ़ दे। यह आपके चुने किसी भी मॉडल के साथ काम करता है, बिना पूछे यह क्या कर सकता है यह आप तय करते हैं, और यह एक ऐसा रिकॉर्ड छोड़ता है जिसे आप पढ़ सकते हैं। हम इसे एक ऐसी जगह बना रहे हैं जहाँ आप और कई एजेंट मिलकर सॉफ़्टवेयर बनाते हैं, आपके टर्मिनल से लेकर क्लाउड के एक कंप्यूटर तक, और यह पूरे रास्ते आपका ही रहता है।",
|
||||
|
||||
sectionModelHeading: "कोई भी मॉडल आपके एजेंट के ज़रिए काम कर सकता है।",
|
||||
sectionModelBody: "बिल्ट-इन प्रदाता, कोई भी OpenAI-संगत एंडपॉइंट या अपनी मशीन पर चल रहा मॉडल इस्तेमाल करें, और टर्नों के बीच मॉडल बदलें। Codewhale बिना खाते के आपकी रिपॉज़िटरी में चलता है, और `codewhale web` से आप अपनी ही मशीन पर ब्राउज़र में इसके साथ काम कर सकते हैं।",
|
||||
sectionControlHeading: "आपका एजेंट आपके प्रति जवाबदेह है।",
|
||||
sectionControlBody: "बिना पूछे Codewhale क्या कर सकता है, यह आप तय करते हैं, और इसके कमांड macOS और Linux पर सैंडबॉक्स में चल सकते हैं। हर सत्र एक रसीद छोड़ता है जिसमें बदली गई फ़ाइलें, चलाए गए कमांड और हर क़दम को किसने मंज़ूरी दी, यह दर्ज होता है।",
|
||||
sectionAgentsHeading: "कई एजेंट एक काम में आपके साथ साझेदारी कर सकते हैं।",
|
||||
sectionAgentsBody: "Codewhale एक काम को खोजकर्ता, कार्यान्वयनकर्ता और समीक्षक जैसी भूमिकाओं वाले एजेंटों में बाँटता है, और आप हर एक को देख सकते हैं, जाँच सकते हैं और रोक सकते हैं। Fleet एक बही रखता है, इसलिए रीस्टार्ट के बाद भी रन फिर से शुरू हो सकता है।",
|
||||
sectionDirectionHeading: "आपका एजेंट यहाँ जा रहा है।",
|
||||
sectionDirectionBody: "Codewhale एक ऐसा वातावरण बन रहा है जो आपके टर्मिनल, डेस्कटॉप ऐप, ब्राउज़र और क्लाउड कंप्यूटरों में फैला है, सब एक इंजन से चलता है, ताकि आपका काम इनके बीच जा सके और आपका ही रहे। नीचे का हर हिस्सा बताता है कि वह आज किस स्थिति में है।",
|
||||
sectionOpenHeading: "Codewhale को इसे इस्तेमाल करने वाले लोग खुले में बनाते हैं।",
|
||||
@@ -48,6 +42,22 @@ export const home: HomeDict = {
|
||||
sectionStartHeading: "आज ही अपने एजेंट को अपना बनाइए।",
|
||||
sectionStartBody: "Codewhale इंस्टॉल करें और इसे ऐसी रिपॉज़िटरी में चलाएँ जिसमें आपके पास पहले से मौजूद मॉडल कुंजी हो या आपके अपने कंप्यूटर पर चलने वाला मॉडल हो। आपको खाते की ज़रूरत नहीं है, और `codewhale update` इसे अद्यतन रखता है।",
|
||||
|
||||
contributorsCount: {
|
||||
one: "{n} योगदानकर्ता",
|
||||
other: "{n} योगदानकर्ता",
|
||||
},
|
||||
factsLabel: "एक नज़र में Codewhale",
|
||||
factRust: "Rust में लिखा गया",
|
||||
factLicense: "ओपन सोर्स, {license} लाइसेंस",
|
||||
factProviders: {
|
||||
one: "{n} प्रदाता रूट",
|
||||
other: "{n} प्रदाता रूट",
|
||||
},
|
||||
factModels: {
|
||||
one: "{n} मॉडल",
|
||||
other: "{n} मॉडल",
|
||||
},
|
||||
|
||||
mapHeading: "एक इंजन",
|
||||
mapStateReleased: "जारी",
|
||||
mapStateDeveloping: "विकास में, डाउनलोड नहीं",
|
||||
|
||||
@@ -4,43 +4,37 @@ import type { HomeDict } from "../types";
|
||||
|
||||
export const home: HomeDict = {
|
||||
metaTitle: "Codewhale · Model boleh milik siapa saja. Agen seharusnya milik Anda.",
|
||||
metaDescription: "Codewhale sedang menjadi lingkungan terbuka tempat Anda membangun perangkat lunak bersama banyak agen, dengan model apa pun, di komputer Anda sendiri dan di cloud. Hari ini ia dimulai di terminal dan browser Anda, bersumber terbuka dan bisa Anda baca sendiri.",
|
||||
metaDescription: "Codewhale adalah agen pemrograman bersumber terbuka untuk terminal Anda, ditulis dalam Rust. Ia bekerja dengan model apa pun, dapat membagi satu tugas ke beberapa agen, dan setiap barisnya bisa Anda baca.",
|
||||
heroTitle: "Model boleh milik siapa saja. Agen seharusnya milik Anda.",
|
||||
heroTitleLead: "Model boleh milik siapa saja.",
|
||||
heroIntro: "{brand} sedang menjadi lingkungan terbuka tempat Anda membangun perangkat lunak bersama banyak agen, dengan model apa pun, di komputer Anda sendiri dan di cloud. Hari ini ia dimulai di terminal dan browser Anda, bersumber terbuka dan bisa Anda baca sendiri.",
|
||||
heroIntro: "{brand} adalah agen pemrograman bersumber terbuka untuk terminal Anda, ditulis dalam Rust. Ia bekerja dengan model apa pun, dapat membagi satu tugas ke beberapa agen, dan setiap barisnya bisa Anda baca.",
|
||||
readTheCode: "Baca kodenya",
|
||||
installNote: "Versi {version} sudah dirilis dan tidak memerlukan akun.",
|
||||
captionDevelopment: "Build pengembangan di terminal sungguhan: pengeditan dan pengujian benar-benar dijalankan, dan modelnya adalah demo berskrip.",
|
||||
captionReleased: "Sesi terminal sungguhan: pengeditan dan pengujian benar-benar dijalankan, dan modelnya adalah demo berskrip.",
|
||||
captureFrameNoPosture: "Nama model dan pengaturan Ask ditampilkan pada bingkai sebelum dan sesudah bingkai ini.",
|
||||
captureFrameNoPosture: "Bingkai Perintah dan Hasil menampilkan nama model dan pengaturan Ask.",
|
||||
frameLabelPrompt: "Perintah",
|
||||
frameLabelEdit: "Edit",
|
||||
frameLabelApproval: "Persetujuan",
|
||||
frameLabelResult: "Hasil",
|
||||
frameLabelReceipt: "Tanda terima",
|
||||
frameSummaryPrompt: "Codewhale di sebuah terminal. Permintaan “Add a --json flag to ls.py so the tests pass, then run the tests.” sudah dikirim, dan baris status menampilkan Ask serta model scripted-demo.",
|
||||
frameSummaryEdit: "Codewhale di sebuah terminal. Ia membaca ls.py dan mengubahnya, diff menampilkan baris yang ditambahkan, dan sebuah kartu menanyakan apakah python3 -m unittest -q dijalankan.",
|
||||
frameSummaryApproval: "Codewhale di sebuah terminal. Perubahan pada ls.py ditampilkan, dan perintah python3 -m unittest -q menunggu persetujuan. Kartu menanyakan “Do you want to proceed?” dan menawarkan untuk mengizinkannya sekali, mengizinkannya untuk percakapan ini, atau tidak mengizinkannya.",
|
||||
frameSummaryResult: "Codewhale di sebuah terminal. Setelah disetujui, perintah menjalankan 2 pengujian dan mencetak OK. Codewhale mengatakan ia menambahkan --json ke ls.py dan kedua pengujian lulus, dan sesi ini memakai model lokal berskrip.",
|
||||
frameSummaryProvider: "Codewhale di sebuah terminal. Formulir penyedia kustom untuk endpoint yang kompatibel dengan OpenAI, dengan nama scripted_local, URL dasar di komputer ini, model bawaan scripted-demo, dan variabel opsional untuk kunci API.",
|
||||
frameSummaryReceipt: "Codewhale di sebuah terminal. Tanda terima sesi: 1 berkas diubah dan 1 perintah dijalankan, perintah disetujui oleh Anda dan perubahan berkas dilakukan tanpa bertanya dalam Ask, lalu daftar hal yang tidak dicatat tanda terima.",
|
||||
getCodewhale: "Instal Codewhale",
|
||||
heroInstallAria: "Perintah instalasi",
|
||||
shotPreview: "Pratinjau terminal",
|
||||
screenshotAlt:
|
||||
"Codewhale v{version}: tangkapan terminal berisi percakapan, kolom pesan, dan kontrol sesi.",
|
||||
proofContributors: "{count} kontributor",
|
||||
sourceCandidate: "Belum dirilis",
|
||||
publishedRelease: "dirilis",
|
||||
modelsHeading: "Gunakan model pilihanmu",
|
||||
modelsLink: "Lihat model dan penyedia",
|
||||
startHeading: "Mulai menggunakan",
|
||||
copy: "Salin",
|
||||
copied: "Tersalin ✓",
|
||||
contribute: "Berkontribusi di GitHub",
|
||||
|
||||
belief: "Model yang Anda pakai biasanya milik pihak lain. Anda menyewanya, dan ia berubah tanpa Anda minta. Agen berbeda. Ia membaca berkas Anda, menjalankan perintah Anda, menyimpan apa yang sedang Anda kerjakan, dan bertindak atas nama Anda, sehingga ia seharusnya menjadi milik Anda. Codewhale adalah agen itu. Ia bersumber terbuka, jadi Anda bisa membaca setiap barisnya, atau memintanya membacakan dirinya sendiri kepada Anda. Ia bekerja dengan model apa pun yang Anda pilih, Anda yang menentukan apa yang boleh ia lakukan tanpa bertanya, dan ia meninggalkan catatan yang bisa Anda baca. Kami sedang membangunnya menjadi satu tempat di mana Anda dan banyak agen membuat perangkat lunak bersama, dari terminal Anda sampai komputer di cloud, dan ia tetap milik Anda sepanjang jalan.",
|
||||
|
||||
sectionModelHeading: "Model apa pun bisa bekerja lewat agen Anda.",
|
||||
sectionModelBody: "Gunakan penyedia bawaan, endpoint apa pun yang kompatibel dengan OpenAI, atau model di mesin Anda sendiri, dan ganti model di antara giliran. Codewhale berjalan di repositori Anda tanpa akun, dan `codewhale web` memungkinkan Anda memakainya di browser di mesin Anda sendiri.",
|
||||
sectionControlHeading: "Agen Anda bertanggung jawab kepada Anda.",
|
||||
sectionControlBody: "Anda yang menentukan apa yang boleh dilakukan Codewhale tanpa bertanya, dan perintahnya dapat berjalan di sandbox pada macOS dan Linux. Setiap sesi meninggalkan tanda terima berisi berkas yang diubah, perintah yang dijalankan, dan siapa yang menyetujui setiap langkah.",
|
||||
sectionAgentsHeading: "Beberapa agen dapat berbagi satu tugas bersama Anda.",
|
||||
sectionAgentsBody: "Codewhale membagi pekerjaan ke beberapa agen dengan peran seperti penjelajah, pelaksana, dan peninjau, dan Anda dapat memantau, memeriksa, dan menghentikan masing-masing. Sebuah Fleet menyimpan buku catatan, sehingga proses dapat dilanjutkan setelah restart.",
|
||||
sectionDirectionHeading: "Ke sinilah agen Anda menuju.",
|
||||
sectionDirectionBody: "Codewhale sedang menjadi satu lingkungan yang mencakup terminal Anda, aplikasi desktop, browser, dan komputer cloud, semuanya dijalankan oleh satu mesin, sehingga pekerjaan Anda dapat berpindah di antaranya dan tetap milik Anda. Setiap bagian di bawah menyebutkan posisinya hari ini.",
|
||||
sectionOpenHeading: "Codewhale dibangun secara terbuka oleh orang-orang yang memakainya.",
|
||||
@@ -48,6 +42,19 @@ export const home: HomeDict = {
|
||||
sectionStartHeading: "Jadikan agen Anda milik Anda hari ini.",
|
||||
sectionStartBody: "Instal Codewhale dan jalankan di repositori dengan kunci model yang sudah Anda miliki atau model di komputer Anda sendiri. Anda tidak memerlukan akun, dan `codewhale update` menjaganya tetap mutakhir.",
|
||||
|
||||
contributorsCount: {
|
||||
other: "{n} kontributor",
|
||||
},
|
||||
factsLabel: "Sekilas tentang Codewhale",
|
||||
factRust: "Ditulis dalam Rust",
|
||||
factLicense: "Sumber terbuka, lisensi {license}",
|
||||
factProviders: {
|
||||
other: "{n} rute penyedia",
|
||||
},
|
||||
factModels: {
|
||||
other: "{n} model",
|
||||
},
|
||||
|
||||
mapHeading: "Satu mesin",
|
||||
mapStateReleased: "Dirilis",
|
||||
mapStateDeveloping: "Dalam pengembangan, belum ada unduhan",
|
||||
|
||||
@@ -4,43 +4,37 @@ import type { HomeDict } from "../types";
|
||||
|
||||
export const home: HomeDict = {
|
||||
metaTitle: "Codewhale · Il modello può essere di chiunque. L’agente dovrebbe essere tuo.",
|
||||
metaDescription: "Codewhale sta diventando l’ambiente aperto in cui costruisci software con molti agenti, su qualsiasi modello, sul tuo computer e nel cloud. Oggi parte dal tuo terminale e dal tuo browser, open source e tuo da leggere.",
|
||||
metaDescription: "Codewhale è un agente di programmazione open source per il tuo terminale, scritto in Rust. Funziona con qualsiasi modello, può dividere un compito tra più agenti e puoi leggerne ogni riga.",
|
||||
heroTitle: "Il modello può essere di chiunque. L’agente dovrebbe essere tuo.",
|
||||
heroTitleLead: "Il modello può essere di chiunque.",
|
||||
heroIntro: "{brand} sta diventando l’ambiente aperto in cui costruisci software con molti agenti, su qualsiasi modello, sul tuo computer e nel cloud. Oggi parte dal tuo terminale e dal tuo browser, open source e tuo da leggere.",
|
||||
heroIntro: "{brand} è un agente di programmazione open source per il tuo terminale, scritto in Rust. Funziona con qualsiasi modello, può dividere un compito tra più agenti e puoi leggerne ogni riga.",
|
||||
readTheCode: "Leggi il codice",
|
||||
installNote: "La versione {version} è pubblicata e non richiede un account.",
|
||||
captionDevelopment: "Una build di sviluppo in un vero terminale: le modifiche e i test sono stati eseguiti davvero, e il modello è una dimostrazione con copione.",
|
||||
captionReleased: "Una vera sessione di terminale: le modifiche e i test sono stati eseguiti davvero, e il modello è una dimostrazione con copione.",
|
||||
captureFrameNoPosture: "Il nome del modello e l’impostazione Ask sono mostrati nei fotogrammi prima e dopo questo.",
|
||||
captureFrameNoPosture: "I fotogrammi «Richiesta» e «Risultato» mostrano il nome del modello e l’impostazione Ask.",
|
||||
frameLabelPrompt: "Richiesta",
|
||||
frameLabelEdit: "Modifica",
|
||||
frameLabelApproval: "Approvazione",
|
||||
frameLabelResult: "Risultato",
|
||||
frameLabelReceipt: "Ricevuta",
|
||||
frameSummaryPrompt: "Codewhale in un terminale. La richiesta «Add a --json flag to ls.py so the tests pass, then run the tests.» è stata inviata, e la riga di stato mostra Ask e il modello scripted-demo.",
|
||||
frameSummaryEdit: "Codewhale in un terminale. Ha letto ls.py e lo ha modificato, il diff mostra le righe aggiunte e una scheda chiede se eseguire python3 -m unittest -q.",
|
||||
frameSummaryApproval: "Codewhale in un terminale. La modifica a ls.py è mostrata, e il comando python3 -m unittest -q attende l’approvazione. La scheda chiede «Do you want to proceed?» e propone di consentirlo una volta, consentirlo per questa conversazione o non consentirlo.",
|
||||
frameSummaryResult: "Codewhale in un terminale. Dopo l’approvazione il comando ha eseguito 2 test e ha stampato OK. Codewhale dice di aver aggiunto --json a ls.py e che entrambi i test passano, e che questa sessione usa un modello locale con copione.",
|
||||
frameSummaryProvider: "Codewhale in un terminale. Il modulo del provider personalizzato per un endpoint compatibile con OpenAI, con il nome scripted_local, un URL di base su questo computer, il modello predefinito scripted-demo e una variabile facoltativa per la chiave API.",
|
||||
frameSummaryReceipt: "Codewhale in un terminale. La ricevuta della sessione: 1 file modificato e 1 comando eseguito, il comando approvato da te e la modifica del file fatta senza chiedere in Ask, poi l’elenco di ciò che la ricevuta non registra.",
|
||||
getCodewhale: "Installa Codewhale",
|
||||
heroInstallAria: "Comando di installazione",
|
||||
shotPreview: "Anteprima del terminale",
|
||||
screenshotAlt:
|
||||
"Codewhale v{version}: una cattura del terminale con conversazione, campo del messaggio e controlli della sessione.",
|
||||
proofContributors: "{count} contributori",
|
||||
sourceCandidate: "Non rilasciata",
|
||||
publishedRelease: "rilasciata",
|
||||
modelsHeading: "Usa i modelli che preferisci",
|
||||
modelsLink: "Sfoglia modelli e provider",
|
||||
startHeading: "Per iniziare",
|
||||
copy: "Copia",
|
||||
copied: "Copiato ✓",
|
||||
contribute: "Contribuisci su GitHub",
|
||||
|
||||
belief: "Il modello che usi di solito è di qualcun altro. Lo noleggi, e cambia sotto i tuoi piedi. L’agente è diverso. Legge i tuoi file, esegue i tuoi comandi, tiene traccia di ciò su cui lavori e agisce a tuo nome, quindi dovrebbe appartenerti. Codewhale è quell’agente. È open source, quindi puoi leggerne ogni riga, oppure chiedergli di leggersi da solo per te. Funziona con qualsiasi modello tu scelga, sei tu a decidere cosa può fare senza chiedere, e lascia una registrazione che puoi leggere. Lo stiamo costruendo come un unico luogo in cui tu e molti agenti create software insieme, dal tuo terminale a un computer nel cloud, e resta tuo per tutto il percorso.",
|
||||
|
||||
sectionModelHeading: "Qualsiasi modello può lavorare attraverso il tuo agente.",
|
||||
sectionModelBody: "Usa un provider integrato, qualsiasi endpoint compatibile con OpenAI o un modello sulla tua macchina, e cambia modello tra un turno e l’altro. Codewhale funziona nel tuo repository senza account, e `codewhale web` ti permette di lavorarci nel browser, sulla tua macchina.",
|
||||
sectionControlHeading: "Il tuo agente risponde a te.",
|
||||
sectionControlBody: "Sei tu a decidere cosa Codewhale può fare senza chiedere, e i suoi comandi possono essere eseguiti in una sandbox su macOS e Linux. Ogni sessione lascia una ricevuta dei file modificati, dei comandi eseguiti e di chi ha approvato ogni passaggio.",
|
||||
sectionAgentsHeading: "Più agenti possono condividere un compito con te.",
|
||||
sectionAgentsBody: "Codewhale divide un lavoro tra agenti con ruoli come esploratore, implementatore e revisore, e puoi osservare, ispezionare e fermare ciascuno di essi. Una Fleet tiene un registro, così un’esecuzione può riprendere dopo un riavvio.",
|
||||
sectionDirectionHeading: "Ecco dove sta andando il tuo agente.",
|
||||
sectionDirectionBody: "Codewhale sta diventando un unico ambiente che copre il tuo terminale, un’app desktop, il browser e i computer nel cloud, tutto guidato da un solo motore, così il tuo lavoro può spostarsi tra di essi e restare tuo. Ogni parte qui sotto dice a che punto si trova oggi.",
|
||||
sectionOpenHeading: "Codewhale è costruito alla luce del sole da chi lo usa.",
|
||||
@@ -48,6 +42,24 @@ export const home: HomeDict = {
|
||||
sectionStartHeading: "Rendi tuo il tuo agente oggi stesso.",
|
||||
sectionStartBody: "Installa Codewhale ed eseguilo in un repository con una chiave di modello che hai già o con un modello sul tuo computer. Non serve alcun account, e `codewhale update` lo mantiene aggiornato.",
|
||||
|
||||
contributorsCount: {
|
||||
one: "{n} contributore",
|
||||
other: "{n} contributori",
|
||||
},
|
||||
factsLabel: "Codewhale in breve",
|
||||
factRust: "Scritto in Rust",
|
||||
factLicense: "Open source, licenza {license}",
|
||||
factProviders: {
|
||||
one: "{n} percorso di provider",
|
||||
many: "{n} di percorsi di provider",
|
||||
other: "{n} percorsi di provider",
|
||||
},
|
||||
factModels: {
|
||||
one: "{n} modello",
|
||||
many: "{n} di modelli",
|
||||
other: "{n} modelli",
|
||||
},
|
||||
|
||||
mapHeading: "Un solo motore",
|
||||
mapStateReleased: "Pubblicato",
|
||||
mapStateDeveloping: "In sviluppo, nessun download",
|
||||
|
||||
@@ -4,43 +4,37 @@ import type { HomeDict } from "../types";
|
||||
|
||||
export const home: HomeDict = {
|
||||
metaTitle: "Codewhale · モデルは誰のものでもかまいません。エージェントはあなたのものであるべきです。",
|
||||
metaDescription: "Codewhale は、多くのエージェントとともに、好きなモデルで、あなた自身のコンピューターとクラウドの上でソフトウェアを作る、オープンな環境になりつつあります。今日は、あなたのターミナルとブラウザーから始まります。オープンソースで、中身を自分で読めます。",
|
||||
metaDescription: "Codewhale は、ターミナルで使うオープンソースのコーディングエージェントで、Rust で書かれています。どんなモデルでも動き、1 つのタスクを複数のエージェントに分担させられます。そのコードは 1 行残らず読めます。",
|
||||
heroTitle: "モデルは誰のものでもかまいません。エージェントはあなたのものであるべきです。",
|
||||
heroTitleLead: "モデルは誰のものでもかまいません。",
|
||||
heroIntro: "{brand} は、多くのエージェントとともに、好きなモデルで、あなた自身のコンピューターとクラウドの上でソフトウェアを作る、オープンな環境になりつつあります。今日は、あなたのターミナルとブラウザーから始まります。オープンソースで、中身を自分で読めます。",
|
||||
heroIntro: "{brand} は、ターミナルで使うオープンソースのコーディングエージェントで、Rust で書かれています。どんなモデルでも動き、1 つのタスクを複数のエージェントに分担させられます。そのコードは 1 行残らず読めます。",
|
||||
readTheCode: "コードを読む",
|
||||
installNote: "バージョン {version} はリリース済みで、アカウントは不要です。",
|
||||
captionDevelopment: "実際のターミナルでの開発ビルド。編集とテストは実際に実行されたもので、モデルは台本どおりのデモです。",
|
||||
captionReleased: "実際のターミナルセッション。編集とテストは実際に実行されたもので、モデルは台本どおりのデモです。",
|
||||
captureFrameNoPosture: "モデル名と Ask の設定は、この画面の前後の画面に表示されています。",
|
||||
captureFrameNoPosture: "モデル名と Ask の設定は、「プロンプト」と「結果」の画面に表示されています。",
|
||||
frameLabelPrompt: "プロンプト",
|
||||
frameLabelEdit: "編集",
|
||||
frameLabelApproval: "承認",
|
||||
frameLabelResult: "結果",
|
||||
frameLabelReceipt: "レシート",
|
||||
frameSummaryPrompt: "ターミナルで動く Codewhale。依頼「Add a --json flag to ls.py so the tests pass, then run the tests.」が送信済みで、ステータス行には Ask とモデル scripted-demo が表示されています。",
|
||||
frameSummaryEdit: "ターミナルで動く Codewhale。ls.py を読み込んで変更し、差分に追加行が表示され、カードが python3 -m unittest -q を実行するかどうかを尋ねています。",
|
||||
frameSummaryApproval: "ターミナルで動く Codewhale。ls.py への変更が表示され、コマンド python3 -m unittest -q が承認を待っています。カードは「Do you want to proceed?」と尋ね、一度だけ許可する、この会話の間許可する、許可しない、の選択肢を示しています。",
|
||||
frameSummaryResult: "ターミナルで動く Codewhale。承認後、コマンドは 2 件のテストを実行して OK を出力しました。Codewhale は ls.py に --json を追加して両方のテストが通ったこと、このセッションが台本どおりのローカルモデルを使っていることを伝えています。",
|
||||
frameSummaryProvider: "ターミナルで動く Codewhale。OpenAI 互換エンドポイント用のカスタムプロバイダーのフォームで、名前 scripted_local、このコンピューター上のベース URL、既定のモデル scripted-demo、任意の API キー用変数が入力されています。",
|
||||
frameSummaryReceipt: "ターミナルで動く Codewhale。セッションのレシートです。変更したファイルは 1 件、実行したコマンドは 1 件で、コマンドはあなたが承認し、ファイルの変更は Ask の設定で確認なしに行われました。続いて、レシートに記録されない項目の一覧があります。",
|
||||
getCodewhale: "Codewhale をインストール",
|
||||
heroInstallAria: "インストールコマンド",
|
||||
shotPreview: "ターミナルのプレビュー",
|
||||
screenshotAlt:
|
||||
"Codewhale v{version} のターミナル収録画面。会話、メッセージ入力欄、セッション操作を表示。",
|
||||
proofContributors: "コントリビューター {count} 人",
|
||||
sourceCandidate: "未リリース",
|
||||
publishedRelease: "リリース済み",
|
||||
modelsHeading: "使いたいモデルを選ぶ",
|
||||
modelsLink: "モデルとプロバイダーを見る",
|
||||
startHeading: "使い始める",
|
||||
copy: "コピー",
|
||||
copied: "コピー済み ✓",
|
||||
contribute: "GitHub で参加",
|
||||
|
||||
belief: "あなたが使うモデルは、たいてい他の誰かのものです。借りて使っていて、知らないうちに変わります。エージェントは違います。あなたのファイルを読み、あなたのコマンドを実行し、取り組んでいる内容を保持し、あなたの名前で動くのですから、あなたのものであるべきです。Codewhale はそのエージェントです。オープンソースなので、すべての行を読むことができ、自分自身を読み上げさせることもできます。選んだどのモデルでも動き、確認なしで何をしてよいかはあなたが決め、読める記録を残します。私たちは、あなたと多くのエージェントがいっしょにソフトウェアを作る一つの場所へと育てています。ターミナルからクラウド上のコンピューターまで、最後まであなたのものです。",
|
||||
|
||||
sectionModelHeading: "どのモデルでも、あなたのエージェントを通して動けます。",
|
||||
sectionModelBody: "組み込みのプロバイダー、OpenAI 互換のエンドポイント、またはあなたのマシン上のモデルを使え、ターンの合間にモデルを切り替えられます。Codewhale はアカウントなしであなたのリポジトリで動き、`codewhale web` を使えば自分のマシン上のブラウザーで操作できます。",
|
||||
sectionControlHeading: "あなたのエージェントは、あなたに応えます。",
|
||||
sectionControlBody: "確認なしで Codewhale に何をさせてよいかはあなたが決め、コマンドは macOS と Linux ではサンドボックス内で実行できます。セッションごとに、変更したファイル、実行したコマンド、各ステップを誰が承認したかを記したレシートが残ります。",
|
||||
sectionAgentsHeading: "複数のエージェントが、あなたと一つのタスクを分け合えます。",
|
||||
sectionAgentsBody: "Codewhale は、探索役、実装役、レビュー役などの役割を持つエージェントに仕事を分け、あなたはそれぞれを見守り、調べ、止められます。Fleet は台帳を保つので、再起動のあとも実行を再開できます。",
|
||||
sectionDirectionHeading: "あなたのエージェントが向かう先は、ここです。",
|
||||
sectionDirectionBody: "Codewhale は、ターミナル、デスクトップアプリ、ブラウザー、クラウドコンピューターにまたがる一つの環境になりつつあり、すべてが一つのエンジンで動きます。作業はそれらの間を移っても、あなたのものであり続けます。以下の各部分に、今日の状況を示しています。",
|
||||
sectionOpenHeading: "Codewhale は、使う人たちによってオープンに作られています。",
|
||||
@@ -48,6 +42,19 @@ export const home: HomeDict = {
|
||||
sectionStartHeading: "今日、あなたのエージェントをあなたのものにしましょう。",
|
||||
sectionStartBody: "Codewhale をインストールし、すでにお持ちのモデルキーか、あなたのコンピューター上のモデルでリポジトリ内で実行してください。アカウントは不要で、`codewhale update` で最新に保てます。",
|
||||
|
||||
contributorsCount: {
|
||||
other: "コントリビューター {n} 人",
|
||||
},
|
||||
factsLabel: "Codewhale の概要",
|
||||
factRust: "Rust で記述",
|
||||
factLicense: "オープンソース、{license} ライセンス",
|
||||
factProviders: {
|
||||
other: "プロバイダールート {n} 本",
|
||||
},
|
||||
factModels: {
|
||||
other: "モデル {n} 種",
|
||||
},
|
||||
|
||||
mapHeading: "ひとつのエンジン",
|
||||
mapStateReleased: "リリース済み",
|
||||
mapStateDeveloping: "開発中、ダウンロードなし",
|
||||
|
||||
@@ -4,43 +4,37 @@ import type { HomeDict } from "../types";
|
||||
|
||||
export const home: HomeDict = {
|
||||
metaTitle: "Codewhale · 모델은 누구의 것이어도 됩니다. 에이전트는 당신의 것이어야 합니다.",
|
||||
metaDescription: "Codewhale은 많은 에이전트와 함께, 원하는 모델로, 내 컴퓨터와 클라우드에서 소프트웨어를 만드는 열린 환경이 되어 가고 있습니다. 오늘은 터미널과 브라우저에서 시작하며, 오픈 소스이므로 직접 읽어 볼 수 있습니다.",
|
||||
metaDescription: "Codewhale는 터미널에서 쓰는 오픈 소스 코딩 에이전트이며 Rust로 작성되었습니다. 어떤 모델과도 함께 작동하고, 하나의 작업을 여러 에이전트에게 나눌 수 있으며, 코드 한 줄 한 줄을 직접 읽을 수 있습니다.",
|
||||
heroTitle: "모델은 누구의 것이어도 됩니다. 에이전트는 당신의 것이어야 합니다.",
|
||||
heroTitleLead: "모델은 누구의 것이어도 됩니다.",
|
||||
heroIntro: "{brand}은 많은 에이전트와 함께, 원하는 모델로, 내 컴퓨터와 클라우드에서 소프트웨어를 만드는 열린 환경이 되어 가고 있습니다. 오늘은 터미널과 브라우저에서 시작하며, 오픈 소스이므로 직접 읽어 볼 수 있습니다.",
|
||||
heroIntro: "{brand}는 터미널에서 쓰는 오픈 소스 코딩 에이전트이며 Rust로 작성되었습니다. 어떤 모델과도 함께 작동하고, 하나의 작업을 여러 에이전트에게 나눌 수 있으며, 코드 한 줄 한 줄을 직접 읽을 수 있습니다.",
|
||||
readTheCode: "코드 읽기",
|
||||
installNote: "버전 {version}은(는) 릴리스되었으며 계정이 필요 없습니다.",
|
||||
captionDevelopment: "실제 터미널에서 실행한 개발 빌드입니다. 편집과 테스트는 실제로 실행되었고, 모델은 대본에 따른 시연입니다.",
|
||||
captionReleased: "실제 터미널 세션입니다. 편집과 테스트는 실제로 실행되었고, 모델은 대본에 따른 시연입니다.",
|
||||
captureFrameNoPosture: "모델 이름과 Ask 설정은 이 화면의 앞뒤 화면에 표시됩니다.",
|
||||
captureFrameNoPosture: "프롬프트 화면과 결과 화면에 모델 이름과 Ask 설정이 표시됩니다.",
|
||||
frameLabelPrompt: "프롬프트",
|
||||
frameLabelEdit: "편집",
|
||||
frameLabelApproval: "승인",
|
||||
frameLabelResult: "결과",
|
||||
frameLabelReceipt: "영수증",
|
||||
frameSummaryPrompt: "터미널에서 실행 중인 Codewhale. 요청 “Add a --json flag to ls.py so the tests pass, then run the tests.”이 전송되었고, 상태 줄에 Ask와 모델 scripted-demo가 표시됩니다.",
|
||||
frameSummaryEdit: "터미널에서 실행 중인 Codewhale. ls.py를 읽고 변경했으며, diff에 추가된 줄이 표시되고, 카드가 python3 -m unittest -q를 실행할지 묻습니다.",
|
||||
frameSummaryApproval: "터미널에서 실행 중인 Codewhale. ls.py의 변경 내용이 표시되고, 명령 python3 -m unittest -q가 승인을 기다립니다. 카드는 “Do you want to proceed?”라고 묻고, 한 번 허용, 이 대화 동안 허용, 허용 안 함 중에서 고르게 합니다.",
|
||||
frameSummaryResult: "터미널에서 실행 중인 Codewhale. 승인 후 명령이 테스트 2개를 실행하고 OK를 출력했습니다. Codewhale은 ls.py에 --json을 추가했고 두 테스트가 모두 통과했으며, 이 세션은 대본에 따른 로컬 모델을 사용한다고 알립니다.",
|
||||
frameSummaryProvider: "터미널에서 실행 중인 Codewhale. OpenAI 호환 엔드포인트용 사용자 지정 공급자 양식으로, 이름 scripted_local, 이 컴퓨터의 기본 URL, 기본 모델 scripted-demo, 선택 사항인 API 키 변수가 입력되어 있습니다.",
|
||||
frameSummaryReceipt: "터미널에서 실행 중인 Codewhale. 세션의 영수증입니다. 파일 1개를 변경하고 명령 1개를 실행했으며, 명령은 당신이 승인했고 파일 변경은 Ask 설정에서 묻지 않고 이루어졌습니다. 이어서 영수증에 기록되지 않는 항목의 목록이 나옵니다.",
|
||||
getCodewhale: "Codewhale 설치",
|
||||
heroInstallAria: "설치 명령",
|
||||
shotPreview: "터미널 미리보기",
|
||||
screenshotAlt:
|
||||
"Codewhale v{version}: 대화, 메시지 입력창, 세션 제어를 보여 주는 실제 터미널 캡처.",
|
||||
proofContributors: "기여자 {count}명",
|
||||
sourceCandidate: "미공개",
|
||||
publishedRelease: "공개됨",
|
||||
modelsHeading: "원하는 모델을 사용하세요",
|
||||
modelsLink: "모델과 제공업체 둘러보기",
|
||||
startHeading: "시작하기",
|
||||
copy: "복사",
|
||||
copied: "복사됨 ✓",
|
||||
contribute: "GitHub에서 기여하기",
|
||||
|
||||
belief: "당신이 쓰는 모델은 대개 다른 누군가의 것입니다. 빌려 쓰는 것이고, 모르는 사이에 바뀝니다. 에이전트는 다릅니다. 에이전트는 당신의 파일을 읽고, 명령을 실행하고, 작업 중인 내용을 기억하며, 당신의 이름으로 행동하므로 당신의 것이어야 합니다. Codewhale이 그런 에이전트입니다. 오픈 소스이므로 모든 줄을 직접 읽을 수 있고, 에이전트에게 자신을 읽어 달라고 할 수도 있습니다. 당신이 고른 어떤 모델과도 작동하고, 묻지 않고 무엇을 해도 되는지는 당신이 정하며, 읽을 수 있는 기록을 남깁니다. 우리는 이것을 당신과 많은 에이전트가 함께 소프트웨어를 만드는 하나의 공간으로 만들고 있습니다. 터미널에서 클라우드의 컴퓨터까지, 끝까지 당신의 것으로 남습니다.",
|
||||
|
||||
sectionModelHeading: "어떤 모델이든 당신의 에이전트를 통해 일할 수 있습니다.",
|
||||
sectionModelBody: "내장 제공업체, OpenAI 호환 엔드포인트, 또는 내 컴퓨터의 모델을 사용할 수 있고, 턴 사이에 모델을 바꿀 수 있습니다. Codewhale은 계정 없이 당신의 저장소에서 실행되며, `codewhale web`으로 내 컴퓨터의 브라우저에서 함께 작업할 수 있습니다.",
|
||||
sectionControlHeading: "당신의 에이전트는 당신에게 응답합니다.",
|
||||
sectionControlBody: "묻지 않고 Codewhale이 무엇을 해도 되는지는 당신이 정하며, 명령은 macOS와 Linux에서 샌드박스 안에서 실행될 수 있습니다. 모든 세션은 변경한 파일, 실행한 명령, 각 단계를 누가 승인했는지를 담은 영수증을 남깁니다.",
|
||||
sectionAgentsHeading: "여러 에이전트가 당신과 하나의 작업을 나눌 수 있습니다.",
|
||||
sectionAgentsBody: "Codewhale은 탐색자, 구현자, 검토자 같은 역할을 가진 에이전트들에게 작업을 나누고, 당신은 각각을 지켜보고, 살펴보고, 멈출 수 있습니다. Fleet은 원장을 유지하므로 재시작 후에도 실행을 이어갈 수 있습니다.",
|
||||
sectionDirectionHeading: "당신의 에이전트가 가는 곳은 여기입니다.",
|
||||
sectionDirectionBody: "Codewhale은 터미널, 데스크톱 앱, 브라우저, 클라우드 컴퓨터를 아우르는 하나의 환경이 되어 가고 있으며, 모두 하나의 엔진으로 돌아갑니다. 그래서 작업이 그 사이를 오가면서도 당신의 것으로 남습니다. 아래의 각 부분은 오늘 어느 단계에 있는지 알려 줍니다.",
|
||||
sectionOpenHeading: "Codewhale은 쓰는 사람들이 공개적으로 만듭니다.",
|
||||
@@ -48,6 +42,19 @@ export const home: HomeDict = {
|
||||
sectionStartHeading: "오늘 당신의 에이전트를 당신의 것으로 만드세요.",
|
||||
sectionStartBody: "Codewhale을 설치하고, 이미 가진 모델 키나 내 컴퓨터의 모델로 저장소에서 실행하세요. 계정은 필요 없고, `codewhale update`로 최신 상태를 유지합니다.",
|
||||
|
||||
contributorsCount: {
|
||||
other: "기여자 {n}명",
|
||||
},
|
||||
factsLabel: "한눈에 보는 Codewhale",
|
||||
factRust: "Rust로 작성",
|
||||
factLicense: "오픈 소스, {license} 라이선스",
|
||||
factProviders: {
|
||||
other: "제공자 경로 {n}개",
|
||||
},
|
||||
factModels: {
|
||||
other: "모델 {n}개",
|
||||
},
|
||||
|
||||
mapHeading: "하나의 엔진",
|
||||
mapStateReleased: "릴리스됨",
|
||||
mapStateDeveloping: "개발 중, 다운로드 없음",
|
||||
|
||||
@@ -4,43 +4,37 @@ import type { HomeDict } from "../types";
|
||||
|
||||
export const home: HomeDict = {
|
||||
metaTitle: "Codewhale · Model może należeć do kogokolwiek. Agent powinien być twój.",
|
||||
metaDescription: "Codewhale staje się otwartym środowiskiem, w którym tworzysz oprogramowanie z wieloma agentami, na dowolnym modelu, na własnym komputerze i w chmurze. Dziś zaczyna się w twoim terminalu i przeglądarce, na otwartym kodzie, który możesz przeczytać.",
|
||||
metaDescription: "Codewhale to otwartoźródłowy agent programistyczny do twojego terminala, napisany w Rust. Działa z dowolnym modelem, potrafi podzielić jedno zadanie między kilku agentów, a każdą jego linię możesz przeczytać.",
|
||||
heroTitle: "Model może należeć do kogokolwiek. Agent powinien być twój.",
|
||||
heroTitleLead: "Model może należeć do kogokolwiek.",
|
||||
heroIntro: "{brand} staje się otwartym środowiskiem, w którym tworzysz oprogramowanie z wieloma agentami, na dowolnym modelu, na własnym komputerze i w chmurze. Dziś zaczyna się w twoim terminalu i przeglądarce, na otwartym kodzie, który możesz przeczytać.",
|
||||
heroIntro: "{brand} to otwartoźródłowy agent programistyczny do twojego terminala, napisany w Rust. Działa z dowolnym modelem, potrafi podzielić jedno zadanie między kilku agentów, a każdą jego linię możesz przeczytać.",
|
||||
readTheCode: "Przeczytaj kod",
|
||||
installNote: "Wersja {version} jest wydana i nie wymaga konta.",
|
||||
captionDevelopment: "Wersja rozwojowa w prawdziwym terminalu: edycje i testy wykonały się naprawdę, a model to demonstracja według scenariusza.",
|
||||
captionReleased: "Prawdziwa sesja w terminalu: edycje i testy wykonały się naprawdę, a model to demonstracja według scenariusza.",
|
||||
captureFrameNoPosture: "Nazwa modelu i ustawienie Ask są widoczne na klatkach przed tą i po niej.",
|
||||
captureFrameNoPosture: "Klatki „Polecenie” i „Wynik” pokazują nazwę modelu i ustawienie Ask.",
|
||||
frameLabelPrompt: "Polecenie",
|
||||
frameLabelEdit: "Edycja",
|
||||
frameLabelApproval: "Zatwierdzenie",
|
||||
frameLabelResult: "Wynik",
|
||||
frameLabelReceipt: "Pokwitowanie",
|
||||
frameSummaryPrompt: "Codewhale w terminalu. Prośba „Add a --json flag to ls.py so the tests pass, then run the tests.” została wysłana, a wiersz stanu pokazuje Ask i model scripted-demo.",
|
||||
frameSummaryEdit: "Codewhale w terminalu. Odczytał ls.py i go zmienił, diff pokazuje dodane wiersze, a karta pyta, czy uruchomić python3 -m unittest -q.",
|
||||
frameSummaryApproval: "Codewhale w terminalu. Widać zmianę w ls.py, a polecenie python3 -m unittest -q czeka na zatwierdzenie. Karta pyta „Do you want to proceed?” i proponuje zezwolić raz, zezwolić w tej rozmowie albo nie zezwalać.",
|
||||
frameSummaryResult: "Codewhale w terminalu. Po zatwierdzeniu polecenie wykonało 2 testy i wypisało OK. Codewhale informuje, że dodał --json do ls.py i oba testy przechodzą oraz że ta sesja używa lokalnego modelu według scenariusza.",
|
||||
frameSummaryProvider: "Codewhale w terminalu. Formularz własnego dostawcy dla punktu końcowego zgodnego z OpenAI, z nazwą scripted_local, bazowym adresem URL na tym komputerze, domyślnym modelem scripted-demo i opcjonalną zmienną na klucz API.",
|
||||
frameSummaryReceipt: "Codewhale w terminalu. Pokwitowanie sesji: 1 zmieniony plik i 1 uruchomione polecenie, polecenie zatwierdzone przez ciebie, a zmiana pliku wykonana bez pytania w trybie Ask, potem lista tego, czego pokwitowanie nie zapisuje.",
|
||||
getCodewhale: "Zainstaluj Codewhale",
|
||||
heroInstallAria: "Polecenie instalacji",
|
||||
shotPreview: "Podgląd terminala",
|
||||
screenshotAlt:
|
||||
"Codewhale v{version}: zrzut terminala z rozmową, polem wiadomości i kontrolkami sesji.",
|
||||
proofContributors: "Współtwórcy: {count}",
|
||||
sourceCandidate: "Niewydane",
|
||||
publishedRelease: "wydane",
|
||||
modelsHeading: "Używaj wybranych przez siebie modeli",
|
||||
modelsLink: "Przeglądaj modele i dostawców",
|
||||
startHeading: "Jak zacząć",
|
||||
copy: "Kopiuj",
|
||||
copied: "Skopiowano ✓",
|
||||
contribute: "Współtwórz na GitHubie",
|
||||
|
||||
belief: "Model, którego używasz, zwykle należy do kogoś innego. Wynajmujesz go, a on zmienia się pod tobą. Agent jest inny. Czyta twoje pliki, uruchamia twoje polecenia, trzyma to, nad czym pracujesz, i działa w twoim imieniu, więc powinien należeć do ciebie. Codewhale jest takim agentem. Ma otwarty kod, więc możesz przeczytać każdą jego linijkę albo poprosić go, by przeczytał siebie tobie. Działa z dowolnym modelem, który wybierzesz, ty decydujesz, co wolno mu robić bez pytania, i zostawia zapis, który możesz przeczytać. Budujemy z niego jedno miejsce, w którym ty i wielu agentów tworzycie oprogramowanie razem, od twojego terminala po komputer w chmurze, i przez całą drogę pozostaje twój.",
|
||||
|
||||
sectionModelHeading: "Każdy model może pracować przez twojego agenta.",
|
||||
sectionModelBody: "Użyj wbudowanego dostawcy, dowolnego punktu końcowego zgodnego z OpenAI albo modelu na własnej maszynie i zmieniaj modele między turami. Codewhale działa w twoim repozytorium bez konta, a `codewhale web` pozwala pracować z nim w przeglądarce na własnej maszynie.",
|
||||
sectionControlHeading: "Twój agent odpowiada przed tobą.",
|
||||
sectionControlBody: "Ty decydujesz, co Codewhale wolno robić bez pytania, a jego polecenia mogą działać w piaskownicy w systemach macOS i Linux. Każda sesja zostawia pokwitowanie z listą zmienionych plików, uruchomionych poleceń i tego, kto zatwierdził każdy krok.",
|
||||
sectionAgentsHeading: "Kilku agentów może dzielić z tobą jedno zadanie.",
|
||||
sectionAgentsBody: "Codewhale dzieli pracę między agentów o rolach takich jak badacz, wykonawca i recenzent, a ty możesz każdego z nich obserwować, sprawdzać i zatrzymać. Fleet prowadzi rejestr, więc uruchomienie może zostać wznowione po restarcie.",
|
||||
sectionDirectionHeading: "Oto dokąd zmierza twój agent.",
|
||||
sectionDirectionBody: "Codewhale staje się jednym środowiskiem obejmującym twój terminal, aplikację na komputer, przeglądarkę i komputery w chmurze, napędzane jednym silnikiem, aby twoja praca mogła przechodzić między nimi i pozostawać twoja. Każda część poniżej mówi, na jakim etapie jest dziś.",
|
||||
sectionOpenHeading: "Codewhale budują otwarcie ludzie, którzy z niego korzystają.",
|
||||
@@ -48,6 +42,28 @@ export const home: HomeDict = {
|
||||
sectionStartHeading: "Spraw, by twój agent był twój, już dziś.",
|
||||
sectionStartBody: "Zainstaluj Codewhale i uruchom go w repozytorium z kluczem modelu, który już masz, albo z modelem na własnym komputerze. Nie potrzebujesz konta, a `codewhale update` utrzymuje go aktualnym.",
|
||||
|
||||
contributorsCount: {
|
||||
one: "Współtwórcy: {n}",
|
||||
few: "Współtwórcy: {n}",
|
||||
many: "Współtwórcy: {n}",
|
||||
other: "Współtwórcy: {n}",
|
||||
},
|
||||
factsLabel: "Codewhale w skrócie",
|
||||
factRust: "Napisany w Rust",
|
||||
factLicense: "Otwarty kod, licencja {license}",
|
||||
factProviders: {
|
||||
one: "Trasy dostawców: {n}",
|
||||
few: "Trasy dostawców: {n}",
|
||||
many: "Trasy dostawców: {n}",
|
||||
other: "Trasy dostawców: {n}",
|
||||
},
|
||||
factModels: {
|
||||
one: "Modele: {n}",
|
||||
few: "Modele: {n}",
|
||||
many: "Modele: {n}",
|
||||
other: "Modele: {n}",
|
||||
},
|
||||
|
||||
mapHeading: "Jeden silnik",
|
||||
mapStateReleased: "Wydane",
|
||||
mapStateDeveloping: "W rozwoju, bez pobierania",
|
||||
|
||||
@@ -4,43 +4,37 @@ import type { HomeDict } from "../types";
|
||||
|
||||
export const home: HomeDict = {
|
||||
metaTitle: "Codewhale · O modelo pode ser de qualquer pessoa. O agente deve ser seu.",
|
||||
metaDescription: "Codewhale está se tornando o ambiente aberto onde você constrói software com muitos agentes, com qualquer modelo, no seu próprio computador e na nuvem. Ele começa hoje no seu terminal e no seu navegador, de código aberto e seu para ler.",
|
||||
metaDescription: "Codewhale é um agente de programação de código aberto para o seu terminal, escrito em Rust. Ele funciona com qualquer modelo, pode dividir uma tarefa entre vários agentes, e você pode ler cada linha do código.",
|
||||
heroTitle: "O modelo pode ser de qualquer pessoa. O agente deve ser seu.",
|
||||
heroTitleLead: "O modelo pode ser de qualquer pessoa.",
|
||||
heroIntro: "{brand} está se tornando o ambiente aberto onde você constrói software com muitos agentes, com qualquer modelo, no seu próprio computador e na nuvem. Ele começa hoje no seu terminal e no seu navegador, de código aberto e seu para ler.",
|
||||
heroIntro: "{brand} é um agente de programação de código aberto para o seu terminal, escrito em Rust. Ele funciona com qualquer modelo, pode dividir uma tarefa entre vários agentes, e você pode ler cada linha do código.",
|
||||
readTheCode: "Leia o código",
|
||||
installNote: "A versão {version} está publicada e não exige conta.",
|
||||
captionDevelopment: "Uma versão de desenvolvimento em um terminal real: as edições e os testes rodaram de verdade, e o modelo é uma demonstração roteirizada.",
|
||||
captionReleased: "Uma sessão real de terminal: as edições e os testes rodaram de verdade, e o modelo é uma demonstração roteirizada.",
|
||||
captureFrameNoPosture: "O nome do modelo e a configuração Ask aparecem nos quadros antes e depois deste.",
|
||||
captureFrameNoPosture: "Os quadros Instrução e Resultado mostram o nome do modelo e a configuração Ask.",
|
||||
frameLabelPrompt: "Instrução",
|
||||
frameLabelEdit: "Edição",
|
||||
frameLabelApproval: "Aprovação",
|
||||
frameLabelResult: "Resultado",
|
||||
frameLabelReceipt: "Recibo",
|
||||
frameSummaryPrompt: "Codewhale em um terminal. O pedido “Add a --json flag to ls.py so the tests pass, then run the tests.” foi enviado, e a linha de status mostra Ask e o modelo scripted-demo.",
|
||||
frameSummaryEdit: "Codewhale em um terminal. Ele leu ls.py e o alterou, o diff mostra as linhas adicionadas e um cartão pergunta se deve executar python3 -m unittest -q.",
|
||||
frameSummaryApproval: "Codewhale em um terminal. A alteração em ls.py aparece, e o comando python3 -m unittest -q aguarda aprovação. O cartão pergunta “Do you want to proceed?” e oferece permitir uma vez, permitir nesta conversa ou não permitir.",
|
||||
frameSummaryResult: "Codewhale em um terminal. Depois da aprovação, o comando executou 2 testes e imprimiu OK. O Codewhale diz que adicionou --json a ls.py e que os dois testes passam, e que esta sessão usa um modelo local roteirizado.",
|
||||
frameSummaryProvider: "Codewhale em um terminal. O formulário de provedor personalizado para um endpoint compatível com OpenAI, com o nome scripted_local, uma URL base neste computador, o modelo padrão scripted-demo e uma variável opcional para a chave de API.",
|
||||
frameSummaryReceipt: "Codewhale em um terminal. O recibo da sessão: 1 arquivo alterado e 1 comando executado, o comando aprovado por você e a alteração do arquivo feita sem perguntar em Ask, e depois uma lista do que o recibo não registra.",
|
||||
getCodewhale: "Instalar o Codewhale",
|
||||
heroInstallAria: "Comando de instalação",
|
||||
shotPreview: "Prévia do terminal",
|
||||
screenshotAlt:
|
||||
"Codewhale v{version}: captura do terminal com conversa, campo de mensagem e controles da sessão.",
|
||||
proofContributors: "{count} colaboradores",
|
||||
sourceCandidate: "Não publicado",
|
||||
publishedRelease: "publicado",
|
||||
modelsHeading: "Use os modelos que você escolher",
|
||||
modelsLink: "Ver modelos e provedores",
|
||||
startHeading: "Comece a usar",
|
||||
copy: "Copiar",
|
||||
copied: "Copiado ✓",
|
||||
contribute: "Contribua no GitHub",
|
||||
|
||||
belief: "O modelo que você usa costuma ser de outra pessoa. Você o aluga, e ele muda sem você decidir. O agente é diferente. Ele lê seus arquivos, executa seus comandos, guarda o que você está fazendo e age em seu nome, então deve pertencer a você. O Codewhale é esse agente. É de código aberto, então você pode ler cada linha dele, ou pedir que ele leia a si mesmo para você. Ele funciona com qualquer modelo que você escolher, você decide o que ele pode fazer sem perguntar, e ele deixa um registro que você pode ler. Estamos construindo isso como um só lugar onde você e muitos agentes fazem software juntos, do seu terminal a um computador na nuvem, e ele continua seu durante todo o caminho.",
|
||||
|
||||
sectionModelHeading: "Qualquer modelo pode trabalhar por meio do seu agente.",
|
||||
sectionModelBody: "Use um provedor integrado, qualquer endpoint compatível com OpenAI ou um modelo na sua própria máquina, e troque de modelo entre os turnos. O Codewhale roda no seu repositório sem conta, e `codewhale web` permite trabalhar com ele no navegador, na sua própria máquina.",
|
||||
sectionControlHeading: "Seu agente responde a você.",
|
||||
sectionControlBody: "Você decide o que o Codewhale pode fazer sem perguntar, e seus comandos podem rodar em um sandbox no macOS e no Linux. Cada sessão deixa um recibo dos arquivos que ele alterou, dos comandos que executou e de quem aprovou cada etapa.",
|
||||
sectionAgentsHeading: "Vários agentes podem dividir uma tarefa com você.",
|
||||
sectionAgentsBody: "O Codewhale divide um trabalho entre agentes com papéis como explorador, implementador e revisor, e você pode acompanhar, inspecionar e interromper cada um. Uma Fleet mantém um registro, então uma execução pode continuar depois de uma reinicialização.",
|
||||
sectionDirectionHeading: "É para cá que seu agente está indo.",
|
||||
sectionDirectionBody: "O Codewhale está se tornando um único ambiente que abrange seu terminal, um aplicativo de desktop, o navegador e computadores na nuvem, tudo movido por um só motor, para que seu trabalho possa passar de um para outro e continuar seu. Cada parte abaixo diz em que ponto está hoje.",
|
||||
sectionOpenHeading: "O Codewhale é construído abertamente por quem o usa.",
|
||||
@@ -48,6 +42,24 @@ export const home: HomeDict = {
|
||||
sectionStartHeading: "Faça seu agente ser seu hoje.",
|
||||
sectionStartBody: "Instale o Codewhale e execute-o em um repositório com uma chave de modelo que você já tem ou um modelo no seu próprio computador. Você não precisa de conta, e `codewhale update` o mantém atualizado.",
|
||||
|
||||
contributorsCount: {
|
||||
one: "{n} colaborador",
|
||||
other: "{n} colaboradores",
|
||||
},
|
||||
factsLabel: "Codewhale em resumo",
|
||||
factRust: "Escrito em Rust",
|
||||
factLicense: "Código aberto, licença {license}",
|
||||
factProviders: {
|
||||
one: "{n} rota de provedor",
|
||||
many: "{n} de rotas de provedor",
|
||||
other: "{n} rotas de provedor",
|
||||
},
|
||||
factModels: {
|
||||
one: "{n} modelo",
|
||||
many: "{n} de modelos",
|
||||
other: "{n} modelos",
|
||||
},
|
||||
|
||||
mapHeading: "Um só motor",
|
||||
mapStateReleased: "Lançado",
|
||||
mapStateDeveloping: "Em desenvolvimento, sem download",
|
||||
|
||||
@@ -4,43 +4,37 @@ import type { HomeDict } from "../types";
|
||||
|
||||
export const home: HomeDict = {
|
||||
metaTitle: "Codewhale · Модель может быть чьей угодно. Агент должен быть вашим.",
|
||||
metaDescription: "Codewhale становится открытой средой, где вы создаёте программы вместе со многими агентами, на любой модели, на своём компьютере и в облаке. Сегодня он начинается в вашем терминале и браузере, с открытым исходным кодом, который можно прочитать самому.",
|
||||
metaDescription: "Codewhale — агент для программирования с открытым исходным кодом для вашего терминала, написанный на Rust. Он работает с любой моделью, умеет разделить одну задачу между несколькими агентами, и каждую его строку можно прочитать.",
|
||||
heroTitle: "Модель может быть чьей угодно. Агент должен быть вашим.",
|
||||
heroTitleLead: "Модель может быть чьей угодно.",
|
||||
heroIntro: "{brand} становится открытой средой, где вы создаёте программы вместе со многими агентами, на любой модели, на своём компьютере и в облаке. Сегодня он начинается в вашем терминале и браузере, с открытым исходным кодом, который можно прочитать самому.",
|
||||
heroIntro: "{brand} — агент для программирования с открытым исходным кодом для вашего терминала, написанный на Rust. Он работает с любой моделью, умеет разделить одну задачу между несколькими агентами, и каждую его строку можно прочитать.",
|
||||
readTheCode: "Прочитать код",
|
||||
installNote: "Версия {version} выпущена, и для неё не нужна учётная запись.",
|
||||
captionDevelopment: "Сборка для разработки в настоящем терминале: правки и тесты выполнялись по-настоящему, а модель — сценарная демонстрация.",
|
||||
captionReleased: "Настоящий сеанс в терминале: правки и тесты выполнялись по-настоящему, а модель — сценарная демонстрация.",
|
||||
captureFrameNoPosture: "Название модели и настройка Ask показаны на кадрах до и после этого.",
|
||||
captureFrameNoPosture: "Кадры «Запрос» и «Результат» показывают название модели и настройку Ask.",
|
||||
frameLabelPrompt: "Запрос",
|
||||
frameLabelEdit: "Правка",
|
||||
frameLabelApproval: "Подтверждение",
|
||||
frameLabelResult: "Результат",
|
||||
frameLabelReceipt: "Квитанция",
|
||||
frameSummaryPrompt: "Codewhale в терминале. Запрос «Add a --json flag to ls.py so the tests pass, then run the tests.» отправлен, а строка состояния показывает Ask и модель scripted-demo.",
|
||||
frameSummaryEdit: "Codewhale в терминале. Он прочитал ls.py и изменил его, diff показывает добавленные строки, а карточка спрашивает, запускать ли python3 -m unittest -q.",
|
||||
frameSummaryApproval: "Codewhale в терминале. Показано изменение в ls.py, а команда python3 -m unittest -q ждёт подтверждения. Карточка спрашивает «Do you want to proceed?» и предлагает разрешить один раз, разрешить в этом разговоре или не разрешать.",
|
||||
frameSummaryResult: "Codewhale в терминале. После подтверждения команда выполнила 2 теста и вывела OK. Codewhale сообщает, что добавил --json в ls.py и оба теста проходят, и что в этом сеансе используется локальная сценарная модель.",
|
||||
frameSummaryProvider: "Codewhale в терминале. Форма своего провайдера для эндпоинта, совместимого с OpenAI: имя scripted_local, базовый URL на этом компьютере, модель по умолчанию scripted-demo и необязательная переменная для ключа API.",
|
||||
frameSummaryReceipt: "Codewhale в терминале. Квитанция сеанса: изменён 1 файл и выполнена 1 команда, команду утвердили вы, а изменение файла сделано без вопроса в режиме Ask, затем список того, что квитанция не записывает.",
|
||||
getCodewhale: "Установить Codewhale",
|
||||
heroInstallAria: "Команда установки",
|
||||
shotPreview: "Предпросмотр терминала",
|
||||
screenshotAlt:
|
||||
"Codewhale v{version}: снимок терминала с беседой, полем сообщения и управлением сессией.",
|
||||
proofContributors: "Участников: {count}",
|
||||
sourceCandidate: "Не выпущено",
|
||||
publishedRelease: "выпущено",
|
||||
modelsHeading: "Используйте модели по своему выбору",
|
||||
modelsLink: "Обзор моделей и провайдеров",
|
||||
startHeading: "Начало работы",
|
||||
copy: "Копировать",
|
||||
copied: "Скопировано ✓",
|
||||
contribute: "Участвовать на GitHub",
|
||||
|
||||
belief: "Модель, которой вы пользуетесь, обычно принадлежит кому-то другому. Вы берёте её в аренду, и она меняется без вашего участия. Агент устроен иначе. Он читает ваши файлы, выполняет ваши команды, хранит то, над чем вы работаете, и действует от вашего имени, поэтому он должен принадлежать вам. Codewhale — такой агент. У него открытый исходный код, поэтому вы можете прочитать каждую его строку или попросить его самого прочитать себя вам. Он работает с любой выбранной вами моделью, вы решаете, что ему можно делать без вопросов, и он оставляет запись, которую можно прочитать. Мы строим из него единое место, где вы и многие агенты вместе создаёте программы, от вашего терминала до компьютера в облаке, и он остаётся вашим на всём пути.",
|
||||
|
||||
sectionModelHeading: "Любая модель может работать через вашего агента.",
|
||||
sectionModelBody: "Используйте встроенного провайдера, любую OpenAI-совместимую конечную точку или модель на вашем компьютере и меняйте модели между ходами. Codewhale работает в вашем репозитории без учётной записи, а `codewhale web` позволяет работать с ним в браузере на вашем компьютере.",
|
||||
sectionControlHeading: "Ваш агент подчиняется вам.",
|
||||
sectionControlBody: "Вы решаете, что Codewhale может делать без вопросов, а его команды могут выполняться в песочнице на macOS и Linux. Каждый сеанс оставляет квитанцию: какие файлы он изменил, какие команды выполнил и кто утвердил каждый шаг.",
|
||||
sectionAgentsHeading: "Несколько агентов могут делить с вами одну задачу.",
|
||||
sectionAgentsBody: "Codewhale распределяет работу между агентами с ролями вроде исследователя, исполнителя и рецензента, и вы можете наблюдать за каждым, проверять его и останавливать. Fleet ведёт журнал, поэтому запуск можно продолжить после перезапуска.",
|
||||
sectionDirectionHeading: "Вот куда движется ваш агент.",
|
||||
sectionDirectionBody: "Codewhale становится единой средой, охватывающей ваш терминал, настольное приложение, браузер и облачные компьютеры; всё это работает на одном движке, поэтому ваша работа может переходить между ними и оставаться вашей. Каждая часть ниже говорит, на каком этапе она сегодня.",
|
||||
sectionOpenHeading: "Codewhale открыто создают те, кто им пользуется.",
|
||||
@@ -48,6 +42,28 @@ export const home: HomeDict = {
|
||||
sectionStartHeading: "Сделайте своего агента своим уже сегодня.",
|
||||
sectionStartBody: "Установите Codewhale и запустите его в репозитории с ключом модели, который у вас уже есть, или с моделью на вашем компьютере. Учётная запись не нужна, а `codewhale update` поддерживает его актуальным.",
|
||||
|
||||
contributorsCount: {
|
||||
one: "Участников: {n}",
|
||||
few: "Участников: {n}",
|
||||
many: "Участников: {n}",
|
||||
other: "Участников: {n}",
|
||||
},
|
||||
factsLabel: "Codewhale вкратце",
|
||||
factRust: "Написан на Rust",
|
||||
factLicense: "Открытый код, лицензия {license}",
|
||||
factProviders: {
|
||||
one: "Маршрутов провайдеров: {n}",
|
||||
few: "Маршрутов провайдеров: {n}",
|
||||
many: "Маршрутов провайдеров: {n}",
|
||||
other: "Маршрутов провайдеров: {n}",
|
||||
},
|
||||
factModels: {
|
||||
one: "Моделей: {n}",
|
||||
few: "Моделей: {n}",
|
||||
many: "Моделей: {n}",
|
||||
other: "Моделей: {n}",
|
||||
},
|
||||
|
||||
mapHeading: "Один движок",
|
||||
mapStateReleased: "Выпущено",
|
||||
mapStateDeveloping: "В разработке, без загрузки",
|
||||
|
||||
@@ -4,43 +4,37 @@ import type { HomeDict } from "../types";
|
||||
|
||||
export const home: HomeDict = {
|
||||
metaTitle: "Codewhale · Model herkesin olabilir. Ajan sizin olmalı.",
|
||||
metaDescription: "Codewhale, birçok ajanla, istediğiniz modelle, kendi bilgisayarınızda ve bulutta yazılım geliştirdiğiniz açık ortam hâline geliyor. Bugün terminalinizde ve tarayıcınızda başlıyor; açık kaynaklı ve okumak size ait.",
|
||||
metaDescription: "Codewhale, terminaliniz için Rust ile yazılmış açık kaynaklı bir kodlama ajanıdır. Her modelle çalışır, bir görevi birkaç ajana bölebilir ve her satırını okuyabilirsiniz.",
|
||||
heroTitle: "Model herkesin olabilir. Ajan sizin olmalı.",
|
||||
heroTitleLead: "Model herkesin olabilir.",
|
||||
heroIntro: "{brand}, birçok ajanla, istediğiniz modelle, kendi bilgisayarınızda ve bulutta yazılım geliştirdiğiniz açık ortam hâline geliyor. Bugün terminalinizde ve tarayıcınızda başlıyor; açık kaynaklı ve okumak size ait.",
|
||||
heroIntro: "{brand}, terminaliniz için Rust ile yazılmış açık kaynaklı bir kodlama ajanıdır. Her modelle çalışır, bir görevi birkaç ajana bölebilir ve her satırını okuyabilirsiniz.",
|
||||
readTheCode: "Kodu okuyun",
|
||||
installNote: "{version} sürümü yayımlandı ve hesap gerektirmez.",
|
||||
captionDevelopment: "Gerçek bir terminalde geliştirme sürümü: düzenlemeler ve testler gerçekten çalıştı, model ise senaryolu bir gösterim.",
|
||||
captionReleased: "Gerçek bir terminal oturumu: düzenlemeler ve testler gerçekten çalıştı, model ise senaryolu bir gösterim.",
|
||||
captureFrameNoPosture: "Model adı ve Ask ayarı, bu karenin öncesindeki ve sonrasındaki karelerde gösterilir.",
|
||||
captureFrameNoPosture: "İstem ve Sonuç kareleri model adını ve Ask ayarını gösterir.",
|
||||
frameLabelPrompt: "İstem",
|
||||
frameLabelEdit: "Düzenleme",
|
||||
frameLabelApproval: "Onay",
|
||||
frameLabelResult: "Sonuç",
|
||||
frameLabelReceipt: "Makbuz",
|
||||
frameSummaryPrompt: "Bir terminalde Codewhale. “Add a --json flag to ls.py so the tests pass, then run the tests.” isteği gönderildi ve durum satırı Ask ayarını ve scripted-demo modelini gösteriyor.",
|
||||
frameSummaryEdit: "Bir terminalde Codewhale. ls.py dosyasını okudu ve değiştirdi, fark eklenen satırları gösteriyor ve bir kart python3 -m unittest -q komutunun çalıştırılıp çalıştırılmayacağını soruyor.",
|
||||
frameSummaryApproval: "Bir terminalde Codewhale. ls.py dosyasındaki değişiklik gösteriliyor ve python3 -m unittest -q komutu onay bekliyor. Kart “Do you want to proceed?” diye soruyor ve bir kez izin vermeyi, bu konuşma için izin vermeyi veya izin vermemeyi öneriyor.",
|
||||
frameSummaryResult: "Bir terminalde Codewhale. Onaydan sonra komut 2 test çalıştırdı ve OK yazdırdı. Codewhale, ls.py dosyasına --json eklediğini ve iki testin de geçtiğini, bu oturumun da senaryolu bir yerel model kullandığını söylüyor.",
|
||||
frameSummaryProvider: "Bir terminalde Codewhale. OpenAI uyumlu bir uç nokta için özel sağlayıcı formu: scripted_local adı, bu bilgisayardaki bir temel URL, varsayılan model scripted-demo ve API anahtarı için isteğe bağlı bir değişken.",
|
||||
frameSummaryReceipt: "Bir terminalde Codewhale. Oturumun makbuzu: 1 dosya değişti ve 1 komut çalıştı, komutu siz onayladınız, dosya değişikliği ise Ask ayarında sorulmadan yapıldı; ardından makbuzun kaydetmediklerinin listesi geliyor.",
|
||||
getCodewhale: "Codewhale'i kur",
|
||||
heroInstallAria: "Kurulum komutu",
|
||||
shotPreview: "Terminal önizlemesi",
|
||||
screenshotAlt:
|
||||
"Codewhale v{version}: sohbeti, mesaj alanını ve oturum kontrollerini gösteren terminal kaydı.",
|
||||
proofContributors: "{count} katkıda bulunan",
|
||||
sourceCandidate: "Yayımlanmadı",
|
||||
publishedRelease: "yayımlandı",
|
||||
modelsHeading: "Seçtiğin modelleri kullan",
|
||||
modelsLink: "Modellere ve sağlayıcılara göz at",
|
||||
startHeading: "Başlarken",
|
||||
copy: "Kopyala",
|
||||
copied: "Kopyalandı ✓",
|
||||
contribute: "GitHub’da katkıda bulun",
|
||||
|
||||
belief: "Kullandığınız model çoğunlukla başkasınındır. Onu kiralarsınız ve siz bir şey yapmadan değişir. Ajan farklıdır. Dosyalarınızı okur, komutlarınızı çalıştırır, üzerinde çalıştığınız işi tutar ve sizin adınıza hareket eder; bu yüzden size ait olmalıdır. Codewhale bu ajandır. Açık kaynaklıdır; bu yüzden her satırını okuyabilir ya da kendini size okumasını isteyebilirsiniz. Seçtiğiniz her modelle çalışır, sormadan neler yapabileceğine siz karar verirsiniz ve okuyabileceğiniz bir kayıt bırakır. Onu, sizin ve birçok ajanın birlikte yazılım yaptığı tek bir yere dönüştürüyoruz; terminalinizden buluttaki bir bilgisayara kadar, ve yol boyunca sizin kalır.",
|
||||
|
||||
sectionModelHeading: "Her model, ajanınız üzerinden çalışabilir.",
|
||||
sectionModelBody: "Yerleşik bir sağlayıcı, OpenAI uyumlu herhangi bir uç nokta veya kendi makinenizdeki bir model kullanın ve turlar arasında model değiştirin. Codewhale hesap olmadan deponuzda çalışır ve `codewhale web` ile kendi makinenizde tarayıcınızda onunla çalışabilirsiniz.",
|
||||
sectionControlHeading: "Ajanınız size hesap verir.",
|
||||
sectionControlBody: "Codewhale'in sormadan neler yapabileceğine siz karar verirsiniz ve komutları macOS ve Linux'ta bir korumalı alanda çalışabilir. Her oturum; değiştirdiği dosyaların, çalıştırdığı komutların ve her adımı kimin onayladığının makbuzunu bırakır.",
|
||||
sectionAgentsHeading: "Birden çok ajan bir görevi sizinle paylaşabilir.",
|
||||
sectionAgentsBody: "Codewhale bir işi kaşif, uygulayıcı ve gözden geçirici gibi rollere sahip ajanlara böler; her birini izleyebilir, inceleyebilir ve durdurabilirsiniz. Bir Fleet defter tutar, böylece bir çalıştırma yeniden başlatmadan sonra kaldığı yerden sürebilir.",
|
||||
sectionDirectionHeading: "Ajanınız buraya gidiyor.",
|
||||
sectionDirectionBody: "Codewhale; terminalinizi, bir masaüstü uygulamasını, tarayıcıyı ve bulut bilgisayarlarını kapsayan tek bir ortam hâline geliyor; hepsi tek bir motorla çalışıyor, böylece işiniz aralarında geçebiliyor ve sizin kalıyor. Aşağıdaki her bölüm, bugün nerede durduğunu söyler.",
|
||||
sectionOpenHeading: "Codewhale, onu kullanan insanlar tarafından açıkta geliştirilir.",
|
||||
@@ -48,6 +42,22 @@ export const home: HomeDict = {
|
||||
sectionStartHeading: "Ajanınızı bugün kendinizin yapın.",
|
||||
sectionStartBody: "Codewhale'i kurun ve zaten sahip olduğunuz bir model anahtarıyla ya da kendi bilgisayarınızdaki bir modelle bir depoda çalıştırın. Hesaba ihtiyacınız yok ve `codewhale update` onu güncel tutar.",
|
||||
|
||||
contributorsCount: {
|
||||
one: "{n} katkıda bulunan",
|
||||
other: "{n} katkıda bulunan",
|
||||
},
|
||||
factsLabel: "Bir bakışta Codewhale",
|
||||
factRust: "Rust ile yazıldı",
|
||||
factLicense: "Açık kaynak, {license} lisansı",
|
||||
factProviders: {
|
||||
one: "{n} sağlayıcı rotası",
|
||||
other: "{n} sağlayıcı rotası",
|
||||
},
|
||||
factModels: {
|
||||
one: "{n} model",
|
||||
other: "{n} model",
|
||||
},
|
||||
|
||||
mapHeading: "Tek bir motor",
|
||||
mapStateReleased: "Yayımlandı",
|
||||
mapStateDeveloping: "Geliştiriliyor, indirme yok",
|
||||
|
||||
@@ -168,39 +168,41 @@ export interface HomeDict {
|
||||
frameLabelEdit: string;
|
||||
frameLabelApproval: string;
|
||||
frameLabelResult: string;
|
||||
frameLabelReceipt: string;
|
||||
/** One accessible summary for each terminal frame the home shows. */
|
||||
frameSummaryPrompt: string;
|
||||
frameSummaryEdit: string;
|
||||
frameSummaryApproval: string;
|
||||
frameSummaryResult: string;
|
||||
frameSummaryProvider: string;
|
||||
frameSummaryReceipt: string;
|
||||
/** Primary action → /install, e.g. "Get Codewhale". */
|
||||
getCodewhale: string;
|
||||
/** Accessible name for the hero install command and its platform choice. */
|
||||
heroInstallAria: string;
|
||||
/** Secondary action → /product, e.g. "Explore the product". */
|
||||
/** Accessible name for a terminal capture's region and for the frame tabs. */
|
||||
shotPreview: string;
|
||||
/** Screenshot alt text for the current media manifest capture. */
|
||||
screenshotAlt: string;
|
||||
|
||||
/** "Latest release {tag}" */
|
||||
proofContributors: string;
|
||||
/** "Source" / "Unreleased" — prepended to `v{version}`. */
|
||||
sourceCandidate: string;
|
||||
/** "released" / "unreleased" — the machine-readable source-state label. */
|
||||
publishedRelease: string;
|
||||
|
||||
/** What a person gains: heading, lede, and three [title, body] rows. */
|
||||
modelsHeading: string;
|
||||
modelsLink: string;
|
||||
|
||||
startHeading: string;
|
||||
copy: string;
|
||||
copied: string;
|
||||
contribute: string;
|
||||
|
||||
belief: string;
|
||||
/** `{n}` is the contributor count; pick the form with `pluralText`. */
|
||||
contributorsCount: PluralForms;
|
||||
/** Accessible name for the home's short list of facts. */
|
||||
factsLabel: string;
|
||||
factRust: string;
|
||||
/** `{license}` is the licence name from the generated facts. */
|
||||
factLicense: string;
|
||||
/** `{n}` is the provider route count of the published release. */
|
||||
factProviders: PluralForms;
|
||||
/** `{n}` is the catalog model count of the published release. */
|
||||
factModels: PluralForms;
|
||||
|
||||
sectionModelHeading: string;
|
||||
sectionModelBody: string;
|
||||
sectionControlHeading: string;
|
||||
sectionControlBody: string;
|
||||
sectionAgentsHeading: string;
|
||||
sectionAgentsBody: string;
|
||||
sectionDirectionHeading: string;
|
||||
sectionDirectionBody: string;
|
||||
sectionOpenHeading: string;
|
||||
|
||||
@@ -4,43 +4,37 @@ import type { HomeDict } from "../types";
|
||||
|
||||
export const home: HomeDict = {
|
||||
metaTitle: "Codewhale · Модель може належати будь-кому. Агент має належати вам.",
|
||||
metaDescription: "Codewhale стає відкритим середовищем, де ви створюєте програми разом із багатьма агентами, на будь-якій моделі, на власному комп’ютері та в хмарі. Сьогодні він починається у вашому терміналі та браузері, з відкритим кодом, який можна прочитати самому.",
|
||||
metaDescription: "Codewhale — агент для програмування з відкритим кодом для вашого термінала, написаний на Rust. Він працює з будь-якою моделлю, вміє розділити одне завдання між кількома агентами, і кожен його рядок можна прочитати.",
|
||||
heroTitle: "Модель може належати будь-кому. Агент має належати вам.",
|
||||
heroTitleLead: "Модель може належати будь-кому.",
|
||||
heroIntro: "{brand} стає відкритим середовищем, де ви створюєте програми разом із багатьма агентами, на будь-якій моделі, на власному комп’ютері та в хмарі. Сьогодні він починається у вашому терміналі та браузері, з відкритим кодом, який можна прочитати самому.",
|
||||
heroIntro: "{brand} — агент для програмування з відкритим кодом для вашого термінала, написаний на Rust. Він працює з будь-якою моделлю, вміє розділити одне завдання між кількома агентами, і кожен його рядок можна прочитати.",
|
||||
readTheCode: "Прочитати код",
|
||||
installNote: "Версію {version} випущено, і облікового запису для неї не потрібно.",
|
||||
captionDevelopment: "Збірка для розробки в справжньому терміналі: редагування й тести виконувалися насправді, а модель — сценарна демонстрація.",
|
||||
captionReleased: "Справжній сеанс у терміналі: редагування й тести виконувалися насправді, а модель — сценарна демонстрація.",
|
||||
captureFrameNoPosture: "Назву моделі та налаштування Ask показано на кадрах до і після цього.",
|
||||
captureFrameNoPosture: "Кадри «Запит» і «Результат» показують назву моделі та налаштування Ask.",
|
||||
frameLabelPrompt: "Запит",
|
||||
frameLabelEdit: "Редагування",
|
||||
frameLabelApproval: "Підтвердження",
|
||||
frameLabelResult: "Результат",
|
||||
frameLabelReceipt: "Квитанція",
|
||||
frameSummaryPrompt: "Codewhale у терміналі. Запит «Add a --json flag to ls.py so the tests pass, then run the tests.» надіслано, а рядок стану показує Ask і модель scripted-demo.",
|
||||
frameSummaryEdit: "Codewhale у терміналі. Він прочитав ls.py і змінив його, diff показує додані рядки, а картка запитує, чи запускати python3 -m unittest -q.",
|
||||
frameSummaryApproval: "Codewhale у терміналі. Показано зміну в ls.py, а команда python3 -m unittest -q чекає на підтвердження. Картка запитує «Do you want to proceed?» і пропонує дозволити один раз, дозволити в цій розмові або не дозволяти.",
|
||||
frameSummaryResult: "Codewhale у терміналі. Після підтвердження команда виконала 2 тести й вивела OK. Codewhale повідомляє, що додав --json до ls.py і обидва тести проходять, і що в цьому сеансі працює локальна сценарна модель.",
|
||||
frameSummaryProvider: "Codewhale у терміналі. Форма власного провайдера для ендпоінта, сумісного з OpenAI: назва scripted_local, базовий URL на цьому комп’ютері, типова модель scripted-demo і необов’язкова змінна для ключа API.",
|
||||
frameSummaryReceipt: "Codewhale у терміналі. Квитанція сеансу: змінено 1 файл і виконано 1 команду, команду схвалили ви, а зміну файлу зроблено без запитання в режимі Ask, далі список того, чого квитанція не записує.",
|
||||
getCodewhale: "Установити Codewhale",
|
||||
heroInstallAria: "Команда встановлення",
|
||||
shotPreview: "Попередній перегляд термінала",
|
||||
screenshotAlt:
|
||||
"Codewhale v{version}: знімок термінала з розмовою, полем повідомлення й керуванням сесією.",
|
||||
proofContributors: "Учасників: {count}",
|
||||
sourceCandidate: "Не випущено",
|
||||
publishedRelease: "випущено",
|
||||
modelsHeading: "Використовуйте моделі на свій вибір",
|
||||
modelsLink: "Переглянути моделі та провайдерів",
|
||||
startHeading: "Початок роботи",
|
||||
copy: "Копіювати",
|
||||
copied: "Скопійовано ✓",
|
||||
contribute: "Долучитися на GitHub",
|
||||
|
||||
belief: "Модель, якою ви користуєтеся, зазвичай належить комусь іншому. Ви орендуєте її, і вона змінюється без вашої участі. Агент інший. Він читає ваші файли, виконує ваші команди, тримає те, над чим ви працюєте, і діє від вашого імені, тож він має належати вам. Codewhale — це такий агент. Він має відкритий код, тому ви можете прочитати кожен його рядок або попросити його самого прочитати себе вам. Він працює з будь-якою обраною вами моделлю, ви вирішуєте, що йому можна робити без запитань, і він залишає запис, який можна прочитати. Ми будуємо з нього єдине місце, де ви та багато агентів разом створюєте програми, від вашого терміналу до комп’ютера в хмарі, і він залишається вашим на всьому шляху.",
|
||||
|
||||
sectionModelHeading: "Будь-яка модель може працювати через вашого агента.",
|
||||
sectionModelBody: "Використовуйте вбудованого провайдера, будь-яку OpenAI-сумісну кінцеву точку або модель на вашому комп’ютері й змінюйте моделі між ходами. Codewhale працює у вашому репозиторії без облікового запису, а `codewhale web` дає змогу працювати з ним у браузері на вашому комп’ютері.",
|
||||
sectionControlHeading: "Ваш агент підпорядковується вам.",
|
||||
sectionControlBody: "Ви вирішуєте, що Codewhale може робити без запитань, а його команди можуть виконуватися в пісочниці на macOS і Linux. Кожен сеанс залишає квитанцію: які файли він змінив, які команди виконав і хто схвалив кожен крок.",
|
||||
sectionAgentsHeading: "Кілька агентів можуть ділити з вами одне завдання.",
|
||||
sectionAgentsBody: "Codewhale розподіляє роботу між агентами з ролями на кшталт дослідника, виконавця та рецензента, а ви можете спостерігати за кожним, перевіряти його й зупиняти. Fleet веде журнал, тож запуск можна продовжити після перезапуску.",
|
||||
sectionDirectionHeading: "Ось куди рухається ваш агент.",
|
||||
sectionDirectionBody: "Codewhale стає єдиним середовищем, що охоплює ваш термінал, настільний застосунок, браузер і хмарні комп’ютери; усе це працює на одному рушії, тож ваша робота може переходити між ними й залишатися вашою. Кожна частина нижче каже, на якому етапі вона сьогодні.",
|
||||
sectionOpenHeading: "Codewhale відкрито створюють ті, хто ним користується.",
|
||||
@@ -48,6 +42,28 @@ export const home: HomeDict = {
|
||||
sectionStartHeading: "Зробіть свого агента своїм уже сьогодні.",
|
||||
sectionStartBody: "Установіть Codewhale і запустіть його в репозиторії з ключем моделі, який у вас уже є, або з моделлю на вашому комп’ютері. Обліковий запис не потрібен, а `codewhale update` підтримує його актуальним.",
|
||||
|
||||
contributorsCount: {
|
||||
one: "Учасників: {n}",
|
||||
few: "Учасників: {n}",
|
||||
many: "Учасників: {n}",
|
||||
other: "Учасників: {n}",
|
||||
},
|
||||
factsLabel: "Codewhale коротко",
|
||||
factRust: "Написаний на Rust",
|
||||
factLicense: "Відкритий код, ліцензія {license}",
|
||||
factProviders: {
|
||||
one: "Маршрутів провайдерів: {n}",
|
||||
few: "Маршрутів провайдерів: {n}",
|
||||
many: "Маршрутів провайдерів: {n}",
|
||||
other: "Маршрутів провайдерів: {n}",
|
||||
},
|
||||
factModels: {
|
||||
one: "Моделей: {n}",
|
||||
few: "Моделей: {n}",
|
||||
many: "Моделей: {n}",
|
||||
other: "Моделей: {n}",
|
||||
},
|
||||
|
||||
mapHeading: "Один рушій",
|
||||
mapStateReleased: "Випущено",
|
||||
mapStateDeveloping: "У розробці, без завантаження",
|
||||
|
||||
@@ -4,43 +4,37 @@ import type { HomeDict } from "../types";
|
||||
|
||||
export const home: HomeDict = {
|
||||
metaTitle: "Codewhale · Mô hình có thể là của bất kỳ ai. Agent nên là của bạn.",
|
||||
metaDescription: "Codewhale đang trở thành môi trường mở để bạn xây dựng phần mềm cùng nhiều agent, trên bất kỳ mô hình nào, ngay trên máy tính của bạn và trên đám mây. Hôm nay nó bắt đầu trong terminal và trình duyệt của bạn, mã nguồn mở và do bạn tự đọc được.",
|
||||
metaDescription: "Codewhale là một agent lập trình mã nguồn mở cho terminal của bạn, được viết bằng Rust. Nó làm việc với bất kỳ mô hình nào, có thể chia một nhiệm vụ cho nhiều agent, và bạn có thể đọc từng dòng mã của nó.",
|
||||
heroTitle: "Mô hình có thể là của bất kỳ ai. Agent nên là của bạn.",
|
||||
heroTitleLead: "Mô hình có thể là của bất kỳ ai.",
|
||||
heroIntro: "{brand} đang trở thành môi trường mở để bạn xây dựng phần mềm cùng nhiều agent, trên bất kỳ mô hình nào, ngay trên máy tính của bạn và trên đám mây. Hôm nay nó bắt đầu trong terminal và trình duyệt của bạn, mã nguồn mở và do bạn tự đọc được.",
|
||||
heroIntro: "{brand} là một agent lập trình mã nguồn mở cho terminal của bạn, được viết bằng Rust. Nó làm việc với bất kỳ mô hình nào, có thể chia một nhiệm vụ cho nhiều agent, và bạn có thể đọc từng dòng mã của nó.",
|
||||
readTheCode: "Đọc mã",
|
||||
installNote: "Phiên bản {version} đã phát hành và không cần tài khoản.",
|
||||
captionDevelopment: "Một bản dựng phát triển trong terminal thật: các chỉnh sửa và bài kiểm thử đã chạy thật, còn mô hình là bản trình diễn theo kịch bản.",
|
||||
captionReleased: "Một phiên terminal thật: các chỉnh sửa và bài kiểm thử đã chạy thật, còn mô hình là bản trình diễn theo kịch bản.",
|
||||
captureFrameNoPosture: "Tên mô hình và thiết lập Ask được hiển thị ở các khung trước và sau khung này.",
|
||||
captureFrameNoPosture: "Các khung Yêu cầu và Kết quả hiển thị tên mô hình và thiết lập Ask.",
|
||||
frameLabelPrompt: "Yêu cầu",
|
||||
frameLabelEdit: "Chỉnh sửa",
|
||||
frameLabelApproval: "Phê duyệt",
|
||||
frameLabelResult: "Kết quả",
|
||||
frameLabelReceipt: "Biên nhận",
|
||||
frameSummaryPrompt: "Codewhale trong một terminal. Yêu cầu “Add a --json flag to ls.py so the tests pass, then run the tests.” đã được gửi, và dòng trạng thái hiển thị Ask cùng mô hình scripted-demo.",
|
||||
frameSummaryEdit: "Codewhale trong một terminal. Nó đã đọc ls.py và sửa tệp, diff hiển thị các dòng được thêm, và một thẻ hỏi có chạy python3 -m unittest -q hay không.",
|
||||
frameSummaryApproval: "Codewhale trong một terminal. Thay đổi trong ls.py được hiển thị, và lệnh python3 -m unittest -q đang chờ phê duyệt. Thẻ hỏi “Do you want to proceed?” và đề nghị cho phép một lần, cho phép trong cuộc trò chuyện này hoặc không cho phép.",
|
||||
frameSummaryResult: "Codewhale trong một terminal. Sau khi được phê duyệt, lệnh đã chạy 2 bài kiểm thử và in ra OK. Codewhale cho biết nó đã thêm --json vào ls.py và cả hai bài kiểm thử đều đạt, và phiên này dùng một mô hình cục bộ theo kịch bản.",
|
||||
frameSummaryProvider: "Codewhale trong một terminal. Biểu mẫu nhà cung cấp tùy chỉnh cho một endpoint tương thích OpenAI, với tên scripted_local, URL gốc trên máy tính này, mô hình mặc định scripted-demo và một biến tùy chọn cho khóa API.",
|
||||
frameSummaryReceipt: "Codewhale trong một terminal. Biên nhận của phiên: 1 tệp đã thay đổi và 1 lệnh đã chạy, lệnh do bạn phê duyệt còn thay đổi tệp được thực hiện mà không hỏi trong Ask, sau đó là danh sách những gì biên nhận không ghi lại.",
|
||||
getCodewhale: "Cài đặt Codewhale",
|
||||
heroInstallAria: "Lệnh cài đặt",
|
||||
shotPreview: "Xem trước terminal",
|
||||
screenshotAlt:
|
||||
"Codewhale v{version}: ảnh chụp terminal với cuộc trò chuyện, ô nhập tin nhắn và điều khiển phiên.",
|
||||
proofContributors: "{count} người đóng góp",
|
||||
sourceCandidate: "Chưa phát hành",
|
||||
publishedRelease: "đã phát hành",
|
||||
modelsHeading: "Sử dụng mô hình bạn chọn",
|
||||
modelsLink: "Xem mô hình và nhà cung cấp",
|
||||
startHeading: "Bắt đầu sử dụng",
|
||||
copy: "Sao chép",
|
||||
copied: "Đã sao chép ✓",
|
||||
contribute: "Đóng góp trên GitHub",
|
||||
|
||||
belief: "Mô hình bạn dùng thường là của người khác. Bạn thuê nó, và nó thay đổi mà bạn không quyết định. Agent thì khác. Nó đọc tệp của bạn, chạy lệnh của bạn, giữ lại việc bạn đang làm và hành động nhân danh bạn, nên nó phải thuộc về bạn. Codewhale là agent đó. Nó là mã nguồn mở, nên bạn có thể đọc từng dòng của nó, hoặc nhờ nó tự đọc cho bạn nghe. Nó làm việc với bất kỳ mô hình nào bạn chọn, bạn quyết định nó được làm gì mà không cần hỏi, và nó để lại một bản ghi mà bạn đọc được. Chúng tôi đang xây dựng nó thành một nơi duy nhất để bạn và nhiều agent cùng làm phần mềm, từ terminal của bạn đến một máy tính trên đám mây, và nó vẫn là của bạn suốt chặng đường.",
|
||||
|
||||
sectionModelHeading: "Mọi mô hình đều có thể làm việc qua agent của bạn.",
|
||||
sectionModelBody: "Dùng nhà cung cấp tích hợp sẵn, bất kỳ endpoint tương thích OpenAI nào hoặc một mô hình trên máy của bạn, và đổi mô hình giữa các lượt. Codewhale chạy trong kho mã của bạn mà không cần tài khoản, và `codewhale web` cho phép bạn làm việc với nó trong trình duyệt trên chính máy của bạn.",
|
||||
sectionControlHeading: "Agent của bạn chịu trách nhiệm trước bạn.",
|
||||
sectionControlBody: "Bạn quyết định Codewhale được làm gì mà không cần hỏi, và các lệnh của nó có thể chạy trong sandbox trên macOS và Linux. Mỗi phiên để lại một biên nhận ghi các tệp nó đã thay đổi, các lệnh nó đã chạy và ai đã phê duyệt từng bước.",
|
||||
sectionAgentsHeading: "Nhiều agent có thể cùng bạn chia sẻ một nhiệm vụ.",
|
||||
sectionAgentsBody: "Codewhale chia một công việc cho nhiều agent với các vai trò như người khám phá, người triển khai và người đánh giá, và bạn có thể theo dõi, kiểm tra và dừng từng agent. Một Fleet giữ sổ ghi, nên một lần chạy có thể tiếp tục sau khi khởi động lại.",
|
||||
sectionDirectionHeading: "Đây là nơi agent của bạn đang hướng tới.",
|
||||
sectionDirectionBody: "Codewhale đang trở thành một môi trường duy nhất trải khắp terminal, ứng dụng máy tính, trình duyệt và máy tính đám mây của bạn, tất cả chạy bằng một động cơ, để công việc của bạn có thể di chuyển giữa chúng và vẫn là của bạn. Mỗi phần bên dưới cho biết hôm nay nó đang ở đâu.",
|
||||
sectionOpenHeading: "Codewhale được xây dựng công khai bởi những người dùng nó.",
|
||||
@@ -48,6 +42,19 @@ export const home: HomeDict = {
|
||||
sectionStartHeading: "Hãy biến agent thành của bạn ngay hôm nay.",
|
||||
sectionStartBody: "Cài Codewhale và chạy nó trong một kho mã với khóa mô hình bạn đã có hoặc một mô hình trên máy tính của bạn. Bạn không cần tài khoản, và `codewhale update` giữ nó luôn mới.",
|
||||
|
||||
contributorsCount: {
|
||||
other: "{n} người đóng góp",
|
||||
},
|
||||
factsLabel: "Codewhale trong nháy mắt",
|
||||
factRust: "Viết bằng Rust",
|
||||
factLicense: "Mã nguồn mở, giấy phép {license}",
|
||||
factProviders: {
|
||||
other: "{n} tuyến nhà cung cấp",
|
||||
},
|
||||
factModels: {
|
||||
other: "{n} mô hình",
|
||||
},
|
||||
|
||||
mapHeading: "Một động cơ",
|
||||
mapStateReleased: "Đã phát hành",
|
||||
mapStateDeveloping: "Đang phát triển, chưa có bản tải",
|
||||
|
||||
@@ -4,43 +4,37 @@ import type { HomeDict } from "../types";
|
||||
|
||||
export const home: HomeDict = {
|
||||
metaTitle: "Codewhale · 模型可以是任何人的。智能体应当属于你。",
|
||||
metaDescription: "Codewhale 正在成为一个开放的环境:你可以在自己的电脑上和云端,使用任意模型,与许多智能体一起构建软件。今天,它从你的终端和浏览器开始,开源,代码任你阅读。",
|
||||
metaDescription: "Codewhale 是一个运行在终端里的开源编程智能体,用 Rust 编写。它支持任意模型,可以把一项任务分给多个智能体,而且它的每一行代码都任你阅读。",
|
||||
heroTitle: "模型可以是任何人的。智能体应当属于你。",
|
||||
heroTitleLead: "模型可以是任何人的。",
|
||||
heroIntro: "{brand} 正在成为一个开放的环境:你可以在自己的电脑上和云端,使用任意模型,与许多智能体一起构建软件。今天,它从你的终端和浏览器开始,开源,代码任你阅读。",
|
||||
heroIntro: "{brand} 是一个运行在终端里的开源编程智能体,用 Rust 编写。它支持任意模型,可以把一项任务分给多个智能体,而且它的每一行代码都任你阅读。",
|
||||
readTheCode: "阅读代码",
|
||||
installNote: "版本 {version} 已发布,无需账号。",
|
||||
captionDevelopment: "开发版本在真实终端中的运行:编辑和测试都是真实执行的,模型是脚本化的演示。",
|
||||
captionReleased: "真实的终端会话:编辑和测试都是真实执行的,模型是脚本化的演示。",
|
||||
captureFrameNoPosture: "模型名称和 Ask 设置显示在这一帧之前和之后的画面中。",
|
||||
captureFrameNoPosture: "“提示”和“结果”两帧显示了模型名称和 Ask 设置。",
|
||||
frameLabelPrompt: "提示",
|
||||
frameLabelEdit: "编辑",
|
||||
frameLabelApproval: "审批",
|
||||
frameLabelResult: "结果",
|
||||
frameLabelReceipt: "回执",
|
||||
frameSummaryPrompt: "终端中的 Codewhale。请求“Add a --json flag to ls.py so the tests pass, then run the tests.”已发送,状态栏显示 Ask 和模型 scripted-demo。",
|
||||
frameSummaryEdit: "终端中的 Codewhale。它读取并修改了 ls.py,差异中显示了新增的行,一张卡片询问是否运行 python3 -m unittest -q。",
|
||||
frameSummaryApproval: "终端中的 Codewhale。画面显示了对 ls.py 的修改,命令 python3 -m unittest -q 正在等待批准。卡片询问“Do you want to proceed?”,并提供允许一次、在本次对话中允许或不允许三个选项。",
|
||||
frameSummaryResult: "终端中的 Codewhale。批准后,命令运行了 2 个测试并输出 OK。Codewhale 说明它已为 ls.py 添加 --json,两个测试都通过,并说明本次会话使用的是脚本化的本地模型。",
|
||||
frameSummaryProvider: "终端中的 Codewhale。用于 OpenAI 兼容端点的自定义提供方表单,名称为 scripted_local,基础 URL 指向本机,默认模型为 scripted-demo,API 密钥变量为可选项。",
|
||||
frameSummaryReceipt: "终端中的 Codewhale。本次会话的回执:修改了 1 个文件,运行了 1 条命令,命令由你批准,文件修改在 Ask 设置下未经询问完成,随后列出回执未记录的内容。",
|
||||
getCodewhale: "安装 Codewhale",
|
||||
heroInstallAria: "安装命令",
|
||||
shotPreview: "本地终端演示",
|
||||
screenshotAlt:
|
||||
"Codewhale v{version} 的终端实录画面,包含对话、消息输入框与会话控制。",
|
||||
proofContributors: "{count} 位贡献者",
|
||||
sourceCandidate: "未发布",
|
||||
publishedRelease: "已发布",
|
||||
modelsHeading: "使用你选择的模型",
|
||||
modelsLink: "浏览模型与提供商",
|
||||
startHeading: "从一件有用的任务开始",
|
||||
copy: "复制",
|
||||
copied: "已复制 ✓",
|
||||
contribute: "参与贡献",
|
||||
|
||||
belief: "你使用的模型通常是别人的。你租用它,它会在你不知情时发生变化。智能体则不同。它读取你的文件,运行你的命令,记住你正在做的工作,并以你的名义行事,所以它应当属于你。Codewhale 就是这样的智能体。它是开源的,所以你可以阅读它的每一行代码,也可以让它把自己读给你听。它支持你选择的任意模型,由你决定它可以在不询问的情况下做什么,并且它会留下一份你能读懂的记录。我们正在把它建设成一个地方,让你和许多智能体在这里一起开发软件,从你的终端一直到云端的一台计算机,而且它自始至终都属于你。",
|
||||
|
||||
sectionModelHeading: "任何模型都可以通过你的智能体工作。",
|
||||
sectionModelBody: "使用内置提供商、任意 OpenAI 兼容端点,或你自己机器上的模型,并且可以在两轮对话之间更换模型。Codewhale 在你的仓库中运行,无需账号,而 `codewhale web` 让你在自己机器上的浏览器里使用它。",
|
||||
sectionControlHeading: "你的智能体对你负责。",
|
||||
sectionControlBody: "由你决定 Codewhale 可以在不询问的情况下做什么,它的命令可以在 macOS 和 Linux 上的沙箱中运行。每次会话都会留下一份回执,记录它修改的文件、运行的命令,以及每一步由谁批准。",
|
||||
sectionAgentsHeading: "多个智能体可以与你共同完成一项任务。",
|
||||
sectionAgentsBody: "Codewhale 把一项工作分给多个智能体,各有探索者、实施者和审查者等角色,你可以查看、检查并停止其中每一个。Fleet 会保存一份账本,所以重启之后,运行可以接着继续。",
|
||||
sectionDirectionHeading: "这是你的智能体将要去的地方。",
|
||||
sectionDirectionBody: "Codewhale 正在成为横跨你的终端、桌面应用、浏览器和云端计算机的统一环境,全部由同一个引擎驱动,所以你的工作可以在它们之间转移,并始终属于你。下面每个部分都说明了它今天所处的状态。",
|
||||
sectionOpenHeading: "Codewhale 由使用它的人公开构建。",
|
||||
@@ -48,6 +42,19 @@ export const home: HomeDict = {
|
||||
sectionStartHeading: "今天就让你的智能体属于你。",
|
||||
sectionStartBody: "安装 Codewhale,并在一个仓库中使用你已有的模型密钥,或你自己电脑上的模型来运行它。你无需账号,`codewhale update` 会让它保持最新。",
|
||||
|
||||
contributorsCount: {
|
||||
other: "{n} 位贡献者",
|
||||
},
|
||||
factsLabel: "Codewhale 一览",
|
||||
factRust: "用 Rust 编写",
|
||||
factLicense: "开源,{license} 许可证",
|
||||
factProviders: {
|
||||
other: "{n} 条提供商路由",
|
||||
},
|
||||
factModels: {
|
||||
other: "{n} 个模型",
|
||||
},
|
||||
|
||||
mapHeading: "一个引擎",
|
||||
mapStateReleased: "已发布",
|
||||
mapStateDeveloping: "开发中,暂无下载",
|
||||
|
||||
@@ -2,7 +2,7 @@ import { ALL_LOCALES } from "./config";
|
||||
|
||||
export type ReviewState = "native" | "machine";
|
||||
|
||||
const NATIVE_LOCALES: readonly string[] = ["en", "zh"];
|
||||
const NATIVE_LOCALES: readonly string[] = ["en"];
|
||||
|
||||
export function reviewState(locale: string): ReviewState {
|
||||
return NATIVE_LOCALES.includes(locale) ? "native" : "machine";
|
||||
|
||||
@@ -3,8 +3,8 @@ import { describe, expect, it } from "vitest";
|
||||
import { siteCss } from "./site-css";
|
||||
|
||||
// The whale-road (codewhale-design/DIRECTION.md): one character drawn from
|
||||
// the v2 kit, one horizon, the sea below it, and a header that stays usable
|
||||
// in every locale.
|
||||
// the v2 kit, a footer on the page's own ground, and a header that stays
|
||||
// usable in every locale.
|
||||
const CSS = siteCss();
|
||||
const web = (path: string) => readFileSync(new URL(`../${path}`, import.meta.url), "utf8");
|
||||
const POSES = [
|
||||
@@ -39,35 +39,29 @@ describe("the whale", () => {
|
||||
expect(web("app/styles/shell.css")).toContain('mask: url("/brand/mark-mono.svg")');
|
||||
});
|
||||
|
||||
it("keeps the founder's 404 poster, seals and recovered water drawing", () => {
|
||||
it("keeps the founder's 404 poster and seals", () => {
|
||||
// The Codwhale poster is the 404 joke, restored on purpose (site merge plan R4).
|
||||
expect(existsSync(new URL("../public/codwhale-404.webp", import.meta.url))).toBe(true);
|
||||
expect(web("components/route-state.tsx")).toContain('src="/codwhale-404.webp"');
|
||||
// Seals and the recovered static water drawing use the original geometry.
|
||||
expect(existsSync(new URL("../components/seal.tsx", import.meta.url))).toBe(true);
|
||||
expect(existsSync(new URL("../components/strata.tsx", import.meta.url))).toBe(true);
|
||||
expect(existsSync(new URL("../components/install-binary.tsx", import.meta.url))).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe("the horizon", () => {
|
||||
it("ends every page on one horizon line, then the sea", () => {
|
||||
describe("the footer", () => {
|
||||
it("sits on the page's own ground under one hairline, with no navy band", () => {
|
||||
const footer = web("components/footer.tsx");
|
||||
expect(footer).toContain('className="horizon"');
|
||||
expect(footer).not.toContain("Strata");
|
||||
expect(CSS).toMatch(/\.site-footer-sea \{[^}]*background: var\(--sea\)/);
|
||||
// A page already ending in the sea (home) runs on without a second line.
|
||||
expect(CSS).toMatch(/main:has\(\.sea-continues:last-child\) \+ \.site-footer \.site-footer-sea > \.horizon \{ display: none; \}/);
|
||||
const home = web("app/[locale]/page.tsx");
|
||||
expect(home).toContain("sea-continues");
|
||||
expect(footer).not.toMatch(/horizon|sea-texture|site-footer-sea|Strata/);
|
||||
expect(CSS).toMatch(/\.site-footer \{[^}]*border-top: 1px solid var\(--line\);/);
|
||||
expect(CSS).not.toMatch(/\.site-footer \{[^}]*background:/);
|
||||
expect(CSS).not.toMatch(/site-footer-sea|sea-texture/);
|
||||
expect(web("app/[locale]/page.tsx")).not.toContain("sea-continues");
|
||||
});
|
||||
|
||||
it("paints the stage with the dark set in both appearances", () => {
|
||||
expect(CSS).toMatch(/\.stage,\s*\.site-footer\s*\{[^}]*--ring: var\(--gpui-dark-accent\);[^}]*color-scheme: dark;/);
|
||||
});
|
||||
|
||||
it("keeps the horizon visible in forced colours", () => {
|
||||
expect(CSS).toMatch(/@media \(forced-colors: active\) \{\s*\.horizon \{ background: CanvasText; \}/);
|
||||
it("folds its four link groups into disclosures on a phone", () => {
|
||||
const footer = web("components/footer.tsx");
|
||||
expect(footer).toContain('<details key={group.label} className="site-footer-fold"');
|
||||
expect(CSS).toMatch(/@media \(max-width: 640px\) \{\s*\.site-footer-links \{ display: none; \}\s*\.site-footer-folds \{ display: block; \}/);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -75,16 +75,12 @@ describe("public website copy contracts", () => {
|
||||
const install = pageSource("install/page.tsx");
|
||||
|
||||
expect(homepage).toContain("facts.latestPublishedRelease");
|
||||
// The machine-readable source-state attribute stays a literal.
|
||||
expect(homepage).toContain('data-source-state={sourceIsPublished ? "published release" : "source candidate"}');
|
||||
expect(homepage).toContain("getReleaseUpdates(facts.latestPublishedRelease)");
|
||||
expect(homepage).toContain("publishedRelease.version");
|
||||
// The home names only the published release; the source version never
|
||||
// reaches it.
|
||||
expect(homepage).toContain("fill(d.installNote, { version: release.version })");
|
||||
expect(homepage).not.toContain("facts.version");
|
||||
expect(EN_HOME.installNote).toBe("Version {version} is released, and it needs no account.");
|
||||
expect(readFileSync(new URL("../components/release-updates.tsx", import.meta.url), "utf8")).toContain("href={release.url}");
|
||||
// The visible wording moved into the dictionary layer (#4934). Assert the
|
||||
// rendered contract — the EN reference value plus the page's use of it —
|
||||
// instead of a raw TSX string, and hold every locale to a real value.
|
||||
expect(homepage).toContain("d.sourceCandidate");
|
||||
expect(EN_HOME.sourceCandidate).toBe("Unreleased");
|
||||
// A development-source route count is not a released-provider total.
|
||||
expect(homepage).not.toContain("<span>{providerRoutes}</span>");
|
||||
expect(homepage).not.toContain("releases/tag/v${version}");
|
||||
|
||||
@@ -567,7 +567,8 @@ done
|
||||
expect(text("web/lib/i18n/links.ts")).toContain(
|
||||
'export const REPO_URL = "https://github.com/codewhale-hq/CodeWhale"',
|
||||
);
|
||||
expect(footer).toContain("GITEE_ENABLED &&");
|
||||
// Mirrors are listed on /install#mirrors, not in the footer.
|
||||
expect(footer).not.toMatch(/gitee|cnb\.cool|npmmirror/i);
|
||||
});
|
||||
|
||||
it("keeps supplied terminal screenshots and website dimensions truthful", () => {
|
||||
@@ -595,15 +596,20 @@ done
|
||||
|
||||
const readme = text("README.md");
|
||||
const homePage = text("web/app/[locale]/page.tsx");
|
||||
const guidePage = text("web/app/[locale]/docs/guide/page.tsx");
|
||||
expect(readme).toContain(matrix.screenshot.readme);
|
||||
expect(`web/public${TERMINAL_SCREENSHOT.src}`).toBe(matrix.screenshot.website);
|
||||
expect(imageDimensions(websiteImage)).toEqual([TERMINAL_SCREENSHOT.width, TERMINAL_SCREENSHOT.height]);
|
||||
expect(homePage).toContain("<NativeTerminalGallery");
|
||||
// The home shows frames from the released version only; the
|
||||
// development-build gallery lives on the docs guide.
|
||||
expect(homePage).toContain("RELEASED_CAPTURE_FRAMES");
|
||||
expect(homePage).not.toContain("NativeTerminalGallery");
|
||||
expect(guidePage).toContain("<NativeTerminalGallery");
|
||||
expect(TERMINAL_SCREENSHOT.capture).toBe("web/lib/terminal-captures/website-home-100x24.json");
|
||||
expect(matrix.screenshot.sources).toContain(TERMINAL_SCREENSHOT.capture);
|
||||
// Every locale describes the actual capture; build identity comes from
|
||||
// the media manifest instead of a stale version embedded in translations.
|
||||
expect(homePage).toContain("label={fill(d.screenshotAlt, { version: TERMINAL_SCREENSHOT.version })}");
|
||||
expect(guidePage).toContain("label={fill(home.screenshotAlt, { version: TERMINAL_SCREENSHOT.version })}");
|
||||
for (const locale of ["en", "zh", "ja", "vi", "ko", "ru", "uk", "es", "pt-BR", "id", "fr", "de", "ca", "hi", "tr", "it", "pl", "ar"]) {
|
||||
const home = getHome(locale);
|
||||
expect(home.screenshotAlt, `${locale} alt`).toContain("{version}");
|
||||
|
||||
@@ -10,9 +10,7 @@ import { footerDocsLinks, footerLegalLinks, footerProductLinks, footerProjectLin
|
||||
import { PERMANENT_ROUTE_REDIRECTS, REDIRECTED_ROUTES } from "./route-redirects";
|
||||
import { AVAILABILITY_ROWS, describeRow } from "./content/availability";
|
||||
import { DESKTOP_RELEASE, desktopDownload, parseDesktopRelease, type DesktopReleaseFact } from "./desktop-release";
|
||||
import { daysLeft, isActiveAt } from "./expiry";
|
||||
import { FOUNDER_SWITCHES, FOUNDER_SWITCH_VALUES } from "./founder-switches";
|
||||
import { PARTNER_PROMO } from "./content/partner-promo";
|
||||
import { STATE_CHROME_KEY, SURFACE_IDS, SURFACE_STATES, stateLabel, surfaceCapabilities, surfaceState } from "./surface-states";
|
||||
import { DesktopDownloadBlock } from "../components/desktop-download";
|
||||
import HomePage from "../app/[locale]/page";
|
||||
@@ -324,40 +322,17 @@ describe("no page links to a missing or gone route", () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe("things with an end time are checked when the page renders", () => {
|
||||
it("compares against the clock it is given", () => {
|
||||
expect(isActiveAt("2026-11-21T12:29:57Z", Date.parse("2026-11-21T12:29:56Z"))).toBe(true);
|
||||
expect(isActiveAt("2026-11-21T12:29:57Z", Date.parse("2026-11-21T12:29:57Z"))).toBe(false);
|
||||
expect(isActiveAt("not a date", 0)).toBe(false);
|
||||
expect(daysLeft("2026-11-21T12:29:57Z", Date.parse("2026-11-21T12:00:00Z"))).toBe(1);
|
||||
expect(daysLeft("2026-11-21T12:29:57Z", Date.parse("2026-11-19T12:00:00Z"))).toBe(3);
|
||||
});
|
||||
|
||||
it("removes the partner strip from the home page after its end time without a rebuild", async () => {
|
||||
const end = Date.parse(PARTNER_PROMO.endsAt);
|
||||
vi.useFakeTimers({ toFake: ["Date"], now: end - 3_600_000 });
|
||||
expect(await render(HomePage as Page)).toContain("OrcaRouter");
|
||||
vi.setSystemTime(end + 1000);
|
||||
expect(await render(HomePage as Page)).not.toMatch(/OrcaRouter|orcarouter\.ai/);
|
||||
});
|
||||
|
||||
it("keeps the partner strip English-only", async () => {
|
||||
vi.useFakeTimers({ toFake: ["Date"], now: Date.parse(PARTNER_PROMO.endsAt) - 3_600_000 });
|
||||
expect(await render(HomePage as Page, "zh")).not.toMatch(/orcarouter\.ai/);
|
||||
});
|
||||
});
|
||||
|
||||
describe("numbers from the build-time snapshot carry the date they were read", () => {
|
||||
it.each(LOCALES)("on the home page in %s", async (locale) => {
|
||||
const snapshot = JSON.parse(readFileSync(join(WEB, "data/repo-stats-snapshot.json"), "utf8")) as { readAt: string };
|
||||
const date = new Intl.DateTimeFormat(getChrome(locale).dateLocale, { dateStyle: "medium", timeZone: "UTC" }).format(new Date(snapshot.readAt));
|
||||
const label = getChrome(locale).numbersAsOf.replace("{date}", date);
|
||||
const html = await render(HomePage as Page, locale);
|
||||
expect(html).toContain(`title="${label}"`);
|
||||
expect(html).toContain(label);
|
||||
const numbers = [...html.matchAll(/<a [^>]*(?:class="tabular"|home-proof-stars)[^>]*>/g)].map((match) => match[0]).filter((tag) => !tag.includes("CONTRIBUTORS.md"));
|
||||
expect(numbers.length).toBeGreaterThan(0);
|
||||
for (const tag of numbers) expect(tag).toContain(`title="${label}"`);
|
||||
const counts = [...html.matchAll(/<p class="home-accounts tabular"[^>]*>/g)].map((match) => match[0]);
|
||||
expect(counts).toHaveLength(1);
|
||||
expect(counts[0]).toContain(`title="${label}"`);
|
||||
expect(html).toContain(`<span class="sr-only"> ${label}</span>`);
|
||||
expect(html.replace(/<span class="sr-only">[^<]*<\/span>/g, "").replace(/title="[^"]*"/g, "")).not.toContain(label);
|
||||
});
|
||||
|
||||
it("on /contribute", async () => {
|
||||
|
||||
@@ -1,96 +0,0 @@
|
||||
import lights from "../vendor/whale-lights/whale-lights.json";
|
||||
|
||||
type Lights = {
|
||||
center: [number, number];
|
||||
beadRadius: number;
|
||||
mark: { scale: number; offset: [number, number] };
|
||||
spine: { head: number; sweep: number };
|
||||
drift: { spread: number; swim: number; swimPeriod: number; swimWaves: number; hover: number; hoverRate: [number, number]; bob: number; bobRatio: number };
|
||||
rest: { breath: [number, number]; wave: [number, number]; glow: [number, number]; crestWidth: number; crestWander: number; crestLift: number; stars: number };
|
||||
beads: [number, number][];
|
||||
};
|
||||
|
||||
const L = lights as unknown as Lights;
|
||||
const TAU = Math.PI * 2;
|
||||
const [CX, CY] = L.center;
|
||||
export const IGNITE = 0.14;
|
||||
export const BEAD_RADIUS = L.beadRadius * L.mark.scale;
|
||||
|
||||
function hash(text: string): number {
|
||||
let h = 0x811c9dc5;
|
||||
for (let i = 0; i < text.length; i += 1) { h ^= text.charCodeAt(i); h = Math.imul(h, 0x01000193); }
|
||||
return h >>> 0;
|
||||
}
|
||||
|
||||
function prng(seed: number): () => number {
|
||||
let a = seed;
|
||||
return () => {
|
||||
a = (a + 0x6d2b79f5) | 0;
|
||||
let t = Math.imul(a ^ (a >>> 15), 1 | a);
|
||||
t = (t + Math.imul(t ^ (t >>> 7), 61 | t)) ^ t;
|
||||
return ((t ^ (t >>> 14)) >>> 0) / 4294967296;
|
||||
};
|
||||
}
|
||||
|
||||
function spineOf([x, y]: [number, number]): number {
|
||||
const angle = (Math.atan2(y - CY, x - CX) * 180 / Math.PI + 360) % 360;
|
||||
return Math.max(0, Math.min(1, ((angle - L.spine.head + 360) % 360) / L.spine.sweep));
|
||||
}
|
||||
|
||||
export const BEADS = L.beads.map((point) => ({
|
||||
home: [(point[0] - CX) * L.mark.scale + L.mark.offset[0], (point[1] - CY) * L.mark.scale + L.mark.offset[1]] as [number, number],
|
||||
point,
|
||||
spine: spineOf(point),
|
||||
}));
|
||||
|
||||
const HOVER = BEADS.map((_, index) => {
|
||||
const random = prng(hash(`bead:${index}`));
|
||||
const [base, span] = L.drift.hoverRate;
|
||||
return [base + random() * span, base + random() * span, random() * TAU, random() * TAU] as const;
|
||||
});
|
||||
|
||||
const HOME = (() => {
|
||||
const random = prng(hash("codewhale"));
|
||||
const stars = new Set<number>();
|
||||
for (let bin = 0; bin < L.rest.stars; bin += 1) {
|
||||
const low = bin / L.rest.stars;
|
||||
const high = (bin + 1) / L.rest.stars + (bin === L.rest.stars - 1 ? 1e-6 : 0);
|
||||
const choices = BEADS.flatMap((bead, index) => (bead.spine >= low && bead.spine < high ? [index] : []));
|
||||
const pick = choices[Math.floor(random() * choices.length)];
|
||||
if (pick !== undefined) stars.add(pick);
|
||||
}
|
||||
for (let skip = 0; skip < 9; skip += 1) random();
|
||||
return {
|
||||
stars,
|
||||
breath: L.rest.breath[0] + random() * L.rest.breath[1],
|
||||
wave: L.rest.wave[0] + random() * L.rest.wave[1],
|
||||
phase: random(),
|
||||
};
|
||||
})();
|
||||
|
||||
export function isStar(index: number): boolean {
|
||||
return HOME.stars.has(index);
|
||||
}
|
||||
|
||||
export function lightsAt(time: number) {
|
||||
const glow = L.rest.glow[0] + L.rest.glow[1] * Math.sin((TAU * time) / HOME.breath);
|
||||
const crest = ((time / HOME.wave + HOME.phase + L.rest.crestWander * Math.sin((TAU * time) / (HOME.wave * L.drift.bobRatio))) % 1) * 1.4 - 0.2;
|
||||
return { glow, crest };
|
||||
}
|
||||
|
||||
export function beadShine(index: number, crest: number): number {
|
||||
return Math.max(0, 1 - Math.abs(BEADS[index]!.spine - crest) / L.rest.crestWidth) ** 2;
|
||||
}
|
||||
|
||||
export function beadOffset(index: number, time: number, shine: number): [number, number] {
|
||||
const bead = BEADS[index]!;
|
||||
const [w1, w2, p1, p2] = HOVER[index]!;
|
||||
const { spread, swim, swimPeriod, swimWaves, hover, bob, bobRatio } = L.drift;
|
||||
const dx = bead.point[0] - CX, dy = bead.point[1] - CY, r = Math.hypot(dx, dy) || 1;
|
||||
const reach = spread * Math.sin((TAU * time) / HOME.breath);
|
||||
const wave = swim * Math.sin(TAU * (time / swimPeriod - bead.spine * swimWaves)) + shine * L.rest.crestLift;
|
||||
return [
|
||||
(dx * reach + (dx / r) * wave + hover * Math.sin(time * w1 + p1)) * L.mark.scale,
|
||||
(dy * reach + (dy / r) * wave + hover * Math.sin(time * w2 + p2) + bob * Math.sin((TAU * time) / (HOME.breath * bobRatio))) * L.mark.scale,
|
||||
];
|
||||
}
|
||||
@@ -7,7 +7,6 @@ copy-freshness: web/lib/content/tools.ts
|
||||
copy-freshness: web/lib/content/reference-guides.ts
|
||||
copy-freshness: web/lib/content/runtime-integrations.ts
|
||||
copy-freshness: web/lib/content/product.ts
|
||||
copy-freshness: web/lib/content/home-overview.ts
|
||||
copy-freshness: web/lib/content/onboarding-check.ts
|
||||
copy-freshness: web/lib/i18n/dictionaries/en/computer-use.ts
|
||||
copy-freshness: web/lib/i18n/dictionaries/en/features.ts
|
||||
@@ -24,7 +23,6 @@ link-health: web/lib/content/tools.ts
|
||||
link-health: web/lib/content/reference-guides.ts
|
||||
link-health: web/lib/content/runtime-integrations.ts
|
||||
link-health: web/lib/content/product.ts
|
||||
link-health: web/lib/content/home-overview.ts
|
||||
link-health: web/lib/content/onboarding-check.ts
|
||||
link-health: web/lib/i18n/dictionaries/en/computer-use.ts
|
||||
link-health: web/lib/i18n/dictionaries/en/features.ts
|
||||
|
||||
Vendored
+18
@@ -0,0 +1,18 @@
|
||||
# Whale B (vendored)
|
||||
|
||||
`whale-b.json` holds the view box, the body gradient's axis and the eight
|
||||
paths of `codewhale-design/whale/svg/whale-b.svg`, with the path data
|
||||
unchanged. The source file's SHA-256 is
|
||||
`75bbb9ef40b0bcb3ae2b27dda3d63923fa65a47d203c53ec83bc8ce9fb16a6f1`.
|
||||
`whale-b-dark.svg`
|
||||
(`66677a183f32f3a6a8e1884c95ea68c78e2207e2cdf4a2447fbb1b29a08427dd`) has the
|
||||
same geometry and different fills.
|
||||
|
||||
The fills are set in `web/app/styles/home.css` from those two files: the body
|
||||
gradient `#1e8fd8` to `#0b48bb`, the throat `#dcebf7` and the pleats `#0b48bb`
|
||||
at 0.78 on the light page; `#3594d8` to `#2b70d5`, `#dcecf8` and `#2b70d5` on
|
||||
the dark page; the eye `#0b1424` on both.
|
||||
|
||||
Re-copy the geometry from its owning source; do not edit it here. The home
|
||||
page draws this whale once, as a character. It is decorative and is never the
|
||||
logo.
|
||||
Vendored
+1
@@ -0,0 +1 @@
|
||||
{"viewBox":"-87.192 -31.606 154.466 47.543","gradient":{"x1":-87.192,"y1":-31.606,"x2":67.274,"y2":15.937},"paths":[{"id":"flukes","d":"M-59.236 -15.009 C-59.767 -15.979 -60.169 -16.719 -60.56 -17.513 C-60.63 -17.655 -60.846 -18.071 -60.876 -18.126 C-60.887 -18.147 -60.899 -18.169 -60.903 -18.177 C-60.939 -18.24 -61.163 -18.639 -61.413 -18.985 C-61.758 -19.464 -62.189 -19.963 -62.371 -20.17 C-62.388 -20.19 -62.436 -20.244 -62.477 -20.29 C-62.566 -20.389 -62.889 -20.743 -63.018 -20.88 C-63.177 -21.05 -63.265 -21.145 -63.584 -21.47 C-64.373 -22.275 -65.528 -23.327 -66.507 -23.986 C-66.665 -24.093 -66.954 -24.279 -67.031 -24.328 C-67.076 -24.356 -67.168 -24.414 -67.236 -24.456 C-67.799 -24.805 -68.712 -25.285 -69.339 -25.606 C-69.188 -25.295 -69.172 -25.288 -69.081 -25.091 C-69.062 -25.049 -68.996 -24.902 -68.891 -24.61 C-68.77 -24.278 -68.644 -23.958 -68.522 -23.442 C-68.482 -23.273 -68.394 -22.648 -68.378 -22.411 C-68.37 -22.299 -68.363 -22.156 -68.359 -22.022 C-68.357 -21.973 -68.355 -21.876 -68.353 -21.806 C-68.352 -21.737 -68.351 -21.651 -68.351 -21.617 C-68.351 -21.583 -68.351 -21.53 -68.352 -21.496 C-68.352 -21.464 -68.353 -21.431 -68.353 -21.422 C-68.354 -21.412 -68.357 -21.319 -68.363 -21.212 C-68.397 -20.661 -68.454 -20.066 -68.5 -19.547 C-68.524 -19.271 -68.515 -18.658 -68.494 -18.544 C-68.49 -18.516 -68.478 -18.455 -68.467 -18.409 C-68.457 -18.364 -68.425 -18.235 -68.393 -18.127 C-68.323 -17.889 -68.178 -17.506 -68.112 -17.278 C-70.837 -18.144 -71.129 -18.03 -71.27 -17.972 C-71.731 -17.785 -71.639 -17.839 -72.165 -17.577 C-73.041 -17.142 -74.42 -15.83 -75.097 -15.238 C-76.952 -13.616 -79.18 -12.668 -81.192 -12.031 C-79.716 -11.626 -79.752 -11.617 -78.481 -11.377 C-75.03 -10.725 -71.467 -10.712 -67.747 -11.124 C-65.508 -11.372 -62.166 -12.423 -60.228 -12.967 C-59.892 -13.659 -59.563 -14.337 -59.236 -15.009 Z"},{"id":"dorsal","d":"M-31.037 -13.452 C-30.982 -13.453 -30.817 -13.457 -30.708 -13.46 C-30.598 -13.462 -30.488 -13.465 -30.378 -13.469 C-30.269 -13.472 -30.159 -13.475 -30.049 -13.479 C-29.939 -13.482 -29.829 -13.485 -29.72 -13.489 C-29.61 -13.493 -29.5 -13.497 -29.39 -13.501 C-29.281 -13.505 -29.171 -13.509 -29.061 -13.513 C-28.951 -13.518 -28.842 -13.522 -28.732 -13.527 C-28.622 -13.531 -28.513 -13.536 -28.403 -13.541 C-28.293 -13.546 -28.183 -13.551 -28.074 -13.556 C-27.964 -13.561 -27.854 -13.566 -27.744 -13.571 C-27.635 -13.576 -27.525 -13.582 -27.415 -13.587 C-27.306 -13.593 -27.196 -13.599 -27.086 -13.604 C-26.976 -13.61 -26.867 -13.616 -26.757 -13.622 C-26.647 -13.628 -26.538 -13.633 -26.428 -13.64 C-26.318 -13.646 -26.209 -13.652 -26.099 -13.658 C-25.989 -13.665 -25.88 -13.671 -25.77 -13.678 C-25.66 -13.684 -25.551 -13.691 -25.441 -13.697 C-25.331 -13.704 -25.221 -13.711 -25.112 -13.718 C-25.002 -13.726 -24.892 -13.733 -24.783 -13.74 C-24.673 -13.748 -24.563 -13.755 -24.453 -13.763 C-24.344 -13.77 -24.234 -13.778 -24.124 -13.786 C-24.014 -13.794 -23.905 -13.802 -23.795 -13.81 C-23.685 -13.818 -23.575 -13.827 -23.466 -13.835 C-23.356 -13.843 -22.622 -13.598 -23.136 -13.861 C-23.651 -14.124 -25.051 -14.866 -26.555 -15.413 C-28.058 -15.961 -31.49 -17.43 -32.155 -17.144 C-32.82 -16.857 -30.727 -14.35 -30.546 -13.694 C-30.364 -13.037 -30.98 -13.287 -31.067 -13.205 Z"},{"id":"body","d":"M-63.431 -12.829 C-62.681 -12.33 -62.298 -12.079 -61.933 -11.845 C-61.465 -11.545 -60.835 -11.155 -60.784 -11.124 C-60.764 -11.112 -60.667 -11.052 -60.567 -10.991 C-60.216 -10.776 -58.683 -9.868 -58.245 -9.618 C-58.088 -9.528 -57.833 -9.383 -57.684 -9.299 C-57.102 -8.971 -56.269 -8.51 -55.375 -8.03 C-55.097 -7.881 -53.879 -7.239 -53.802 -7.199 C-53.784 -7.189 -53.627 -7.107 -53.454 -7.018 C-53.281 -6.928 -52.934 -6.749 -52.682 -6.62 C-52.432 -6.492 -52.138 -6.343 -52.034 -6.29 C-49.747 -5.136 -46.759 -3.754 -43.905 -2.664 C-43.623 -2.556 -43.003 -2.325 -42.904 -2.288 C-42.836 -2.263 -42.701 -2.213 -42.6 -2.176 C-42.444 -2.12 -42.102 -1.993 -41.352 -1.731 C-40.976 -1.6 -40.763 -1.524 -40.053 -1.288 C-39.649 -1.154 -39.28 -1.034 -39.232 -1.019 C-39.115 -0.981 -37.866 -0.586 -37.582 -0.5 C-37.193 -0.381 -37.163 -0.372 -36.829 -0.273 C-36.206 -0.087 -35.999 -0.028 -35.574 0.093 C-35.419 0.138 -35.207 0.198 -35.11 0.225 C-35.014 0.252 -34.843 0.3 -34.737 0.33 C-34.626 0.36 -34.443 0.411 -34.327 0.443 C-33.953 0.546 -33.163 0.756 -32.946 0.813 C-32.872 0.832 -32.741 0.867 -32.651 0.89 C-32.493 0.931 -32.219 1.003 -31.569 1.165 C-30.848 1.345 -29.974 1.553 -29.41 1.684 C-29.232 1.725 -29.065 1.763 -29.04 1.769 C-28.91 1.799 -27.218 2.173 -26.485 2.325 C-24.335 2.772 -21.755 3.234 -20.189 3.493 C-20.076 3.511 -19.962 3.53 -19.934 3.535 C-19.907 3.539 -19.778 3.56 -19.645 3.582 C-19.513 3.603 -19.305 3.637 -19.18 3.657 C-18.577 3.754 -17.763 3.884 -16.686 4.045 C-16.603 4.057 -16.508 4.071 -16.469 4.077 C-16.431 4.083 -16.286 4.104 -16.149 4.124 C-16.011 4.144 -15.854 4.167 -15.795 4.176 C-15.741 4.184 -15.527 4.214 -15.322 4.244 C-14.935 4.299 -13.461 4.5 -13.192 4.535 C-13.095 4.548 -12.423 4.634 -12.348 4.643 C-12Line truncated
|
||||
-18
@@ -1,18 +0,0 @@
|
||||
# Whale lights (vendored)
|
||||
|
||||
`whale-drones.json` is an unchanged copy of
|
||||
`codewhale-design/whale/out/whale-drones.json`, generated from source hash
|
||||
`c281468862e8bdc5bc894dcafa5ff5d1997152abf177e4a0426ffec3ab62576c`.
|
||||
The hero uses its generated launch lattices, constant radius and minimum
|
||||
physical-pixel pitch. Motion follows `codewhale-design/whale/MOTION.md`.
|
||||
|
||||
The older `whale-lights.json` remains the unchanged 64-bead reference from
|
||||
`codewhale-app/assets/whale-lights.json` at
|
||||
`55c9ba0c95368accca78ca36d9b86a3fd7ce4253`, used by `web/lib/whale-lights.ts`.
|
||||
Its SHA-256 is
|
||||
`529b8d0f70113c8aa1153e28e596c263b1df69cbc04e268b15d04da8445e3e15`.
|
||||
|
||||
Re-copy generated assets from their owning source; do not edit their geometry
|
||||
here. The hero is an illustration of the ready formation, not an indicator of
|
||||
an observed session. CPU usage needs measurement on the reference Mac before
|
||||
claiming compliance with the kit's 5% budget.
|
||||
-1
@@ -1 +0,0 @@
|
||||
{"schema":1,"kind":"drones","source":"codewhale-design/whale/source sha256 c281468862e8bdc5bc894dcafa5ff5d1997152abf177e4a0426ffec3ab62576c","note":"Generated by codewhale-design/whale/generate.mjs. Do not edit.","encoding":"Each density lists rows of lattice cells: \".\" empty, \"b\" body, \"t\" throat, \"p\" pleat, \"e\" the eye (no drone). A cell at column i, row j sits at origin + (i + 0.5, j + 0.5) * pitch, in mark units. Drones are numbered in row-major order, skipping \".\" and \"e\". Pick the densest entry whose drones stay at least minPitchPx device pixels apart.","radius":0.32,"minPitchPx":3,"launch":[{"cols":16,"pitch":5.8848,"origin":[-46.875,-44.082],"count":119,"rows":["......bb........","...bbbbbb.......","..bbbbbbbbb.....",".bbb....bbb.....",".bb.............","bb..............","bb..........bbbb","bb......bbbbbbbb","bbb...bbbbbbbttb","bbbbbbbbbbbbtttb",".bbbbbbbbebtttt.",".bbbbbbbbbtttt..","..bbbbbbbtttt...",".....bbbtttb....","......bbb......."]},{"cols":20,"pitch":4.7078,"origin":[-46.875,-42.316],"count":185,"rows":["......bbbbb.........","....bbbbbbb.........","...bbbbbbbbb........","..bbbb....bbbb......",".bbb.......bb.......",".bbb................","bbb.................","bbb.............bbb.","bbb.........bbbbbbbb","bbb......bbbbbbbbbbb","bbbbb.bbbbbbbbbbtttb","bbbbbbbbbbbbbbbtttt.",".bbbbbbbbbbebbttttb.",".bbbbbbbbbbbbttttt..","..bbbbbbbbbbttttt...","...bbbbbbbbttttb....",".......bbbttttb.....",".......bbbbbb......."]},{"cols":24,"pitch":3.9232,"origin":[-46.875,-43.101],"count":265,"rows":["........bbbb............","......bbbbbbb...........","....bbbbbbbb............","...bbbbbbbbbbbbb........","..bbbbb.....bbbbb.......",".bbbb........bbb........",".bbbb...................",".bbb....................","bbbb....................","bbb...............bbbbb.","bbb...........bbbbbbbbbb","bbbb.......bbbbbbbbbbbbb","bbbbb....bbbbbbbbbbbtttb","bbbbbbbbbbbbbbbbbbttttt.",".bbbbbbbbbbbbbbbbtttttb.",".bbbbbbbbbbbbebbttttttb.","..bbbbbbbbbbbbbttttttb..","..bbbbbbbbbbbbbtttttb...","...bbbbbbbbbbbtttttb....",".........bbbbttttbb.....",".......bbbbbtttbb.......",".........bbbbb.........."]},{"cols":28,"pitch":3.3627,"origin":[-46.875,-43.661],"count":357,"rows":["............b...............",".......bbbbbbbbb............","......bbbbbbbbb.............","....bbbbbbbbbb.b............","...bbbbbbbbbbbbbbbb.........","..bbbbbb......bbbbbb........","..bbbb.........bbb..........",".bbbb.......................",".bbbb.......................","bbbb........................","bbbb........................","bbbb................bbbbbbbb","bbbb............bbbbbbbbbbbb","bbbb.........bbbbbbbbbbbbbbb","bbbbb......bbbbbbbbbbbbbtttb","bbbbbbbbbbbbbbbbbbbbbbtttttb",".bbbbbbbbbbbbbbbbbbbbtttttb.",".bbbbbbbbbbbbbbbebbbttttttb.",".bbbbbbbbbbbbbbbbbbttttttb..","..bbbbbbbbbbbbbbbbttttttb...","...bbbbbbbbbbbbbbtttttttb...","....bbbbbbb.bbbbttttttbb....",".....bbbb..bbbbbtttttb......",".........bbbbbbttttbb.......","........bbbbbbttbbb.........",".............b.............."]},{"cols":34,"pitch":2.7693,"origin":[-46.875,-42.87],"count":532,"rows":["............bbbbb.................",".........bbbbbbbbbb...............",".......bbbbbbbbbbbb...............","......bbbbbbbbbbb.................",".....bbbbbbbbbbbbbbbb.............","....bbbbbbbb...bbbbbbbbb..........","...bbbbbb........bbbbbbb..........","..bbbbbb..........bbbbb...........","..bbbbb...........................",".bbbbb............................",".bbbbb............................",".bbbb.............................","bbbbb.............................","bbbbb...................bbbbbbbbb.","bbbbb................bbbbbbbbbbbbb","bbbbb.............bbbbbbbbbbbbbbbb","bbbbbb.........bbbbbbbbbbbbbbbbttb","bbbbbbb......bbbbbbbbbbbbbbbtttttb","bbbbbbbbbbbbbbbbbbbbbbbbbbbtttttb.",".bbbbbbbbbbbbbbbbbbbbbbbbtttttttb.",".bbbbbbbbbbbbbbbbbbbbbbbttttttttb.",".bbbbbbbbbbbbbbbbbbebbbttttttttb..","..bbbbbbbbbbbbbbbbbbbbttttttttb...","..bbbbbbbbbbbbbbbbbbbbttttttttb...","...bbbbbbbbbbbbbbbbbbttttttttb....","....bbbbbbbbbbbbbbbbttttttttb.....",".....bbbbbbbb.bbbbbtttttttbb......",".............bbbbbbttttttbb.......","..........bbbbbbbtttttbbb.........","..........bbbbbbtttbbbb...........",".............bbbbbb..............."]},{"cols":40,"pitch":2.3539,"origin":[-46.875,-42.316],"count":735,"rows":["..............bbbbbbb...................","...........bbbbbbbbbbbb.................",".........bbbbbbbbbbbbb..................",".......bbbbbbbbbbbbbb...................","......bbbbbbbbbbbbbb.bb.................",".....bbbbbbbbbbbbbbbbbbbbb..............","....bbbbbbbbb......bbbbbbbbbb...........","...bbbbbbbb.........bbbbbbbb............","...bbbbbb............bbbbbb.............","..bbbbbb................................","..bbbbbb................................",".bbbbbb.................................",".bbbbb..................................",".bbbbb..................................","bbbbbb..................................","bbbbbb.......................bbbbbbbbbb."Line truncated
|
||||
-99
@@ -1,99 +0,0 @@
|
||||
{
|
||||
"schema": 1,
|
||||
"space": [40, 38],
|
||||
"center": [20, 19],
|
||||
"beadRadius": 1.08,
|
||||
"mark": {
|
||||
"scale": 2.64,
|
||||
"offset": [0.3, 0.6]
|
||||
},
|
||||
"spine": {
|
||||
"head": 340,
|
||||
"sweep": 335
|
||||
},
|
||||
"drift": {
|
||||
"spread": 0.025,
|
||||
"swim": 0.45,
|
||||
"swimPeriod": 3.2,
|
||||
"swimWaves": 1.2,
|
||||
"hover": 0.22,
|
||||
"hoverRate": [0.7, 0.8],
|
||||
"bob": 0.3,
|
||||
"bobRatio": 1.618
|
||||
},
|
||||
"rest": {
|
||||
"breath": [3.6, 1.2],
|
||||
"wave": [5.2, 1.6],
|
||||
"glow": [0.87, 0.13],
|
||||
"crestWidth": 0.18,
|
||||
"crestWander": 0.06,
|
||||
"crestLift": 0.3,
|
||||
"stars": 5
|
||||
},
|
||||
"beads": [
|
||||
[15.8, 3.4],
|
||||
[24, 34],
|
||||
[3, 23.1],
|
||||
[37, 17],
|
||||
[21.7, 18.7],
|
||||
[4.9, 10.5],
|
||||
[11.7, 32.8],
|
||||
[27.6, 8.7],
|
||||
[33.6, 27.8],
|
||||
[12.9, 22.8],
|
||||
[19.9, 27],
|
||||
[29.6, 20.5],
|
||||
[19.9, 9.9],
|
||||
[7.4, 17.3],
|
||||
[12.1, 9.6],
|
||||
[6.3, 29],
|
||||
[27, 28.1],
|
||||
[17.7, 34.3],
|
||||
[22, 4],
|
||||
[36.4, 22.8],
|
||||
[14.6, 28.1],
|
||||
[24.3, 23.4],
|
||||
[17.9, 22.3],
|
||||
[8, 24.3],
|
||||
[2.6, 18.1],
|
||||
[8.3, 6.7],
|
||||
[32, 16.4],
|
||||
[26.2, 17.3],
|
||||
[8.8, 12.8],
|
||||
[30.4, 24.9],
|
||||
[30.2, 31.1],
|
||||
[24, 11.1],
|
||||
[10.4, 27.8],
|
||||
[16.3, 7.9],
|
||||
[20.8, 31.1],
|
||||
[12, 4.6],
|
||||
[3.6, 14.3],
|
||||
[33.7, 19.9],
|
||||
[6, 20.8],
|
||||
[23.3, 28.4],
|
||||
[22.9, 7.6],
|
||||
[17.1, 30.8],
|
||||
[26.8, 31.9],
|
||||
[18.8, 5.2],
|
||||
[9.8, 21.4],
|
||||
[21, 23.7],
|
||||
[15.7, 24.9],
|
||||
[4.5, 26.1],
|
||||
[24.8, 20.2],
|
||||
[27.7, 23.1],
|
||||
[8.9, 30.8],
|
||||
[33.3, 23.7],
|
||||
[14.8, 33.1],
|
||||
[21, 34.3],
|
||||
[8.9, 9.6],
|
||||
[37.3, 19.9],
|
||||
[12.6, 25.8],
|
||||
[30.4, 27.8],
|
||||
[29.2, 17.3],
|
||||
[15.2, 21.1],
|
||||
[6.4, 14.6],
|
||||
[12.3, 29.9],
|
||||
[13.5, 7],
|
||||
[25.1, 26.1]
|
||||
]
|
||||
}
|
||||
Reference in new issue
Block a user