Skip to content

Commit 29b83af

Browse files
committed
fix: ecs test case
1 parent 87b03bc commit 29b83af

File tree

12 files changed

+103
-35
lines changed

12 files changed

+103
-35
lines changed

packages/ecs/src/systems/GridRenderer.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -95,8 +95,9 @@ export class GridRenderer {
9595
],
9696
});
9797
}
98-
99-
this.#program.setUniformsLegacy(uniformLegacyObject);
98+
if (uniformLegacyObject) {
99+
this.#program.setUniformsLegacy(uniformLegacyObject);
100+
}
100101
renderPass.setBindings(this.#bindings);
101102
renderPass.setPipeline(this.#pipeline);
102103
renderPass.setVertexInput(

packages/ecs/src/systems/MeshPipeline.ts

Lines changed: 11 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -282,6 +282,9 @@ export class MeshPipeline extends System {
282282
return;
283283
}
284284

285+
let renderer: GPURenderer;
286+
let gpuResource = canvas.read(GPUResource);
287+
285288
const request = canvas.has(RasterScreenshotRequest)
286289
? canvas.read(RasterScreenshotRequest)
287290
: null;
@@ -294,41 +297,34 @@ export class MeshPipeline extends System {
294297
const shouldRenderGrid = !request || grid;
295298
const shouldRenderPartially = nodes.length > 0;
296299

297-
let renderer: GPURenderer;
298-
let gpuResource: GPUResource;
299300
if (shouldRenderPartially) {
300301
// Render to offscreen canvas.
301302
gpuResource = this.setupDevice.getOffscreenGPUResource();
302303
renderer = this.createRenderer(gpuResource);
303304
} else {
304-
gpuResource = canvas.read(GPUResource);
305305
if (!this.renderers.get(camera)) {
306306
this.renderers.set(camera, this.createRenderer(gpuResource));
307307
}
308308
renderer = this.renderers.get(camera);
309-
const { batchManager } = renderer;
310-
if (request) {
311-
batchManager.hideUIs();
312-
}
313309
}
314310

315311
const { swapChain, device, renderTarget, depthRenderTarget } = gpuResource;
316-
const { uniformBuffer, uniformLegacyObject, gridRenderer, batchManager } =
317-
renderer;
312+
const { uniformBuffer, gridRenderer, batchManager } = renderer;
318313

319314
const { width, height } = swapChain.getCanvas();
320315
const onscreenTexture = swapChain.getOnscreenTexture();
321316

317+
if (request) {
318+
batchManager.hideUIs();
319+
}
320+
322321
const [buffer, legacyObject] = this.updateUniform(
323322
canvas,
324323
camera,
325324
shouldRenderGrid,
326325
swapChain,
327326
);
328-
this.renderers.set(camera, {
329-
...this.renderers.get(camera),
330-
uniformLegacyObject: legacyObject,
331-
});
327+
renderer.uniformLegacyObject = legacyObject;
332328

333329
uniformBuffer.setSubData(0, new Uint8Array(buffer.buffer));
334330

@@ -343,7 +339,7 @@ export class MeshPipeline extends System {
343339
});
344340
renderPass.setViewport(0, 0, width, height);
345341

346-
gridRenderer.render(device, renderPass, uniformBuffer, uniformLegacyObject);
342+
gridRenderer.render(device, renderPass, uniformBuffer, legacyObject);
347343

348344
if (shouldRenderPartially) {
349345
const { api } = canvas.read(Canvas);
@@ -367,7 +363,7 @@ export class MeshPipeline extends System {
367363
if (sort) {
368364
batchManager.sort();
369365
}
370-
batchManager.flush(renderPass, uniformBuffer, uniformLegacyObject);
366+
batchManager.flush(renderPass, uniformBuffer, legacyObject);
371367

372368
device.submitPass(renderPass);
373369
device.endFrame();

packages/ecs/src/systems/SetupDevice.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,16 @@ export class SetupDevice extends System {
150150
this.destroyCanvas(canvas);
151151
});
152152

153+
if (this.#offscreenElement) {
154+
const { device, renderTarget, depthRenderTarget, renderCache } =
155+
this.#offscreenGPUResource;
156+
renderCache.destroy();
157+
renderTarget.destroy();
158+
depthRenderTarget.destroy();
159+
device.destroy();
160+
device.checkForLeaks();
161+
}
162+
153163
this.#texturePool.destroy();
154164
}
155165

packages/ecs/src/utils/serialize/type.ts

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -296,6 +296,13 @@ export interface VectorNetworkSerializedNode
296296
extends BaseSerializeNode<'vector-network'>,
297297
Partial<VectorNetworkAttributes> {}
298298

299+
export interface HtmlAttributes {
300+
html: string;
301+
}
302+
export interface HtmlSerializedNode
303+
extends BaseSerializeNode<'html'>,
304+
Partial<HtmlAttributes> {}
305+
299306
export type SerializedNode =
300307
| GSerializedNode
301308
| EllipseSerializedNode
@@ -307,7 +314,8 @@ export type SerializedNode =
307314
| BrushSerializedNode
308315
| RoughRectSerializedNode
309316
| RoughEllipseSerializedNode
310-
| VectorNetworkSerializedNode;
317+
| VectorNetworkSerializedNode
318+
| HtmlSerializedNode;
311319

312320
export type SerializedNodeAttributes = GSerializedNode &
313321
EllipseSerializedNode &
@@ -319,4 +327,5 @@ export type SerializedNodeAttributes = GSerializedNode &
319327
BrushSerializedNode &
320328
RoughRectSerializedNode &
321329
RoughEllipseSerializedNode &
322-
VectorNetworkSerializedNode;
330+
VectorNetworkSerializedNode &
331+
HtmlSerializedNode;

packages/site/docs/.vitepress/config/en.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,10 @@ export const en = defineConfig({
127127
text: 'Lesson 028 - Integrating with AI',
128128
link: 'lesson-028',
129129
},
130+
{
131+
text: 'Lesson 029 - Embedding HTML content',
132+
link: 'lesson-029',
133+
},
130134
],
131135
},
132136
],

packages/site/docs/.vitepress/config/zh.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ export const zh = defineConfig({
7878
{ text: '课程26 - 选择工具', link: 'lesson-026' },
7979
{ text: '课程27 - 吸附与对齐', link: 'lesson-027' },
8080
{ text: '课程28 - 与 AI 结合', link: 'lesson-028' },
81+
{ text: '课程29 - 嵌入 HTML 内容', link: 'lesson-029' },
8182
],
8283
},
8384
],
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
outline: deep
3+
description: ''
4+
---
5+
6+
# Lesson 29 - Embedding HTML content
295 KB
Loading

