Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions src/components/CopyCommand.astro
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
---
// A shell command with a copy button: the command scrolls, the button stays.
// A shell command with a copy button. A long command wraps rather than
// scrolling out of sight, so the whole of it can be read before it is run.
// Used for the twcore install command on the Core page and on the home page.
import { getLang, t } from "~/i18n";

Expand All @@ -17,9 +18,8 @@ const ui = t(getLang(Astro)).common;
class="flex min-w-0 items-center rounded-[14px] border border-[var(--color-border-strong)] bg-[var(--color-surface)] font-mono leading-relaxed"
data-copy-command
>
<code class:list={["min-w-0 flex-1 overflow-x-auto whitespace-nowrap p-5 sm:p-6", className]}>
<span class="select-none text-[var(--color-dim)]">$ </span><span data-cmd>{command}</span>
</code>
{/* One line: the command wraps with white-space: pre-wrap, which would keep indentation as a leading space */}
<code class:list={["min-w-0 flex-1 whitespace-pre-wrap break-all p-5 sm:p-6", className]}><span class="select-none text-[var(--color-dim)]">$ </span><span data-cmd>{command}</span></code>
<button type="button" class="copy-button" data-copy={command} data-copied-label={ui.copied}>{ui.copy}</button>
</div>

Expand Down
33 changes: 17 additions & 16 deletions src/components/mocks/LogExplorerMock.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ export default function LogExplorerMock() {
}, [inView]);

