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
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ const capabilityCopy: Record<WorkspaceLocale, Record<string, LocalizedCopy>> = {
en: {
codex_chat: {displayName: "Codex Goal conversation defaults", description: "Choose the native Codex profile, model, speed tier and reasoning effort for new Goal conversations. Existing conversations keep their original history. This does not start or reconfigure an active Agent Loop, worker or steward."},
decision_context: {displayName: "Find evidence", description: "Find advisory evidence with a temporary private profile. OpenViking and BM25 are retrieval implementations; this page does not save a binding or enable automatic memory."},
content_ops: {displayName: "Content production", description: "Prepare source-backed outlines and local videos. Review the result before sharing; rendering uses an optional local extension."},
content_ops: {displayName: "Content production", description: "Prepare source-backed outlines, interactive learning models and local videos. Review before sharing; video rendering uses an optional local extension."},
goal_storage: { displayName: "New Goal authority", description: "New Goals default to canonical SQLite with hard leases. Choose storage and execution policy for future Goals. Existing Goals require a separate backed-up migration." },
manager_runtime: {
displayName: "Runtime",
Expand Down Expand Up @@ -83,7 +83,7 @@ const capabilityCopy: Record<WorkspaceLocale, Record<string, LocalizedCopy>> = {
"zh-CN": {
codex_chat: {displayName: "Codex Goal 对话默认值", description: "分别选择 Codex 配置来源、模型、速度档和推理强度,作用于本机的新 Goal 对话。已有对话保留原历史;不会启动或重配正在运行的 Agent Loop、工作 Agent 或管家。"},
decision_context: {displayName: "资料召回", description: "通过临时私有配置查找参考证据。OpenViking、BM25 是检索实现;本页不保存绑定,也不启用自动记忆。"},
content_ops: {displayName: "内容制作", description: "准备有来源的提纲与本机视频,检查作品后再分享;视频制作使用可选本机扩展。"},
content_ops: {displayName: "内容制作", description: "准备有来源的提纲、交互学习模型与本机视频,检查作品后再分享;视频制作使用可选本机扩展。"},
goal_storage: { displayName: "新 Goal 的权威存储", description: "新 Goal 默认使用 canonical SQLite 与 hard lease;可选择之后新 Goal 的存储与执行策略。已有 Goal 仍需单独备份、迁移。" },
manager_runtime: {
displayName: "运行环境",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import type {ExperiencePlan, ExperienceRun, ExperienceFrame} from "../../../../../../loopx/control_plane/capabilities/content_experience_contract";

export type ExperienceSceneRow = {label: string; run: ExperienceRun | null; index: number};
export function experienceSceneHeight(width: number, count: number) {return 100 + 258 * (width < 640 ? count : 1);}
export function experienceFrameFeedback(frame: ExperienceFrame, status: ExperienceRun["status"] | undefined, cn: boolean) {
if (cn && !frame.applied && status === "budget_exhausted") return "剩余模型预算不足,状态与已用预算保持原值。";
if (cn && !frame.applied && status === "action_unavailable") return "当前条件没有这一步的转移规则,状态保持原值。";
return frame.feedback;
}

/** Draw only this model's own content. Recording never captures another page. */
export function drawExperienceScene(ctx: CanvasRenderingContext2D, width: number, height: number,
model: ExperiencePlan, rows: ExperienceSceneRow[], digest: string, locale: string) {
ctx.clearRect(0, 0, width, height); ctx.fillStyle = "#f5f6f8"; ctx.fillRect(0, 0, width, height);
const text = (s: string, x: number, y: number, size: number, color = "#1a202c", maxWidth = width - x - 20, lines = 1) => {
ctx.font = `${size >= 20 ? "600" : "400"} ${size}px system-ui, sans-serif`; ctx.fillStyle = color;
const words = s.split(/\s+/u); let line = "", used = 0;
for (const word of words) {
const candidate = line ? `${line} ${word}` : word;
if (ctx.measureText(candidate).width > maxWidth && line) {
if (++used === lines) {ctx.fillText(`${line.slice(0, -1)}…`, x, y, maxWidth); return;}
ctx.fillText(line, x, y, maxWidth); y += size * 1.4; line = word;
} else line = candidate;
}
ctx.fillText(line, x, y, maxWidth);
};
text(model.title, 20, 31, width < 500 ? 19 : 23);
text(model.boundary, 20, 54, 12, "#596272");
const vertical = width < 640, gap = 12, cellWidth = vertical ? width - 32 : (width - 32 - gap * (rows.length - 1)) / rows.length;
rows.forEach((row, i) => {
const x = vertical ? 16 : 16 + i * (cellWidth + gap), y = 73 + (vertical ? i * 258 : 0);
ctx.fillStyle = "#fff"; ctx.fillRect(x, y, cellWidth, 244); ctx.strokeStyle = "#d8dce3"; ctx.lineWidth = 1; ctx.strokeRect(x + .5, y + .5, cellWidth - 1, 243);
const trace = row.run?.trace ?? [{state: model.initial_state, action: null, feedback: model.states.find(s => s.id === model.initial_state)!.description, spent: 0, applied: true}];
const at = Math.min(row.index, trace.length - 1), frame = trace[at]!, state = model.states.find(s => s.id === frame.state)!;
text(row.label, x + 15, y + 23, 14, "#596272", cellWidth - 30);
const conditions = row.run ? model.parameters.map(p => p.choices.find(c => c.id === row.run!.parameters[p.id])!.label).join(" · ") : (locale === "zh-CN" ? "等待运行" : "Awaiting a run");
text(conditions, x + 15, y + 44, 12, "#596272", cellWidth - 30, 2);
text(state.label, x + 15, y + 88, cellWidth < 340 ? 23 : 28, "#165ed3", cellWidth - 30);
text(experienceFrameFeedback(frame, row.run?.status, locale === "zh-CN"), x + 15, y + 115, 15, "#303a49", cellWidth - 30, 3);
text(`${locale === "zh-CN" ? "模型用量" : "Model effort"} ${frame.spent} / ${model.budget} · ${at} ${locale === "zh-CN" ? "步" : "decisions"}`, x + 15, y + 201, 14, "#303a49", cellWidth - 30);
const trail = trace.slice(Math.max(0, at - 3), at + 1).map(f => model.states.find(s => s.id === f.state)!.label).join(" → ");
text(trail, x + 15, y + 224, 12, "#596272", cellWidth - 30);
});
text(`Source revision ${digest.slice(7, 19)} · model illustration`, 20, height - 12, 11, "#68717e");
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
.content-experience {padding:18px 0 26px; color:var(--pw-text);}
.experience-heading {display:flex; align-items:flex-start; justify-content:space-between; gap:18px; margin-bottom:18px;}
.content-experience h3,.content-experience h4 {margin:0 0 8px; font-size:var(--text-lead); font-weight:600; line-height:1.4;}
.content-experience p {margin:6px 0 14px; font-size:var(--text-body); line-height:1.65; color:var(--pw-muted);}
.content-experience button,.content-experience select {min-height:44px; border:1px solid var(--pw-line-strong); border-radius:6px; background:var(--pw-card); color:inherit; padding:9px 12px; font:inherit; font-size:var(--text-body);}
.content-experience button {cursor:pointer;}
.content-experience button:disabled,.content-experience select:disabled {opacity:.5; cursor:default;}
.content-experience button:focus-visible,.content-experience select:focus-visible,.content-experience textarea:focus-visible,.content-experience a:focus-visible,.content-experience input:focus-visible,.content-experience [tabindex]:focus-visible {outline:2px solid var(--pw-blue); outline-offset:3px;}
.content-experience label {display:grid; gap:7px; font-size:var(--text-body); min-width:0;}
.content-experience select {width:100%; min-width:0; text-overflow:ellipsis;}
.experience-model-picker {max-width:500px; margin-bottom:20px;}
.experience-player {min-width:0;}
.experience-player:fullscreen {overflow:auto; box-sizing:border-box; padding:24px max(20px,calc((100vw - 1160px)/2)); background:var(--pw-bg); color:var(--pw-text);}
.experience-focus {margin:2px 0 8px;}
.experience-goal {padding:20px 0 10px; border-top:1px solid var(--pw-line);}
.experience-goal>span {display:block; font-size:var(--text-caption); margin-bottom:7px; color:var(--pw-muted);}
.experience-goal h4 {font-size:24px; letter-spacing:-.02em;}
.experience-modes {display:flex; flex-wrap:wrap; gap:6px; margin:10px 0 22px;}
.experience-modes button {border-color:transparent;}
.experience-modes button[aria-pressed=true] {background:var(--pw-hover); border-color:var(--pw-line-strong); font-weight:600;}
.experience-controls {display:flex; gap:14px; align-items:flex-end; margin-bottom:14px; flex-wrap:wrap;}
.experience-controls label {flex:1; max-width:460px;}
.experience-comparison {display:grid; grid-template-columns:minmax(0,1fr) minmax(0,1fr); gap:16px; margin:14px 0;}
.experience-comparison fieldset {min-width:0; margin:0; border:1px solid var(--pw-line); border-radius:7px; padding:14px; display:grid; gap:12px;}
.experience-comparison legend {padding:0 6px; font-size:var(--text-body); font-weight:600;}
.content-experience .experience-primary {background:var(--pw-text); color:var(--pw-bg); border-color:var(--pw-text); font-weight:600;}
.experience-results {display:grid; grid-template-columns:1fr 1fr; gap:16px; margin:15px 0;}
.experience-results p {margin:0;}
.experience-results strong,.experience-results span {display:block;}
.experience-results strong {color:var(--pw-text); font-size:var(--text-body);}
.experience-results span {font-size:var(--text-caption);}
.experience-game-status {border-left:3px solid var(--pw-blue); padding:4px 0 4px 16px; margin:20px 0;}
.experience-game-status>strong {display:block; font-size:22px; margin-bottom:5px;}
.experience-game-status>span {font-size:var(--text-body); color:var(--pw-muted);}
.experience-game-status p {margin-bottom:4px;}
.experience-actions {display:flex; gap:10px; flex-wrap:wrap; margin:16px 0;}
.experience-actions button {display:grid; text-align:left; gap:5px; min-width:160px;}
.experience-actions small {font-size:var(--text-caption); color:var(--pw-muted);}
.experience-replay {padding:12px 0; font-size:var(--text-caption)!important; line-height:1.6;}
.experience-replay input {width:100%; min-height:32px; accent-color:var(--pw-blue);}
.experience-canvas {display:block; width:100%; height:358px; margin:20px 0; border:1px solid var(--pw-line); border-radius:8px;}
.experience-record {display:flex; align-items:center; flex-wrap:wrap; gap:12px; margin:14px 0; font-size:var(--text-body);}
.experience-record [aria-pressed=true] {border-color:var(--pw-blue); color:var(--pw-blue);}
.experience-reflection {margin:14px 0;}
.experience-reflection textarea {min-height:68px; resize:vertical; padding:10px; border-radius:6px; background:var(--pw-card); color:inherit; border:1px solid var(--pw-line-strong); font:inherit;}
.content-experience details {margin-top:18px; font-size:var(--text-caption); line-height:1.6; overflow-wrap:anywhere;}
.content-experience summary {cursor:pointer; color:var(--pw-muted); min-height:32px;}
.content-experience details ul {padding-left:20px;}
.experience-rules {overflow-x:auto;}
.experience-rules table {width:100%; min-width:520px; border-collapse:collapse; font-size:var(--text-caption);}
.experience-rules th,.experience-rules td {padding:10px; text-align:left; border-bottom:1px solid var(--pw-line); vertical-align:top;}
.content-production-tabs {display:flex; gap:6px; padding:12px 0; border-bottom:1px solid var(--pw-line); overflow-x:auto;}
.content-production-tabs button {min-height:44px; flex-shrink:0; padding:8px 14px; border:1px solid transparent; border-radius:6px; color:var(--pw-muted); background:transparent; font:inherit; cursor:pointer;}
.content-production-tabs button[aria-selected=true] {border-color:var(--pw-line-strong); color:var(--pw-text); background:var(--pw-hover);}
.content-production-tabs button:focus-visible {outline:2px solid var(--pw-blue); outline-offset:2px;}
@media(max-width:680px) {.experience-heading {flex-wrap:wrap;}.experience-comparison,.experience-results {grid-template-columns:minmax(0,1fr);}.experience-modes {display:grid; grid-template-columns:1fr;}.experience-modes button {text-align:left;}.experience-actions {display:grid; grid-template-columns:1fr;}.experience-model-picker {max-width:none;}.experience-goal h4 {font-size:22px;}}
Loading
Loading