hikkaku 0.1.14 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -0
- package/blocks/index.d.mts +2339 -5
- package/blocks/index.mjs +2427 -44
- package/client/index.mjs +4 -1
- package/{composer-JWWbnKkh.mjs → composer-BudVTTBs.mjs} +83 -12
- package/index.d.mts +2 -2
- package/index.mjs +73 -12
- package/package.json +7 -4
- package/project-Ca9rsVT3.d.mts +139 -0
- package/vite/index.mjs +71 -7
- package/project-BSiPw18H.d.mts +0 -73
package/client/index.mjs
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { zipSync } from "fflate";
|
|
2
|
+
|
|
1
3
|
//#region src/client/fiber.ts
|
|
2
4
|
const findDOMAppRoot = () => {
|
|
3
5
|
const probably = [document.getElementById("app"), document.querySelector("[class^=index_app]")];
|
|
@@ -59,7 +61,8 @@ import.meta.hot?.on("hikkaku:project", async (project) => {
|
|
|
59
61
|
isFirstLoad = false;
|
|
60
62
|
}
|
|
61
63
|
console.log("Received updated project:", project);
|
|
62
|
-
|
|
64
|
+
const projectSB3 = zipSync({ "project.json": new TextEncoder().encode(JSON.stringify(project)) });
|
|
65
|
+
await state.vm.loadProject(projectSB3).catch(console.error);
|
|
63
66
|
console.log("Project loaded.");
|
|
64
67
|
});
|
|
65
68
|
|
|
@@ -1,9 +1,37 @@
|
|
|
1
|
+
import { InputType, Shadow } from "sb3-types/enum";
|
|
2
|
+
|
|
3
|
+
//#region src/core/sb3-enum.ts
|
|
4
|
+
const toNumericEnum = (value, fallback) => {
|
|
5
|
+
if (typeof value === "number" && Number.isInteger(value)) return value;
|
|
6
|
+
if (typeof value === "string") {
|
|
7
|
+
const normalized = value.trim();
|
|
8
|
+
if (normalized.length === 0) return fallback;
|
|
9
|
+
const parsed = Number(normalized);
|
|
10
|
+
if (Number.isInteger(parsed)) return parsed;
|
|
11
|
+
}
|
|
12
|
+
return fallback;
|
|
13
|
+
};
|
|
14
|
+
const shadow = Shadow;
|
|
15
|
+
const inputType = InputType;
|
|
16
|
+
const Shadow$1 = {
|
|
17
|
+
SameBlockShadow: toNumericEnum(shadow.SameBlockShadow ?? shadow.UnObscured, 1),
|
|
18
|
+
NoShadow: toNumericEnum(shadow.NoShadow ?? shadow.No, 2),
|
|
19
|
+
DiffBlockShadow: toNumericEnum(shadow.DiffBlockShadow ?? shadow.Obscured, 3)
|
|
20
|
+
};
|
|
21
|
+
const InputType$1 = {
|
|
22
|
+
Number: toNumericEnum(inputType.Number, 4),
|
|
23
|
+
PositiveInteger: toNumericEnum(inputType.PositiveInteger ?? inputType.PossiveInteger, 6),
|
|
24
|
+
String: toNumericEnum(inputType.String, 10),
|
|
25
|
+
Broadcast: toNumericEnum(inputType.Broadcast, 11)
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
//#endregion
|
|
1
29
|
//#region src/core/block-helper.ts
|
|
2
30
|
const fromPrimitiveSource = (source) => {
|
|
3
|
-
if (typeof source === "number") return [1, [
|
|
4
|
-
if (typeof source === "boolean") return [1, [
|
|
5
|
-
if (typeof source === "string") return [1, [
|
|
6
|
-
return [1, source.id];
|
|
31
|
+
if (typeof source === "number") return [Shadow$1.SameBlockShadow, [InputType$1.Number, source]];
|
|
32
|
+
if (typeof source === "boolean") return [Shadow$1.SameBlockShadow, [InputType$1.PositiveInteger, source ? 1 : 0]];
|
|
33
|
+
if (typeof source === "string") return [Shadow$1.SameBlockShadow, [InputType$1.String, source]];
|
|
34
|
+
return [Shadow$1.SameBlockShadow, source.id];
|
|
7
35
|
};
|
|
8
36
|
const fromCostumeSource = (source) => {
|
|
9
37
|
if (typeof source === "object" && source !== null && "type" in source && source.type === "costume") return fromPrimitiveSource(source.name);
|
|
@@ -88,8 +116,35 @@ const catchNewBlocks = (handler, catched) => {
|
|
|
88
116
|
ctx.adder = (id, block) => {
|
|
89
117
|
catched(id, block);
|
|
90
118
|
};
|
|
91
|
-
|
|
92
|
-
|
|
119
|
+
try {
|
|
120
|
+
handler();
|
|
121
|
+
} finally {
|
|
122
|
+
ctx.adder = oldAdder;
|
|
123
|
+
}
|
|
124
|
+
};
|
|
125
|
+
const firstExecutableBlockId = (blocks) => {
|
|
126
|
+
const ctx = getRootContext();
|
|
127
|
+
for (const block of blocks) {
|
|
128
|
+
if (!block || ctx.usedAsValueSet.has(block)) continue;
|
|
129
|
+
const blockId = ctx.blockToId.get(block);
|
|
130
|
+
if (blockId) return blockId;
|
|
131
|
+
}
|
|
132
|
+
return null;
|
|
133
|
+
};
|
|
134
|
+
const layoutTopLevelBlocks = (blocks) => {
|
|
135
|
+
const ctx = getRootContext();
|
|
136
|
+
const seen = /* @__PURE__ */ new Set();
|
|
137
|
+
let index = 0;
|
|
138
|
+
for (const block of blocks) {
|
|
139
|
+
if (!block || !block.topLevel) continue;
|
|
140
|
+
const blockId = ctx.blockToId.get(block);
|
|
141
|
+
if (!blockId || seen.has(blockId)) continue;
|
|
142
|
+
seen.add(blockId);
|
|
143
|
+
if (block.x !== 0 || block.y !== 0) continue;
|
|
144
|
+
block.x = 64;
|
|
145
|
+
block.y = 72 + index * 260;
|
|
146
|
+
index++;
|
|
147
|
+
}
|
|
93
148
|
};
|
|
94
149
|
const substack = (handler) => {
|
|
95
150
|
const ctx = getRootContext();
|
|
@@ -99,12 +154,27 @@ const substack = (handler) => {
|
|
|
99
154
|
blocks.push(block);
|
|
100
155
|
});
|
|
101
156
|
applyNextAndParent(blocks);
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
157
|
+
return firstExecutableBlockId(blocks);
|
|
158
|
+
};
|
|
159
|
+
const attachStack = (parentId, handler) => {
|
|
160
|
+
if (!handler) return null;
|
|
161
|
+
const ctx = getRootContext();
|
|
162
|
+
const blocks = [];
|
|
163
|
+
catchNewBlocks(handler, (id, block) => {
|
|
164
|
+
ctx.blocks[id] = block;
|
|
165
|
+
blocks.push(block);
|
|
166
|
+
});
|
|
167
|
+
const stackBlocks = blocks.filter((block) => !block.topLevel);
|
|
168
|
+
applyNextAndParent(stackBlocks);
|
|
169
|
+
const firstBlockId = firstExecutableBlockId(stackBlocks);
|
|
170
|
+
if (!firstBlockId) return null;
|
|
171
|
+
const parentBlock = ctx.blocks[parentId];
|
|
172
|
+
const firstBlock = ctx.blocks[firstBlockId];
|
|
173
|
+
if (parentBlock && firstBlock) {
|
|
174
|
+
parentBlock.next = firstBlockId;
|
|
175
|
+
firstBlock.parent = parentId;
|
|
106
176
|
}
|
|
107
|
-
return
|
|
177
|
+
return firstBlockId;
|
|
108
178
|
};
|
|
109
179
|
const createBlocks = (handler) => {
|
|
110
180
|
const blocks = {};
|
|
@@ -120,6 +190,7 @@ const createBlocks = (handler) => {
|
|
|
120
190
|
blocksForAddingNext.push(block);
|
|
121
191
|
});
|
|
122
192
|
applyNextAndParent(blocksForAddingNext);
|
|
193
|
+
layoutTopLevelBlocks(blocksForAddingNext);
|
|
123
194
|
const unconnectedValueBlocks = [];
|
|
124
195
|
for (const [blockId, block] of Object.entries(blocks)) if (rootContext.valueBlockSet.has(block) && !rootContext.usedAsValueSet.has(block)) unconnectedValueBlocks.push({
|
|
125
196
|
id: blockId,
|
|
@@ -135,4 +206,4 @@ const createBlocks = (handler) => {
|
|
|
135
206
|
};
|
|
136
207
|
|
|
137
208
|
//#endregion
|
|
138
|
-
export {
|
|
209
|
+
export { valueBlock as a, fromSoundSource as c, substack as i, InputType$1 as l, block as n, fromCostumeSource as o, createBlocks as r, fromPrimitiveSource as s, attachStack as t, Shadow$1 as u };
|
package/index.d.mts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import {
|
|
2
|
-
export { BlockInit, CostumeReference, CostumeSource, Handler, HikkakuBlock, ListReference, PrimitiveAvailableOnScratch, PrimitiveSource, Project, SoundReference, SoundSource, Target, VariableBase, VariableReference, block, createBlocks, fromCostumeSource, fromPrimitiveSource, fromSoundSource, substack, valueBlock };
|
|
1
|
+
import { C as SoundReference, D as VariableMonitorMode, E as VariableDefinition, O as VariableMonitorOptions, S as PrimitiveSource, T as VariableBase, _ as HikkakuBlock, a as attachStack, b as MonitorPosition, c as substack, d as fromPrimitiveSource, f as fromSoundSource, g as CreateVariableOptions, h as CreateListOptions, i as Handler, k as VariableReference, l as valueBlock, m as CostumeSource, n as Target, o as block, p as CostumeReference, r as BlockInit, s as createBlocks, t as Project, u as fromCostumeSource, v as ListMonitorOptions, w as SoundSource, x as PrimitiveAvailableOnScratch, y as ListReference } from "./project-Ca9rsVT3.mjs";
|
|
2
|
+
export { BlockInit, CostumeReference, CostumeSource, CreateListOptions, CreateVariableOptions, Handler, HikkakuBlock, ListMonitorOptions, ListReference, MonitorPosition, PrimitiveAvailableOnScratch, PrimitiveSource, Project, SoundReference, SoundSource, Target, VariableBase, VariableDefinition, VariableMonitorMode, VariableMonitorOptions, VariableReference, attachStack, block, createBlocks, fromCostumeSource, fromPrimitiveSource, fromSoundSource, substack, valueBlock };
|
package/index.mjs
CHANGED
|
@@ -1,5 +1,54 @@
|
|
|
1
|
-
import { a as
|
|
1
|
+
import { a as valueBlock, c as fromSoundSource, i as substack, n as block, o as fromCostumeSource, r as createBlocks, s as fromPrimitiveSource, t as attachStack } from "./composer-BudVTTBs.mjs";
|
|
2
2
|
|
|
3
|
+
//#region src/core/monitors.ts
|
|
4
|
+
const createVariableMonitor = (id, name, defaultValue, spriteName, options) => {
|
|
5
|
+
const sliderMin = options.sliderMin ?? 0;
|
|
6
|
+
const sliderMax = options.sliderMax ?? 100;
|
|
7
|
+
return {
|
|
8
|
+
id,
|
|
9
|
+
mode: options.mode ?? "default",
|
|
10
|
+
opcode: "data_variable",
|
|
11
|
+
params: { VARIABLE: name },
|
|
12
|
+
spriteName,
|
|
13
|
+
value: defaultValue,
|
|
14
|
+
sliderMin: Math.min(sliderMin, sliderMax),
|
|
15
|
+
sliderMax: Math.max(sliderMin, sliderMax),
|
|
16
|
+
isDiscrete: options.isDiscrete ?? true,
|
|
17
|
+
x: options.x ?? null,
|
|
18
|
+
y: options.y ?? null,
|
|
19
|
+
width: 0,
|
|
20
|
+
height: 0,
|
|
21
|
+
visible: options.visible ?? true
|
|
22
|
+
};
|
|
23
|
+
};
|
|
24
|
+
const createListMonitor = (id, name, defaultValue, spriteName, options) => {
|
|
25
|
+
return {
|
|
26
|
+
id,
|
|
27
|
+
mode: "list",
|
|
28
|
+
opcode: "data_listcontents",
|
|
29
|
+
params: { LIST: name },
|
|
30
|
+
spriteName,
|
|
31
|
+
value: [...defaultValue],
|
|
32
|
+
x: options.x ?? null,
|
|
33
|
+
y: options.y ?? null,
|
|
34
|
+
width: options.width ?? 0,
|
|
35
|
+
height: options.height ?? 0,
|
|
36
|
+
visible: options.visible ?? true
|
|
37
|
+
};
|
|
38
|
+
};
|
|
39
|
+
const cloneMonitor = (monitor) => {
|
|
40
|
+
if (monitor.mode === "list") return {
|
|
41
|
+
...monitor,
|
|
42
|
+
params: { ...monitor.params },
|
|
43
|
+
value: [...monitor.value]
|
|
44
|
+
};
|
|
45
|
+
return {
|
|
46
|
+
...monitor,
|
|
47
|
+
params: { ...monitor.params }
|
|
48
|
+
};
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
//#endregion
|
|
3
52
|
//#region src/core/project.ts
|
|
4
53
|
let nextAssetId = 0;
|
|
5
54
|
const createAssetId = () => `asset-${(++nextAssetId).toString(16)}`;
|
|
@@ -34,6 +83,7 @@ var Target = class {
|
|
|
34
83
|
#blocks = {};
|
|
35
84
|
#variables = {};
|
|
36
85
|
#lists = {};
|
|
86
|
+
#monitors = [];
|
|
37
87
|
#costumes = [];
|
|
38
88
|
#sounds = [];
|
|
39
89
|
constructor(isStage, name) {
|
|
@@ -49,22 +99,30 @@ var Target = class {
|
|
|
49
99
|
...blocks
|
|
50
100
|
};
|
|
51
101
|
}
|
|
52
|
-
createVariable(name, defaultValue = 0,
|
|
102
|
+
createVariable(name, defaultValue = 0, isCloudVariableOrOptions) {
|
|
103
|
+
const options = typeof isCloudVariableOrOptions === "boolean" ? { isCloudVariable: isCloudVariableOrOptions } : isCloudVariableOrOptions;
|
|
53
104
|
const id = createAssetId();
|
|
54
|
-
this.#variables[id] = isCloudVariable ? [
|
|
105
|
+
this.#variables[id] = options?.isCloudVariable ? [
|
|
55
106
|
name,
|
|
56
107
|
defaultValue,
|
|
57
108
|
true
|
|
58
109
|
] : [name, defaultValue];
|
|
110
|
+
if (options?.monitor) this.#monitors.push(createVariableMonitor(id, name, defaultValue, this.isStage ? null : this.name, options.monitor));
|
|
59
111
|
return {
|
|
60
112
|
id,
|
|
61
113
|
name,
|
|
62
|
-
type: "variable"
|
|
114
|
+
type: "variable",
|
|
115
|
+
get: () => valueBlock("data_variable", { fields: { VARIABLE: [name, id] } }),
|
|
116
|
+
set: (value) => block("data_setvariableto", {
|
|
117
|
+
inputs: { VALUE: fromPrimitiveSource(value) },
|
|
118
|
+
fields: { VARIABLE: [name, id] }
|
|
119
|
+
})
|
|
63
120
|
};
|
|
64
121
|
}
|
|
65
|
-
createList(name, defaultValue = []) {
|
|
122
|
+
createList(name, defaultValue = [], options) {
|
|
66
123
|
const id = createAssetId();
|
|
67
124
|
this.#lists[id] = [name, defaultValue];
|
|
125
|
+
if (options?.monitor) this.#monitors.push(createListMonitor(id, name, defaultValue, this.isStage ? null : this.name, options.monitor));
|
|
68
126
|
return {
|
|
69
127
|
id,
|
|
70
128
|
name,
|
|
@@ -85,6 +143,9 @@ var Target = class {
|
|
|
85
143
|
type: "sound"
|
|
86
144
|
};
|
|
87
145
|
}
|
|
146
|
+
get monitors() {
|
|
147
|
+
return this.#monitors;
|
|
148
|
+
}
|
|
88
149
|
toScratch() {
|
|
89
150
|
const costumes = this.#costumes.length > 0 ? this.#costumes : [{
|
|
90
151
|
name: this.name,
|
|
@@ -98,7 +159,8 @@ var Target = class {
|
|
|
98
159
|
lists: this.#lists,
|
|
99
160
|
sounds: this.#sounds,
|
|
100
161
|
currentCostume: this.currentCostume,
|
|
101
|
-
costumes
|
|
162
|
+
costumes,
|
|
163
|
+
comments: {}
|
|
102
164
|
};
|
|
103
165
|
if (this.isStage) return {
|
|
104
166
|
...target,
|
|
@@ -128,18 +190,17 @@ var Project = class {
|
|
|
128
190
|
}
|
|
129
191
|
toScratch() {
|
|
130
192
|
const targets = this.#targets.map((target) => target.toScratch());
|
|
131
|
-
|
|
132
|
-
const project = {
|
|
193
|
+
return {
|
|
133
194
|
targets,
|
|
195
|
+
monitors: this.#targets.flatMap((target) => target.monitors.map((monitor) => cloneMonitor(monitor))),
|
|
196
|
+
extensions: collectExtensions(targets),
|
|
134
197
|
meta: {
|
|
135
198
|
semver: "3.0.0",
|
|
136
|
-
agent: `Hikkaku | ${
|
|
199
|
+
agent: `Hikkaku | ${typeof navigator !== "undefined" ? navigator.userAgent : "unknown"}`
|
|
137
200
|
}
|
|
138
201
|
};
|
|
139
|
-
if (extensions.length > 0) project.extensions = extensions;
|
|
140
|
-
return project;
|
|
141
202
|
}
|
|
142
203
|
};
|
|
143
204
|
|
|
144
205
|
//#endregion
|
|
145
|
-
export { Project, Target, block, createBlocks, fromCostumeSource, fromPrimitiveSource, fromSoundSource, substack, valueBlock };
|
|
206
|
+
export { Project, Target, attachStack, block, createBlocks, fromCostumeSource, fromPrimitiveSource, fromSoundSource, substack, valueBlock };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "hikkaku",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"private": false,
|
|
6
6
|
"exports": {
|
|
@@ -30,15 +30,18 @@
|
|
|
30
30
|
"typecheck": "tsgo"
|
|
31
31
|
},
|
|
32
32
|
"devDependencies": {
|
|
33
|
+
"@turbowarp/packager": "^3.11.0",
|
|
33
34
|
"@types/bun": "latest",
|
|
34
|
-
"
|
|
35
|
-
"
|
|
35
|
+
"@typescript/native-preview": "^7.0.0-dev.20260127.1",
|
|
36
|
+
"rolldown": "1.0.0-beta.60",
|
|
37
|
+
"tsdown": "^0.20.0-beta.4"
|
|
36
38
|
},
|
|
37
39
|
"peerDependencies": {
|
|
38
40
|
"typescript": "^5"
|
|
39
41
|
},
|
|
40
42
|
"dependencies": {
|
|
41
|
-
"
|
|
43
|
+
"fflate": "^0.8.2",
|
|
44
|
+
"sb3-types": "^0.1.16",
|
|
42
45
|
"vite": "8.0.0-beta.8"
|
|
43
46
|
},
|
|
44
47
|
"repository": {
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import * as sb3 from "sb3-types";
|
|
2
|
+
|
|
3
|
+
//#region src/core/types.d.ts
|
|
4
|
+
type PrimitiveAvailableOnScratch = number | boolean | string;
|
|
5
|
+
type PrimitiveSource<T extends PrimitiveAvailableOnScratch> = T | HikkakuBlock;
|
|
6
|
+
interface VariableBase {
|
|
7
|
+
id: string;
|
|
8
|
+
name: string;
|
|
9
|
+
}
|
|
10
|
+
interface MonitorPosition {
|
|
11
|
+
x?: number | null;
|
|
12
|
+
y?: number | null;
|
|
13
|
+
}
|
|
14
|
+
type VariableMonitorMode = 'default' | 'large' | 'slider';
|
|
15
|
+
interface VariableMonitorOptions extends MonitorPosition {
|
|
16
|
+
visible?: boolean;
|
|
17
|
+
mode?: VariableMonitorMode;
|
|
18
|
+
sliderMin?: number;
|
|
19
|
+
sliderMax?: number;
|
|
20
|
+
isDiscrete?: boolean;
|
|
21
|
+
}
|
|
22
|
+
interface ListMonitorOptions extends MonitorPosition {
|
|
23
|
+
visible?: boolean;
|
|
24
|
+
width?: number;
|
|
25
|
+
height?: number;
|
|
26
|
+
}
|
|
27
|
+
interface CreateVariableOptions {
|
|
28
|
+
isCloudVariable?: boolean;
|
|
29
|
+
monitor?: VariableMonitorOptions;
|
|
30
|
+
}
|
|
31
|
+
interface CreateListOptions {
|
|
32
|
+
monitor?: ListMonitorOptions;
|
|
33
|
+
}
|
|
34
|
+
interface VariableReference extends VariableBase {
|
|
35
|
+
type: 'variable';
|
|
36
|
+
}
|
|
37
|
+
interface VariableDefinition extends VariableReference {
|
|
38
|
+
get(): HikkakuBlock;
|
|
39
|
+
set(value: PrimitiveSource<number | string>): HikkakuBlock;
|
|
40
|
+
}
|
|
41
|
+
interface ListReference extends VariableBase {
|
|
42
|
+
type: 'list';
|
|
43
|
+
}
|
|
44
|
+
interface CostumeReference {
|
|
45
|
+
name: string;
|
|
46
|
+
type: 'costume';
|
|
47
|
+
}
|
|
48
|
+
type CostumeSource = PrimitiveSource<string> | CostumeReference;
|
|
49
|
+
interface SoundReference {
|
|
50
|
+
name: string;
|
|
51
|
+
type: 'sound';
|
|
52
|
+
}
|
|
53
|
+
type SoundSource = PrimitiveSource<string> | SoundReference;
|
|
54
|
+
interface HikkakuBlock {
|
|
55
|
+
isBlock: true;
|
|
56
|
+
id: string;
|
|
57
|
+
}
|
|
58
|
+
//#endregion
|
|
59
|
+
//#region src/core/block-helper.d.ts
|
|
60
|
+
declare const fromPrimitiveSource: <T extends PrimitiveAvailableOnScratch>(source: PrimitiveSource<T>) => sb3.Input;
|
|
61
|
+
declare const fromCostumeSource: (source: CostumeSource) => sb3.Input;
|
|
62
|
+
declare const fromSoundSource: (source: SoundSource) => sb3.Input;
|
|
63
|
+
//#endregion
|
|
64
|
+
//#region src/core/composer.d.ts
|
|
65
|
+
type Handler = () => void;
|
|
66
|
+
interface BlockInit {
|
|
67
|
+
inputs?: Record<string, sb3.Input>;
|
|
68
|
+
fields?: Record<string, sb3.Fields>;
|
|
69
|
+
topLevel?: boolean;
|
|
70
|
+
mutation?: sb3.Mutation;
|
|
71
|
+
isShadow?: boolean;
|
|
72
|
+
isValue?: boolean;
|
|
73
|
+
}
|
|
74
|
+
declare const block: (opcode: string, init: BlockInit) => HikkakuBlock;
|
|
75
|
+
declare const valueBlock: (opcode: string, init: BlockInit) => HikkakuBlock;
|
|
76
|
+
declare const substack: (handler: Handler) => string | null;
|
|
77
|
+
declare const attachStack: (parentId: string, handler?: Handler) => string | null;
|
|
78
|
+
declare const createBlocks: (handler: Handler) => Record<string, sb3.Block>;
|
|
79
|
+
//#endregion
|
|
80
|
+
//#region src/core/monitors.d.ts
|
|
81
|
+
interface VariableMonitor {
|
|
82
|
+
id: string;
|
|
83
|
+
mode: 'default' | 'large' | 'slider';
|
|
84
|
+
opcode: 'data_variable';
|
|
85
|
+
params: {
|
|
86
|
+
VARIABLE: string;
|
|
87
|
+
};
|
|
88
|
+
spriteName: string | null;
|
|
89
|
+
value: sb3.ScalarVal;
|
|
90
|
+
sliderMin: number;
|
|
91
|
+
sliderMax: number;
|
|
92
|
+
isDiscrete: boolean;
|
|
93
|
+
x: number | null;
|
|
94
|
+
y: number | null;
|
|
95
|
+
width: number;
|
|
96
|
+
height: number;
|
|
97
|
+
visible: boolean;
|
|
98
|
+
}
|
|
99
|
+
interface ListMonitor {
|
|
100
|
+
id: string;
|
|
101
|
+
mode: 'list';
|
|
102
|
+
opcode: 'data_listcontents';
|
|
103
|
+
params: {
|
|
104
|
+
LIST: string;
|
|
105
|
+
};
|
|
106
|
+
spriteName: string | null;
|
|
107
|
+
value: sb3.ScalarVal[];
|
|
108
|
+
x: number | null;
|
|
109
|
+
y: number | null;
|
|
110
|
+
width: number;
|
|
111
|
+
height: number;
|
|
112
|
+
visible: boolean;
|
|
113
|
+
}
|
|
114
|
+
type Monitor = VariableMonitor | ListMonitor;
|
|
115
|
+
//#endregion
|
|
116
|
+
//#region src/core/project.d.ts
|
|
117
|
+
declare class Target<IsStage extends boolean = boolean> {
|
|
118
|
+
#private;
|
|
119
|
+
readonly isStage: IsStage;
|
|
120
|
+
readonly name: IsStage extends true ? 'Stage' : string;
|
|
121
|
+
currentCostume: number;
|
|
122
|
+
constructor(isStage: IsStage, name: IsStage extends true ? 'Stage' : string);
|
|
123
|
+
run(handler: (target: Target<IsStage>) => void): void;
|
|
124
|
+
createVariable(name: string, defaultValue?: sb3.ScalarVal, isCloudVariableOrOptions?: boolean | CreateVariableOptions): VariableDefinition;
|
|
125
|
+
createList(name: string, defaultValue?: sb3.ScalarVal[], options?: CreateListOptions): ListReference;
|
|
126
|
+
addCostume(costume: sb3.Costume): CostumeReference;
|
|
127
|
+
addSound(sound: sb3.Sound): SoundReference;
|
|
128
|
+
get monitors(): readonly Monitor[];
|
|
129
|
+
toScratch(): IsStage extends true ? sb3.Stage : sb3.Sprite;
|
|
130
|
+
}
|
|
131
|
+
declare class Project {
|
|
132
|
+
#private;
|
|
133
|
+
readonly stage: Target<true>;
|
|
134
|
+
constructor();
|
|
135
|
+
createSprite(name: string): Target<false>;
|
|
136
|
+
toScratch(): sb3.ScratchProject;
|
|
137
|
+
}
|
|
138
|
+
//#endregion
|
|
139
|
+
export { SoundReference as C, VariableMonitorMode as D, VariableDefinition as E, VariableMonitorOptions as O, PrimitiveSource as S, VariableBase as T, HikkakuBlock as _, attachStack as a, MonitorPosition as b, substack as c, fromPrimitiveSource as d, fromSoundSource as f, CreateVariableOptions as g, CreateListOptions as h, Handler as i, VariableReference as k, valueBlock as l, CostumeSource as m, Target as n, block as o, CostumeReference as p, BlockInit as r, createBlocks as s, Project as t, fromCostumeSource as u, ListMonitorOptions as v, SoundSource as w, PrimitiveAvailableOnScratch as x, ListReference as y };
|
package/vite/index.mjs
CHANGED
|
@@ -1,29 +1,93 @@
|
|
|
1
|
+
import { zip } from "fflate";
|
|
2
|
+
import { mkdir, rm, writeFile } from "node:fs/promises";
|
|
3
|
+
import * as path from "node:path";
|
|
4
|
+
import { pathToFileURL } from "node:url";
|
|
1
5
|
import { createServerModuleRunner } from "vite";
|
|
2
6
|
|
|
3
7
|
//#region src/vite/index.ts
|
|
4
8
|
const BASE_URL = "https://scratchfoundation.github.io/scratch-gui/";
|
|
9
|
+
const VIRTUAL_MODULE_IDS = { project: "/@virtual/hikkaku-project" };
|
|
5
10
|
function hikkaku(init) {
|
|
6
11
|
let runner = null;
|
|
7
12
|
return {
|
|
8
13
|
name: "vite-plugin-hikkaku",
|
|
9
|
-
config() {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
+
config(config, env) {
|
|
15
|
+
if (env.command === "build") (config.plugins?.find((p) => p && typeof p === "object" && "name" in p && p.name === "vite-plugin-turbowarp-packager"))?.api.setEntry(path.join(process.cwd(), "dist", "project.sb3"));
|
|
16
|
+
return {
|
|
17
|
+
environments: { hikkaku: { build: { rolldownOptions: {
|
|
18
|
+
input: init.entry,
|
|
19
|
+
output: {
|
|
20
|
+
entryFileNames: "project.mjs",
|
|
21
|
+
format: "es"
|
|
22
|
+
}
|
|
23
|
+
} } } },
|
|
24
|
+
builder: {}
|
|
25
|
+
};
|
|
26
|
+
},
|
|
27
|
+
async buildApp(builder) {
|
|
28
|
+
const env = builder.environments.hikkaku;
|
|
29
|
+
if (!env) throw new Error("Hikkaku environment is not configured.");
|
|
30
|
+
await builder.build(env);
|
|
31
|
+
},
|
|
32
|
+
async generateBundle(_options, bundle) {
|
|
33
|
+
const tmpDir = path.join(process.cwd(), "dist", ".tmp");
|
|
34
|
+
for (const [filePath, file] of Object.entries(bundle)) if (file.type === "chunk") {
|
|
35
|
+
const fullPath = path.join(tmpDir, filePath);
|
|
36
|
+
await mkdir(path.dirname(fullPath), { recursive: true });
|
|
37
|
+
await writeFile(fullPath, file.code);
|
|
38
|
+
}
|
|
39
|
+
const { default: project } = await import(pathToFileURL(path.join(process.cwd(), "dist/.tmp", "project.mjs")).href);
|
|
40
|
+
const projectJSON = project.toScratch();
|
|
41
|
+
const zipData = await new Promise((resolve, reject) => {
|
|
42
|
+
zip({ "project.json": new TextEncoder().encode(JSON.stringify(projectJSON)) }, (err, data) => {
|
|
43
|
+
if (err) reject(err);
|
|
44
|
+
else resolve(data);
|
|
45
|
+
});
|
|
46
|
+
});
|
|
47
|
+
this.emitFile({
|
|
48
|
+
type: "asset",
|
|
49
|
+
fileName: "project.sb3",
|
|
50
|
+
name: "project.sb3",
|
|
51
|
+
source: zipData
|
|
52
|
+
});
|
|
53
|
+
this.emitFile({
|
|
54
|
+
type: "asset",
|
|
55
|
+
fileName: "project.json",
|
|
56
|
+
name: "project.json",
|
|
57
|
+
source: JSON.stringify(projectJSON, null, 2)
|
|
58
|
+
});
|
|
59
|
+
await rm(tmpDir, {
|
|
60
|
+
recursive: true,
|
|
61
|
+
force: true
|
|
62
|
+
});
|
|
14
63
|
},
|
|
15
64
|
resolveId(source) {
|
|
16
65
|
if (source === "/@virtual/hikkaku-client") return source;
|
|
66
|
+
if (source === VIRTUAL_MODULE_IDS.project) return source;
|
|
17
67
|
},
|
|
18
|
-
load(id) {
|
|
68
|
+
async load(id) {
|
|
19
69
|
if (id === "/@virtual/hikkaku-client") return `
|
|
20
70
|
import 'hikkaku/client'
|
|
21
71
|
`;
|
|
72
|
+
if (id === VIRTUAL_MODULE_IDS.project) {
|
|
73
|
+
if (this.environment.mode === "dev") {
|
|
74
|
+
if (!runner) throw new Error("Module runner is not initialized.");
|
|
75
|
+
const project = (await runner.import(init.entry)).default;
|
|
76
|
+
return `
|
|
77
|
+
export default ${JSON.stringify(project.toScratch())}
|
|
78
|
+
`;
|
|
79
|
+
} else if (this.environment.mode === "build") {
|
|
80
|
+
const projectJSON = await import(pathToFileURL(path.join(process.cwd(), "dist", "project.json")).href, { with: { type: "json" } });
|
|
81
|
+
return `
|
|
82
|
+
export default ${JSON.stringify(projectJSON)}
|
|
83
|
+
`;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
22
86
|
},
|
|
23
87
|
async hotUpdate(options) {
|
|
24
88
|
if (this.environment.name !== "hikkaku") return;
|
|
25
89
|
if (!runner) throw new Error("Module runner is not initialized.");
|
|
26
|
-
const project = (await runner.import(
|
|
90
|
+
const project = (await runner.import(init.entry)).default;
|
|
27
91
|
options.server.environments.client.hot.send("hikkaku:project", project.toScratch());
|
|
28
92
|
},
|
|
29
93
|
async configureServer(server) {
|
package/project-BSiPw18H.d.mts
DELETED
|
@@ -1,73 +0,0 @@
|
|
|
1
|
-
import * as sb3 from "sb3-types";
|
|
2
|
-
|
|
3
|
-
//#region src/core/types.d.ts
|
|
4
|
-
type PrimitiveAvailableOnScratch = number | boolean | string;
|
|
5
|
-
type PrimitiveSource<T extends PrimitiveAvailableOnScratch> = T | HikkakuBlock;
|
|
6
|
-
interface VariableBase {
|
|
7
|
-
id: string;
|
|
8
|
-
name: string;
|
|
9
|
-
}
|
|
10
|
-
interface VariableReference extends VariableBase {
|
|
11
|
-
type: 'variable';
|
|
12
|
-
}
|
|
13
|
-
interface ListReference extends VariableBase {
|
|
14
|
-
type: 'list';
|
|
15
|
-
}
|
|
16
|
-
interface CostumeReference {
|
|
17
|
-
name: string;
|
|
18
|
-
type: 'costume';
|
|
19
|
-
}
|
|
20
|
-
type CostumeSource = PrimitiveSource<string> | CostumeReference;
|
|
21
|
-
interface SoundReference {
|
|
22
|
-
name: string;
|
|
23
|
-
type: 'sound';
|
|
24
|
-
}
|
|
25
|
-
type SoundSource = PrimitiveSource<string> | SoundReference;
|
|
26
|
-
interface HikkakuBlock {
|
|
27
|
-
isBlock: true;
|
|
28
|
-
id: string;
|
|
29
|
-
}
|
|
30
|
-
//#endregion
|
|
31
|
-
//#region src/core/block-helper.d.ts
|
|
32
|
-
declare const fromPrimitiveSource: <T extends PrimitiveAvailableOnScratch>(source: PrimitiveSource<T>) => sb3.Input;
|
|
33
|
-
declare const fromCostumeSource: (source: CostumeSource) => sb3.Input;
|
|
34
|
-
declare const fromSoundSource: (source: SoundSource) => sb3.Input;
|
|
35
|
-
//#endregion
|
|
36
|
-
//#region src/core/composer.d.ts
|
|
37
|
-
type Handler = () => void;
|
|
38
|
-
interface BlockInit {
|
|
39
|
-
inputs?: Record<string, sb3.Input>;
|
|
40
|
-
fields?: Record<string, sb3.Fields>;
|
|
41
|
-
topLevel?: boolean;
|
|
42
|
-
mutation?: sb3.Mutation;
|
|
43
|
-
isShadow?: boolean;
|
|
44
|
-
isValue?: boolean;
|
|
45
|
-
}
|
|
46
|
-
declare const block: (opcode: string, init: BlockInit) => HikkakuBlock;
|
|
47
|
-
declare const valueBlock: (opcode: string, init: BlockInit) => HikkakuBlock;
|
|
48
|
-
declare const substack: (handler: Handler) => string | null;
|
|
49
|
-
declare const createBlocks: (handler: Handler) => Record<string, sb3.Block>;
|
|
50
|
-
//#endregion
|
|
51
|
-
//#region src/core/project.d.ts
|
|
52
|
-
declare class Target<IsStage extends boolean = boolean> {
|
|
53
|
-
#private;
|
|
54
|
-
readonly isStage: IsStage;
|
|
55
|
-
readonly name: IsStage extends true ? 'Stage' : string;
|
|
56
|
-
currentCostume: number;
|
|
57
|
-
constructor(isStage: IsStage, name: IsStage extends true ? 'Stage' : string);
|
|
58
|
-
run(handler: (target: Target<IsStage>) => void): void;
|
|
59
|
-
createVariable(name: string, defaultValue?: sb3.ScalarVal, isCloudVariable?: boolean): VariableReference;
|
|
60
|
-
createList(name: string, defaultValue?: sb3.ScalarVal[]): ListReference;
|
|
61
|
-
addCostume(costume: sb3.Costume): CostumeReference;
|
|
62
|
-
addSound(sound: sb3.Sound): SoundReference;
|
|
63
|
-
toScratch(): IsStage extends true ? sb3.Stage : sb3.Sprite;
|
|
64
|
-
}
|
|
65
|
-
declare class Project {
|
|
66
|
-
#private;
|
|
67
|
-
readonly stage: Target<true>;
|
|
68
|
-
constructor();
|
|
69
|
-
createSprite(name: string): Target<false>;
|
|
70
|
-
toScratch(): sb3.ScratchProject;
|
|
71
|
-
}
|
|
72
|
-
//#endregion
|
|
73
|
-
export { PrimitiveSource as _, block as a, VariableBase as b, valueBlock as c, fromSoundSource as d, CostumeReference as f, PrimitiveAvailableOnScratch as g, ListReference as h, Handler as i, fromCostumeSource as l, HikkakuBlock as m, Target as n, createBlocks as o, CostumeSource as p, BlockInit as r, substack as s, Project as t, fromPrimitiveSource as u, SoundReference as v, VariableReference as x, SoundSource as y };
|