return (
<div ref={ref} className="rounded-2xl border border-white/10 bg-[var(--color-surface)]/80 overflow-hidden backdrop-blur-sm h-full flex flex-col">
<div ref={ref} className="@container rounded-2xl border border-white/10 bg-[var(--color-surface)]/80 overflow-hidden backdrop-blur-sm h-full flex flex-col">
{/* Top bar */}
<div className="flex items-center gap-2 sm:gap-3 px-3 sm:px-4 py-3 border-b border-white/10 bg-black/40">
<div className="hidden sm:flex gap-1.5 shrink-0">
Expand All @@ -111,17 +111,18 @@ export default function LogExplorerMock() {
</div>
</div>

{/* Column headers */}
{/* Column headers. Columns follow the panel's own width, not the window's: in the
hero it sits in half the page, where seven columns would all be cut short. */}
<div
className="grid gap-3 px-4 py-2 text-[10px] uppercase tracking-wider text-[var(--color-dim)] border-b border-white/5
grid-cols-[1fr_auto_56px] sm:grid-cols-[60px_1fr_1fr_56px_60px] lg:grid-cols-[70px_1fr_110px_1fr_70px_50px_56px]"
grid-cols-[1fr_auto_56px] @md:grid-cols-[60px_1fr_1fr_56px_60px] @2xl:grid-cols-[70px_1fr_110px_1fr_70px_50px_56px]"
>
<div className="hidden sm:block">time</div>
<div className="hidden @md:block">time</div>
<div>user</div>
<div className="hidden lg:block">key</div>
<div className="hidden sm:block">model</div>
<div className="hidden sm:block text-right">tokens</div>
<div className="hidden lg:block text-right">ms</div>
<div className="hidden @2xl:block">key</div>
<div className="hidden @md:block">model</div>
<div className="hidden @md:block text-right">tokens</div>
<div className="hidden @2xl:block text-right">ms</div>
<div className="text-right">status</div>
</div>

Expand All @@ -131,20 +132,20 @@ export default function LogExplorerMock() {
<li
key={r.id}
className="grid gap-3 px-4 py-2.5 border-b border-white/5 hover:bg-white/[0.02] animate-[slideIn_400ms_ease-out]
grid-cols-[1fr_auto_56px] sm:grid-cols-[60px_1fr_1fr_56px_60px] lg:grid-cols-[70px_1fr_110px_1fr_70px_50px_56px]"
grid-cols-[1fr_auto_56px] @md:grid-cols-[60px_1fr_1fr_56px_60px] @2xl:grid-cols-[70px_1fr_110px_1fr_70px_50px_56px]"
style={{ opacity: 1 - i * 0.04 }}
>
<div className="hidden sm:block text-[var(--color-dim)]">{r.ts}</div>
<div className="hidden @md:block text-[var(--color-dim)]">{r.ts}</div>
<div className="text-white truncate">{r.user}</div>
<div className="hidden lg:block text-[var(--color-brand-1)] truncate">{r.key}</div>
<div className="hidden sm:block text-[var(--color-brand-2)] truncate">{r.model}</div>
<div className="hidden sm:block text-right text-[var(--color-text)] tabular-nums">
<div className="hidden @2xl:block text-[var(--color-brand-1)] truncate">{r.key}</div>
<div className="hidden @md:block text-[var(--color-brand-2)] truncate">{r.model}</div>
<div className="hidden @md:block text-right text-[var(--color-text)] tabular-nums">
{r.tokens.toLocaleString()}
</div>
<div className="hidden lg:block text-right text-[var(--color-muted)] tabular-nums">{r.ms}</div>
<div className="hidden @2xl:block text-right text-[var(--color-muted)] tabular-nums">{r.ms}</div>

{/* Compact: visible only at < sm — collapses model + tokens onto user line */}
<div className="sm:hidden text-[10px] text-[var(--color-dim)] truncate">
{/* Compact: visible only when the panel is narrower than 28rem — collapses model + tokens onto user line */}
<div className="@md:hidden text-[10px] text-[var(--color-dim)] truncate">
{r.model} · {r.tokens}t
</div>

Expand Down
4 changes: 2 additions & 2 deletions src/components/pages/CorePage.astro
Original file line number Diff line number Diff line change
Expand Up @@ -185,7 +185,7 @@ const fileLink = "break-all text-[var(--color-brand-1)] transition-colors hover:
</div>
<div class="flex min-w-0 flex-col gap-2 text-sm leading-relaxed text-[var(--color-muted)]">
<p class="m-0">{c.install.serviceUser}</p>
<code class="block overflow-x-auto whitespace-nowrap font-mono text-[13px] text-[#cbd5e1]">{asServiceUser}</code>
<code class="block break-all font-mono text-[13px] text-[#cbd5e1]">{asServiceUser}</code>
</div>
</div>
</div>
Expand Down Expand Up @@ -218,7 +218,7 @@ const fileLink = "break-all text-[var(--color-brand-1)] transition-colors hover:
<div class="overflow-x-auto rounded-[10px] border border-[var(--color-border)] bg-[var(--color-surface)] p-4 font-mono text-[13px] leading-normal sm:p-5 sm:text-sm">
<ul class="m-0 flex list-none flex-col gap-2 p-0">
{sourceCommands.map((cmd) => (
<li class="whitespace-nowrap text-[#e6ebf2]"><span class="select-none text-[var(--color-dim)]">$ </span>{cmd}</li>
<li class="break-all text-[#e6ebf2]"><span class="select-none text-[var(--color-dim)]">$ </span>{cmd}</li>
))}
</ul>
</div>
Expand Down
7 changes: 4 additions & 3 deletions src/components/sections/Features.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { useState } from "react";
import { spanClass } from "~/lib/grid";

export type FeatureModule = {
id: string;
Expand Down Expand Up @@ -42,16 +43,16 @@ export default function Features({ modules }: { modules: readonly FeatureModule[
</div>

<div className="mt-10 mb-8 max-w-2xl">
<p className="text-2xl md:text-3xl font-semibold tracking-tight text-gradient">
<p className="text-2xl md:text-3xl font-semibold tracking-tight text-gradient [text-wrap:balance]">
{current.tagline}
</p>
</div>

<div className="grid gap-5 md:grid-cols-2 lg:grid-cols-3">
<div className="grid gap-5 md:grid-cols-2 lg:grid-cols-6">
{current.bullets.map((b, i) => (
<div
key={b.title}
className="group rounded-xl border border-white/10 bg-[var(--color-surface)]/60 p-6 hover:border-white/25 transition-colors animate-[fadeUp_400ms_ease-out_both]"
className={`group rounded-xl border border-white/10 bg-[var(--color-surface)]/60 p-6 hover:border-white/25 transition-colors animate-[fadeUp_400ms_ease-out_both] ${spanClass(i, current.bullets.length)}`}
style={{ animationDelay: `${i * 60}ms` }}
>
<div className="flex items-center justify-between mb-4">
Expand Down
2 changes: 1 addition & 1 deletion src/components/sections/FeaturesSection.astro
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ const s = t(getLang(Astro)).features;
<div class="text-xs uppercase tracking-[0.18em] text-[var(--color-brand-1)] font-medium mb-4">
{s.eyebrow}
</div>
<h2 class="text-3xl sm:text-4xl md:text-5xl font-semibold tracking-tight">
<h2 class="text-3xl sm:text-4xl md:text-5xl font-semibold tracking-tight [text-wrap:balance]">
{s.title}<span class="text-gradient">{s.titleHighlight}</span>
</h2>
<p class="mt-4 sm:mt-5 text-base sm:text-lg text-[var(--color-muted)] leading-relaxed">
Expand Down
2 changes: 1 addition & 1 deletion src/components/sections/HowItWorks.astro
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ const providers = [
<div class="text-xs uppercase tracking-[0.18em] text-[var(--color-brand-1)] font-medium mb-4">
{s.eyebrow}
</div>
<h2 class="text-3xl sm:text-4xl md:text-5xl font-semibold tracking-tight">
<h2 class="text-3xl sm:text-4xl md:text-5xl font-semibold tracking-tight [text-wrap:balance]">
{s.title}<span class="text-gradient">{s.titleHighlight}</span>
</h2>
<p class="mt-4 sm:mt-5 text-base sm:text-lg text-[var(--color-muted)] leading-relaxed">
Expand Down
2 changes: 1 addition & 1 deletion src/components/sections/LivePanels.astro
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ const s = t(getLang(Astro)).live;
<div class="text-xs uppercase tracking-[0.18em] text-[var(--color-brand-1)] font-medium mb-4">
{s.eyebrow}
</div>
<h2 class="text-3xl sm:text-4xl md:text-5xl font-semibold tracking-tight">
<h2 class="text-3xl sm:text-4xl md:text-5xl font-semibold tracking-tight [text-wrap:balance]">
{s.title}<span class="text-gradient">{s.titleHighlight}</span>
</h2>
<p class="mt-4 sm:mt-5 text-base sm:text-lg text-[var(--color-muted)] leading-relaxed">
Expand Down
2 changes: 1 addition & 1 deletion src/components/sections/MCPAdvantage.astro
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ const treatmentClass: Record<Treatment, string> = {
<div class="text-xs uppercase tracking-[0.18em] text-[var(--color-brand-1)] font-medium mb-4">
{s.eyebrow}
</div>
<h2 class="text-3xl sm:text-4xl md:text-5xl font-semibold tracking-tight">
<h2 class="text-3xl sm:text-4xl md:text-5xl font-semibold tracking-tight [text-wrap:balance]">
{s.title}<span class="text-gradient">{s.titleHighlight}</span>
</h2>
<p class="mt-4 sm:mt-5 text-base sm:text-lg text-[var(--color-muted)] leading-relaxed">
Expand Down
7 changes: 4 additions & 3 deletions src/components/sections/Problem.astro
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
---
import TiltCard from "../ui/TiltCard.tsx";
import { getLang, t } from "~/i18n";
import { spanClass } from "~/lib/grid";

const s = t(getLang(Astro)).problem;

Expand All @@ -20,17 +21,17 @@ const icons: Record<string, string> = {
<div class="text-xs uppercase tracking-[0.18em] text-[var(--color-accent)] font-medium mb-4">
{s.eyebrow}
</div>
<h2 class="text-3xl sm:text-4xl md:text-5xl font-semibold tracking-tight">
<h2 class="text-3xl sm:text-4xl md:text-5xl font-semibold tracking-tight [text-wrap:balance]">
{s.title}<span class="text-gradient">{s.titleHighlight}</span>
</h2>
<p class="mt-4 sm:mt-5 text-base sm:text-lg text-[var(--color-muted)] leading-relaxed">
{s.sub}
</p>
</div>

<div class="mt-12 sm:mt-16 grid gap-4 sm:gap-5 sm:grid-cols-2 lg:grid-cols-3">
<div class="mt-12 sm:mt-16 grid gap-4 sm:gap-5 sm:grid-cols-2 lg:grid-cols-6">
{s.pains.map((p, i) => (
<div class="pain-item" style={`--pain-delay:${i * 80}ms`}>
<div class:list={["pain-item", spanClass(i, s.pains.length, "sm")]} style={`--pain-delay:${i * 80}ms`}>
<TiltCard client:visible className="rounded-2xl">
<div class="relative rounded-2xl border border-white/10 bg-[var(--color-surface)]/60 p-5 sm:p-6 hover:border-white/25 transition-colors overflow-hidden h-full">
<div class="flex items-start justify-between mb-4">
Expand Down
2 changes: 1 addition & 1 deletion src/components/sections/QuickStart.astro
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ const steps = s.steps.map((step, i) => ({
<div class="text-xs uppercase tracking-[0.18em] text-[var(--color-brand-1)] font-medium mb-4">
{s.eyebrow}
</div>
<h2 class="text-3xl sm:text-4xl md:text-5xl font-semibold tracking-tight">
<h2 class="text-3xl sm:text-4xl md:text-5xl font-semibold tracking-tight [text-wrap:balance]">
{s.title}<span class="text-gradient">{s.titleHighlight}</span>{s.titleEnd}
</h2>
<p class="mt-4 sm:mt-5 text-base sm:text-lg text-[var(--color-muted)] leading-relaxed">
Expand Down
7 changes: 4 additions & 3 deletions src/content/docs-core/en/crate-layers.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
# Crate layers

ThinkWatch Core consists of sixteen crates and one binary, `twcore`. The workspace divides the crates in two: the three that ThinkWatch Enterprise depends on, and the crates of the gateway that `twcore` runs, which ThinkWatch Enterprise does not use. Within the second part, the crates are grouped by role.
ThinkWatch Core consists of seventeen crates and one binary, `twcore`. The workspace divides the crates in two: the four that ThinkWatch Enterprise depends on, and the crates of the gateway that `twcore` runs, which ThinkWatch Enterprise does not use. Within the second part, the crates are grouped by role.

```
tw-dialect · tw-guard · tw-breaker ← shared with ThinkWatch Enterprise
tw-dialect · tw-guard · tw-breaker · tw-bedrock ← shared with ThinkWatch Enterprise
tw-types · tw-engine · tw-pricing · tw-yaml · tw-secret · tw-watch ← domain logic
tw-api · tw-link ← control-plane contract
tw-config · tw-store · tw-observe ← assembly
Expand All @@ -17,6 +17,7 @@ No crate depends on a group below its own.
| Shared with ThinkWatch Enterprise | `tw-dialect` | Conversion of requests, responses and streams between Anthropic Messages, OpenAI Chat Completions, OpenAI Responses and Gemini; usage parsing |
| | `tw-guard` | Outbound redaction and restoration, inspection of the tool calls an upstream returns, hidden characters, content filtering and the output length limit |
| | `tw-breaker` | The circuit-breaker state machine |
| | `tw-bedrock` | Amazon Bedrock on the wire: SigV4 signing, event-stream framing, addresses and the model catalog |
| Domain logic | `tw-types` | Messages for people: a stable code, its arguments and the English sentence |
| | `tw-engine` | The routing rule engine and strategy groups |
| | `tw-pricing` | The public price table, price sheets, and cost in three states: measured, estimated and unpriced |
Expand All @@ -33,7 +34,7 @@ No crate depends on a group below its own.

## Shared with ThinkWatch Enterprise

ThinkWatch Enterprise depends on the first group and nothing else: format conversion and usage parsing (`tw-dialect`), the guards (`tw-guard`) and the circuit breaker (`tw-breaker`). These three depend only on each other, which a test in `tw-dialect` enforces, and CI builds ThinkWatch Enterprise against every change to them. A component that only one product uses lives in that product's repository rather than in Core.
ThinkWatch Enterprise depends on the first group and nothing else: format conversion and usage parsing (`tw-dialect`), the guards (`tw-guard`), the circuit breaker (`tw-breaker`) and Amazon Bedrock's wire protocol (`tw-bedrock`). These four depend only on each other, which a test in `tw-dialect` enforces, and CI builds ThinkWatch Enterprise against every change to them. A component that only one product uses lives in that product's repository rather than in Core.

## Used by ThinkWatch Lite

Expand Down
2 changes: 1 addition & 1 deletion src/content/docs-core/en/overview.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# ThinkWatch Core

ThinkWatch Core is the gateway engine of the ThinkWatch products: a set of MIT-licensed Rust crates and `twcore`, a self-contained AI API gateway binary. `twcore` is the gateway inside the desktop app, [ThinkWatch Lite](/docs/lite), and also runs on its own as a systemd service on a Linux server. ThinkWatch Enterprise depends on three of the crates: `tw-dialect`, `tw-guard` and `tw-breaker`.
ThinkWatch Core is the gateway engine of the ThinkWatch products: a set of MIT-licensed Rust crates and `twcore`, a self-contained AI API gateway binary. `twcore` is the gateway inside the desktop app, [ThinkWatch Lite](/docs/lite), and also runs on its own as a systemd service on a Linux server. ThinkWatch Enterprise depends on four of the crates: `tw-dialect`, `tw-guard`, `tw-breaker` and `tw-bedrock`.

## Scope of Core

Expand Down
2 changes: 1 addition & 1 deletion src/content/docs-core/en/quick-start.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ On a Linux server, the install script does this in one step, and also creates a
curl -fsSL https://raw.githubusercontent.com/ThinkWatchProject/ThinkWatch-Core/main/scripts/install.sh | sudo sh
```

To build from source instead, with a stable Rust toolchain (1.85 or newer):
To build from source instead, with a stable Rust toolchain (1.94.1 or newer):

```sh
git clone https://github.com/ThinkWatchProject/ThinkWatch-Core.git
Expand Down
7 changes: 4 additions & 3 deletions src/content/docs-core/zh-CN/crate-layers.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
# crate 分层

ThinkWatch Core 由十六个 crate 和一个二进制 `twcore` 组成。工作区把这些 crate 分为两部分:ThinkWatch 企业版依赖的三个 crate,以及 `twcore` 所运行网关的其余 crate(ThinkWatch 企业版不使用)。第二部分再按职责分组。
ThinkWatch Core 由十七个 crate 和一个二进制 `twcore` 组成。工作区把这些 crate 分为两部分:ThinkWatch 企业版依赖的四个 crate,以及 `twcore` 所运行网关的其余 crate(ThinkWatch 企业版不使用)。第二部分再按职责分组。

```
tw-dialect · tw-guard · tw-breaker ← 与 ThinkWatch 企业版共用
tw-dialect · tw-guard · tw-breaker · tw-bedrock ← 与 ThinkWatch 企业版共用
tw-types · tw-engine · tw-pricing · tw-yaml · tw-secret · tw-watch ← 领域逻辑
tw-api · tw-link ← 控制面契约
tw-config · tw-store · tw-observe ← 装配
Expand All @@ -17,6 +17,7 @@ tw-gateway · tw-control ← 数据
| 与 ThinkWatch 企业版共用 | `tw-dialect` | 请求、响应与流在 Anthropic Messages、OpenAI Chat Completions、OpenAI Responses 与 Gemini 之间的转换;用量解析 |
| | `tw-guard` | 出站脱敏与还原、上游返回的工具调用审查、隐藏字符、内容过滤与输出长度限制 |
| | `tw-breaker` | 熔断器状态机 |
| | `tw-bedrock` | Amazon Bedrock 的线上协议:SigV4 签名、事件流拆帧、接口地址与模型目录 |
| 领域逻辑 | `tw-types` | 面向用户的消息:稳定的消息码、参数与英文句子 |
| | `tw-engine` | 路由规则引擎与策略组 |
| | `tw-pricing` | 公开价目表、自定义价目表,以及实测、估算、无法计价三种状态的费用 |
Expand All @@ -33,7 +34,7 @@ tw-gateway · tw-control ← 数据

## 与 ThinkWatch 企业版共用

ThinkWatch 企业版只依赖第一组:格式转换与用量解析(`tw-dialect`)、各项防护(`tw-guard`)与熔断器(`tw-breaker`)。这三个 crate 只依赖彼此,`tw-dialect` 中的一项测试保证这一点;每次改动它们,CI 都会用 ThinkWatch 企业版编译一遍。只有一个产品使用的组件放在该产品自己的仓库中,不留在 Core。
ThinkWatch 企业版只依赖第一组:格式转换与用量解析(`tw-dialect`)、各项防护(`tw-guard`)、熔断器(`tw-breaker`)与 Amazon Bedrock 的线上协议(`tw-bedrock`)。这四个 crate 只依赖彼此,`tw-dialect` 中的一项测试保证这一点;每次改动它们,CI 都会用 ThinkWatch 企业版编译一遍。只有一个产品使用的组件放在该产品自己的仓库中,不留在 Core。

## ThinkWatch Lite 使用的部分

Expand Down
2 changes: 1 addition & 1 deletion src/content/docs-core/zh-CN/overview.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# ThinkWatch Core

ThinkWatch Core 是 ThinkWatch 各产品共用的网关引擎,由一组采用 MIT 许可证的 Rust crate 和独立运行的 AI API 网关二进制 `twcore` 组成。`twcore` 是桌面应用 [ThinkWatch Lite](/zh-CN/docs/lite) 内置的网关,也可以作为 systemd 服务独立运行在 Linux 服务器上。ThinkWatch 企业版依赖其中的三个 crate:`tw-dialect`、`tw-guard` 与 `tw-breaker`。
ThinkWatch Core 是 ThinkWatch 各产品共用的网关引擎,由一组采用 MIT 许可证的 Rust crate 和独立运行的 AI API 网关二进制 `twcore` 组成。`twcore` 是桌面应用 [ThinkWatch Lite](/zh-CN/docs/lite) 内置的网关,也可以作为 systemd 服务独立运行在 Linux 服务器上。ThinkWatch 企业版依赖其中的四个 crate:`tw-dialect`、`tw-guard`、`tw-breaker` 与 `tw-bedrock`。

## Core 的定位

Expand Down
Loading
Loading