packages/site/docs/zh/guide/lesson-028.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,14 @@ console.log(result.data); // { image: [{ url: 'https://...' }]; description: 'Su
117117

118118
目前 GPT 4o 仅支持三种固定尺寸,而 Nano banana 想实现任意图片尺寸输出需要借助一些 hack 手段,例如传入一张指定尺寸的空白图作为参考图并在 prompt 中强调。我们可以通过画布操作让它变的十分自然:用户只需要拖拽到合适的尺寸即可,应用通过 Canvas API 自动生成这个空白的参考图。
119119

120+
## MCP
121+
122+
来自 [MCP: What It Is and Why It Matters]
123+
124+
> Instead of only having a GUI or API that humans use, you get an AI interface “for free.” This idea has led to the concept of “MCP-first development”, where you build the MCP server for your app before or alongside the GUI.
125+
126+
[Figma MCP Server] 可以操作 [Figma API]
127+
120128
[课程 21 - Transformer]: /zh/guide/lesson-021
121129
[UI for AI]: https://medium.com/ui-for-ai
122130
[课程 1 - 硬件抽象层]: /zh/guide/lesson-001#hardware-abstraction-layers
@@ -129,3 +137,6 @@ console.log(result.data); // { image: [{ url: 'https://...' }]; description: 'Su
129137
[课程 26 - 选择工具]: /zh/guide/lesson-026#marquee-selection
130138
[课程 25 - 绘制模式与笔刷]: /zh/guide/lesson-025#brush-mode
131139
[Paper Shaders]: https://shaders.paper.design/
140+
[MCP: What It Is and Why It Matters]: https://addyo.substack.com/p/mcp-what-it-is-and-why-it-matters
141+
[Figma MCP Server]: https://github.com/GLips/Figma-Context-MCP
142+
[Figma API]: https://www.figma.com/developers/api

packages/site/docs/zh/guide/lesson-029.md

100755100644
Lines changed: 37 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,45 @@
11
---
22
outline: deep
3-
description: '探索MCP(模型上下文协议)在设计工具中的应用。学习如何为应用构建AI界面,实现MCP优先的开发模式。'
4-
publish: false
3+
description: ''
54
---
65

7-
# 课程 29 - MCP
6+
# 课程 29 - 嵌入 HTML 内容
87

9-
来自 [MCP: What It Is and Why It Matters]
8+
有时候我们希望在画布中嵌入 HTML 内容,例如 YouTube 播放器、CodeSandbox 组件、ShaderToy 等等。
109

11-
> Instead of only having a GUI or API that humans use, you get an AI interface “for free.” This idea has led to the concept of “MCP-first development”, where you build the MCP server for your app before or alongside the GUI.
10+
## HTML
1211

13-
[Figma MCP Server] 可以操作 [Figma API]
12+
Excalidraw 并不支持在画布中嵌入 HTML 内容,但 tldraw 支持 [TLEmbedShape]。它在网页中将一个 HTML 容器(含 iframe 或其他元素)和画布 `<svg>` 元素并排或叠加显示,而不是“完全”在单一画布内部。
1413

15-
[MCP: What It Is and Why It Matters]: https://addyo.substack.com/p/mcp-what-it-is-and-why-it-matters
16-
[Figma MCP Server]: https://github.com/GLips/Figma-Context-MCP
17-
[Figma API]: https://www.figma.com/developers/api
14+
![HTML external content in tldraw](/html-in-tldraw.png)
15+
16+
[External content sources] 例子中,我们可以看到 tldraw 是这样支持 HTML 内容的:
17+
18+
```ts
19+
class DangerousHtmlExample extends BaseBoxShapeUtil<IDangerousHtmlShape> {
20+
static override type = 'html' as const;
21+
22+
override getDefaultProps() {
23+
return {
24+
type: 'html',
25+
w: 500,
26+
h: 300,
27+
html: '<div>hello</div>',
28+
};
29+
}
30+
}
31+
```
32+
33+
我们也增加一种可序列化图形:
34+
35+
```ts
36+
export interface HtmlAttributes {
37+
html: string;
38+
}
39+
export interface HtmlSerializedNode
40+
extends BaseSerializeNode<'html'>,
41+
Partial<HtmlAttributes> {}
42+
```
43+
44+
[External content sources]: https://tldraw.dev/examples/external-content-sources
45+
[TLEmbedShape]: https://tldraw.dev/reference/tlschema/TLEmbedShape

0 commit comments

Comments
 (0)