Compare commits
3 Commits
master
...
cfbc6c36a5
| Author | SHA1 | Date | |
|---|---|---|---|
| cfbc6c36a5 | |||
| 4e7b4c3e2c | |||
| 6c1a77b346 |
@@ -5900,6 +5900,16 @@ iframe.preview-frame {
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
box-shadow: 0 8px 18px rgb(96 62 47 / 20%);
|
||||
transform: scale(var(--genarrative-image-canvas-inverse-scale, 1));
|
||||
transform-origin: top right;
|
||||
}
|
||||
|
||||
.game-resource-card-media-control,
|
||||
.game-resource-card-placeholder > svg,
|
||||
.game-resource-card-version-visual > svg,
|
||||
.game-resource-card-audio-visual > svg {
|
||||
transform: scale(var(--genarrative-image-canvas-inverse-scale, 1));
|
||||
transform-origin: center;
|
||||
}
|
||||
|
||||
.game-resource-card-open {
|
||||
|
||||
@@ -3,6 +3,7 @@ import {
|
||||
type CanvasViewport,
|
||||
resolveViewportFromWheel,
|
||||
} from '@genarrative/image-canvas-core';
|
||||
import { CanvasWorld } from '@genarrative/image-canvas-react';
|
||||
import {
|
||||
Code2,
|
||||
FileText,
|
||||
@@ -4906,17 +4907,12 @@ export default function ProjectDevelopmentView({
|
||||
>
|
||||
<RotateCcw size={15} aria-hidden="true" />
|
||||
</button>
|
||||
<div
|
||||
<CanvasWorld
|
||||
className="game-resource-page-world"
|
||||
viewport={activeResourceCanvasViewport}
|
||||
data-resource-boundary={`${resourceCanvasNavigationBounds.x},${resourceCanvasNavigationBounds.y},${resourceCanvasNavigationBounds.width},${resourceCanvasNavigationBounds.height}`}
|
||||
data-resource-fit-boundary={`${resourceCanvasFitBounds.x},${resourceCanvasFitBounds.y},${resourceCanvasFitBounds.width},${resourceCanvasFitBounds.height}`}
|
||||
data-resource-viewport={`${activeResourceCanvasViewport.x},${activeResourceCanvasViewport.y},${activeResourceCanvasViewport.scale}`}
|
||||
style={{
|
||||
width: `${resourceCanvasNavigationBounds.width}px`,
|
||||
height: `${resourceCanvasNavigationBounds.height}px`,
|
||||
transform: `translate(${activeResourceCanvasViewport.x}px, ${activeResourceCanvasViewport.y}px) scale(${activeResourceCanvasViewport.scale})`,
|
||||
transformOrigin: '0 0',
|
||||
}}
|
||||
>
|
||||
{sortMode === 'dependency' ? (
|
||||
<ResourceDependencyOverlay
|
||||
@@ -5016,7 +5012,7 @@ export default function ProjectDevelopmentView({
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</CanvasWorld>
|
||||
{(
|
||||
visibleResourcesByCategory.get(
|
||||
activePageCategory,
|
||||
|
||||
@@ -7949,6 +7949,13 @@ CI 上 `background_agent_runtime_recovers_stale_running_before_pending_task` 在
|
||||
- 新建/恢复 ephemeral Codex thread 时,replay 使用 `contextWindowTokens`、`autoCompactTokenLimit`、本次 `maxOutputTokens` 与 4096 安全余量计算预算,从最新记录向前选择连续完整的 `user` / `assistant` / `tool` 行;超预算旧前缀被省略,单条记录不截断,当前 user request 始终保留。
|
||||
- 发生省略时在 prompt 开头加入普通 `system: Earlier conversation history was omitted due to context budget.` 提示;当前请求本身超过硬上下文预算则直接失败。该策略是 Direct 专用滑动窗口,不复用 Runtime Agent 的摘要、tail 或 session compaction 生命周期。
|
||||
|
||||
## 2026-09-05 共享 CanvasWorld 统一超采样与资源页迁移
|
||||
|
||||
- 背景:资源总览页曾在业务层重复实现 world 的 transform、动态矩形尺寸和 2× `zoom`,与网站 / Tauri 共用的 `@genarrative/image-canvas-react` 形成第二套缩放逻辑;超采样还会让 SVG 卡片图标的线条产生视觉加粗。
|
||||
- 决策:`CanvasWorld` 默认统一采用 2× 超采样,内容按 `zoom: 2` 排版,外层使用 `viewport.scale / 2`,最终视觉比例仍由 `viewport.scale` 定义。所有调用方继续使用共享默认正方形 `CANVAS_WORLD_SIZE = 12000`,不引入 `worldWidth/worldHeight` 等矩形 API。资源页 `navigationBounds` 只保留布局、fit、依赖线 geometry 和 data 属性用途,不再驱动 world DOM 尺寸。共享样式对 world 内 SVG 几何元素设置 `vector-effect: non-scaling-stroke`;位图维持浏览器默认插值,不全局启用 `pixelated`。
|
||||
- 影响范围:资源总览迁移为直接渲染共享 `CanvasWorld`,卡片控件通过共享 inverse-scale CSS 变量保持屏幕级尺寸;Asset Canvas、UI Editor 和依赖线继续消费相同 viewport 坐标与事件换算,不改变业务状态或持久化合同。
|
||||
- 验证方式:共享包与 AGC 定向类型 / 测试、资源页集成测试、`npm run check:encoding` 和 `git diff --check`;重点检查 `scale=1/2` 的最终 transform、SVG 描边、拖拽坐标、关系线端点和默认 `12000 × 12000` world。
|
||||
|
||||
## 2026-08-31 AGC 错误报告与诊断上传
|
||||
|
||||
- AGC 采用 IDEA 风格的当前进程错误池:按 fingerprint 合并 React / window / Promise / Tauri / Agent 错误,重启后不恢复,不使用 run 或 run_id。
|
||||
|
||||
@@ -549,6 +549,7 @@ game-project/
|
||||
- 中间主视窗提供 `资源管理 / 运行` 切换。`code-prototype` 任务完成前运行入口保持视觉不可用,但仍可点击查看“当前无可运行版本”,不能使用会阻断说明交互的原生 `disabled` 或 `aria-disabled`;完成后才允许进入运行表现层。切回资源管理只修改前端展示态,不伪造后端预览暂停结果。
|
||||
- 资源管理从当前 `GameCreationAppManifest`(包含可选 `versions`)、合法 Agent 文本回执和已导入附件派生资源,固定按文档、项目版本、美术资源、音乐音效资源分区;未知任务产物不再兜底为版本,任务声明中的未登记音频也不冒充正式音频。`按依赖 / 按类型` 使用各自前端排列,dependency 模式额外绘制当前 manifest 与资源投影可证明的依赖关系。排列与图层都不写回 manifest,不能推断或伪造缺失依赖。
|
||||
- 资源卡支持点击聚焦、搜索和类型筛选。2026-07-28 起完成两套二维坐标与本地 CAS sidecar;2026-07-31 起 dependency 模式增加不持久化的原生 SVG 关系图层。2026-08-03 mentor 决定暂缓资源卡拖动,当前卡片不挂载 Pointer Down / Move / Up / Cancel 拖动入口,只允许自动布局和点击聚焦。聚焦态替换中央主视窗内容,保留左侧导航、右侧对话和底部 Agent 状态栏,退出后恢复搜索、布局模式、滚动位置与选中资源;不提供工具栏、工具侧边栏或可拖动标题栏。2026-08-10 起聚焦态以资源元数据、Rust 权威深度、同类型上下游 / 任务流和版本信息为首屏;美术图片与视频只保留卡内本体,不在详情重复放大。安全文档正文与按意图读取的音频控制位于元数据之后;美术编辑、音频编辑 / 替换、版本替换或运行模块仍不在本阶段。
|
||||
- 2026-09-05 起所有 `@genarrative/image-canvas-react` 的 `CanvasWorld` 默认使用统一 2× 超采样:内容层按 `zoom: 2` 排版,外层 transform 使用 `viewport.scale / 2`,因此 `viewport.scale` 仍表示最终视觉比例(`1` 为原始大小,`2` 为放大两倍)。world 继续使用共享默认正方形 `CANVAS_WORLD_SIZE = 12000`,不新增矩形尺寸 API;资源页的 `navigationBounds` 只用于布局、fit、关系线 geometry 和数据属性,不能再设置 world DOM 宽高。共享 world 为 SVG 几何元素启用 `vector-effect: non-scaling-stroke`,避免超采样或缩放使卡片图标和关系线描边变粗;普通位图不强制 `pixelated` 插值。依赖线、卡片拖拽、平移和滚轮锚点继续使用同一逻辑 viewport 坐标。
|
||||
|
||||
#### 资源管理串行改造:本体卡、分区缩放与依赖聚类
|
||||
|
||||
|
||||
@@ -2,7 +2,9 @@ import {
|
||||
CANVAS_WORLD_SIZE,
|
||||
type CanvasViewport,
|
||||
} from '@genarrative/image-canvas-core';
|
||||
import type { HTMLAttributes, ReactNode } from 'react';
|
||||
import type { CSSProperties, HTMLAttributes, ReactNode } from 'react';
|
||||
|
||||
const CANVAS_RENDER_SUPERSAMPLE = 2;
|
||||
|
||||
export function CanvasWorld({
|
||||
children,
|
||||
@@ -16,18 +18,34 @@ export function CanvasWorld({
|
||||
viewport: CanvasViewport;
|
||||
worldSize?: number;
|
||||
}) {
|
||||
const viewportScale =
|
||||
Number.isFinite(viewport.scale) && viewport.scale > 0 ? viewport.scale : 1;
|
||||
|
||||
return (
|
||||
<div
|
||||
{...props}
|
||||
className={`genarrative-image-canvas__world ${className}`.trim()}
|
||||
style={{
|
||||
...style,
|
||||
width: worldSize,
|
||||
height: worldSize,
|
||||
transform: `translate(${viewport.x}px, ${viewport.y}px) scale(${viewport.scale})`,
|
||||
}}
|
||||
style={
|
||||
{
|
||||
...style,
|
||||
width: worldSize,
|
||||
height: worldSize,
|
||||
transform: `translate(${viewport.x}px, ${viewport.y}px) scale(${viewportScale / CANVAS_RENDER_SUPERSAMPLE})`,
|
||||
'--genarrative-image-canvas-inverse-scale': String(1 / viewportScale),
|
||||
} as CSSProperties
|
||||
}
|
||||
>
|
||||
{children}
|
||||
<div
|
||||
className="genarrative-image-canvas__world-content"
|
||||
style={{
|
||||
position: 'relative',
|
||||
width: worldSize,
|
||||
height: worldSize,
|
||||
zoom: CANVAS_RENDER_SUPERSAMPLE,
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
/* @vitest-environment jsdom */
|
||||
|
||||
import type { CanvasLayer } from '@genarrative/image-canvas-core';
|
||||
import { act, renderHook } from '@testing-library/react';
|
||||
import { act, render, renderHook, screen } from '@testing-library/react';
|
||||
import { createRef } from 'react';
|
||||
import { afterEach, describe, expect, it, vi } from 'vitest';
|
||||
|
||||
import { CanvasWorld } from './CanvasWorld';
|
||||
import { useImageCanvasViewportControls } from './useImageCanvasViewportControls';
|
||||
|
||||
function layer(id: string, x: number): CanvasLayer {
|
||||
@@ -52,6 +53,24 @@ afterEach(() => {
|
||||
});
|
||||
|
||||
describe('shared canvas React lifecycle', () => {
|
||||
it('renders a supersampled world while preserving the public viewport scale', () => {
|
||||
render(
|
||||
<CanvasWorld viewport={{ x: 12, y: 24, scale: 2 }}>
|
||||
<span>content</span>
|
||||
</CanvasWorld>,
|
||||
);
|
||||
|
||||
const world = screen.getByText('content').parentElement?.parentElement;
|
||||
const content = screen.getByText('content').parentElement;
|
||||
expect(world?.style.width).toBe('12000px');
|
||||
expect(world?.style.height).toBe('12000px');
|
||||
expect(world?.style.transform).toBe('translate(12px, 24px) scale(1)');
|
||||
expect(
|
||||
world?.style.getPropertyValue('--genarrative-image-canvas-inverse-scale'),
|
||||
).toBe('0.5');
|
||||
expect(content?.style.zoom).toBe('2');
|
||||
});
|
||||
|
||||
it('removes every native listener across repeated mounts', () => {
|
||||
const viewport = createViewport();
|
||||
const addListener = vi.spyOn(viewport, 'addEventListener');
|
||||
|
||||
@@ -29,6 +29,16 @@
|
||||
transform-origin: 0 0;
|
||||
}
|
||||
|
||||
.genarrative-image-canvas__world svg path,
|
||||
.genarrative-image-canvas__world svg line,
|
||||
.genarrative-image-canvas__world svg polyline,
|
||||
.genarrative-image-canvas__world svg polygon,
|
||||
.genarrative-image-canvas__world svg circle,
|
||||
.genarrative-image-canvas__world svg rect,
|
||||
.genarrative-image-canvas__world svg ellipse {
|
||||
vector-effect: non-scaling-stroke;
|
||||
}
|
||||
|
||||
.genarrative-image-canvas__layer {
|
||||
position: absolute;
|
||||
display: block;
|
||||
|
||||
Reference in New Issue
Block a user