@lingbi/studio 0.8.1 → 0.8.3

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/index.js CHANGED
@@ -33618,25 +33618,21 @@ class PlyLoader {
33618
33618
  }
33619
33619
  }
33620
33620
  class SimulationSnapshotFactory {
33621
- create(stone, base) {
33621
+ constructor(scheduler) {
33622
+ this.scheduler = scheduler;
33623
+ }
33624
+ scheduler;
33625
+ // Allocation granularity, not a per-task limit: tasks copy until their time budget expires.
33626
+ static CHUNK_BYTES = 4 * 1024 * 1024;
33627
+ static ATTRIBUTE_CHUNK_BYTES = 256 * 1024;
33628
+ async create(stone, base) {
33622
33629
  const started = performance.now();
33623
33630
  const stoneRoot = this.root(stone);
33624
33631
  const baseRoot = this.root(base);
33625
33632
  stoneRoot.updateMatrixWorld(true);
33626
33633
  if (baseRoot !== stoneRoot) baseRoot.updateMatrixWorld(true);
33627
- const stoneInput = this.collect(stone);
33628
- const baseInput = this.collect(base);
33629
- const bounds = new Box3();
33630
- const point = new Vector3();
33631
- for (let index = 0; index < stoneInput.positions.length; index += 3) {
33632
- bounds.expandByPoint(point.fromArray(stoneInput.positions, index));
33633
- }
33634
- const origin = bounds.getCenter(new Vector3()).toArray();
33635
- for (const input of [stoneInput, baseInput]) {
33636
- for (let index = 0; index < input.positions.length; index++) {
33637
- input.positions[index] = Number(input.positions[index]) - Number(origin[index % 3]);
33638
- }
33639
- }
33634
+ const stoneInput = await this.collect(stone);
33635
+ const baseInput = await this.collect(base);
33640
33636
  return {
33641
33637
  stone: stoneInput,
33642
33638
  base: baseInput,
@@ -33648,54 +33644,126 @@ class SimulationSnapshotFactory {
33648
33644
  while (root.parent !== null) root = root.parent;
33649
33645
  return root;
33650
33646
  }
33651
- collect(root) {
33652
- const positions = [];
33653
- const indices = [];
33654
- const point = new Vector3();
33655
- root.traverse((object) => {
33656
- if (!(object instanceof Mesh)) return;
33647
+ async collect(root) {
33648
+ const result = [];
33649
+ const objects = [root];
33650
+ for (const object of objects) {
33651
+ const pause = this.scheduler.checkpoint();
33652
+ if (pause !== void 0) await pause;
33653
+ objects.push(...object.children);
33654
+ if (!(object instanceof Mesh)) continue;
33657
33655
  const mesh = object;
33658
- if (!mesh.geometry.hasAttribute("position")) {
33659
- throw new Error("The model has no triangular position attribute.");
33660
- }
33661
33656
  const position = mesh.geometry.getAttribute("position");
33662
- if (position.itemSize < 3) {
33657
+ if (!mesh.geometry.hasAttribute("position") || position.itemSize < 3) {
33663
33658
  throw new Error("The model has no triangular position attribute.");
33664
33659
  }
33665
- const determinant = mesh.matrixWorld.determinant();
33666
- if (!Number.isFinite(determinant) || determinant === 0) {
33667
- throw new Error("The model transform is singular or nonfinite.");
33668
- }
33669
- const offset = positions.length / 3;
33670
- for (let index = 0; index < position.count; index++) {
33671
- point.set(position.getX(index), position.getY(index), position.getZ(index)).applyMatrix4(mesh.matrixWorld);
33672
- if (!point.toArray().every(Number.isFinite)) {
33673
- throw new Error("The model contains nonfinite positions.");
33674
- }
33675
- positions.push(point.x, point.y, point.z);
33676
- }
33677
- const sourceIndex = mesh.geometry.index;
33678
- const count = sourceIndex?.count ?? position.count;
33679
- if (count === 0 || count % 3 !== 0) {
33680
- throw new Error("The model has incomplete triangles.");
33681
- }
33682
- for (let index = 0; index < count; index += 3) {
33683
- const a = sourceIndex?.getX(index) ?? index;
33684
- const b = sourceIndex?.getX(index + 1) ?? index + 1;
33685
- const c = sourceIndex?.getX(index + 2) ?? index + 2;
33686
- if ([a, b, c].some(
33687
- (value) => !Number.isSafeInteger(value) || value < 0 || value >= position.count
33688
- )) {
33689
- throw new Error("The model contains an invalid triangle index.");
33690
- }
33691
- indices.push(
33692
- offset + a,
33693
- offset + (determinant < 0 ? c : b),
33694
- offset + (determinant < 0 ? b : c)
33695
- );
33660
+ const matrixWorld = mesh.matrixWorld.toArray();
33661
+ if (!matrixWorld.every(Number.isFinite)) {
33662
+ throw new Error("The model transform is nonfinite.");
33663
+ }
33664
+ const positions = await this.copyPositions(position);
33665
+ const index = mesh.geometry.index;
33666
+ const indices = index === null ? null : await this.copyIndices(index);
33667
+ result.push({ positions, indices, vertexCount: position.count, matrixWorld });
33668
+ }
33669
+ return result;
33670
+ }
33671
+ async copyPositions(attribute) {
33672
+ const result = [];
33673
+ const source = attribute.array;
33674
+ const packed = !("isInterleavedBufferAttribute" in attribute) && !attribute.normalized && attribute.itemSize === 3 && (source instanceof Float32Array || source instanceof Float64Array);
33675
+ const vertexChunk = Math.floor(
33676
+ (packed ? SimulationSnapshotFactory.CHUNK_BYTES : SimulationSnapshotFactory.ATTRIBUTE_CHUNK_BYTES) / 24
33677
+ );
33678
+ for (let start = 0; start < attribute.count; start += vertexChunk) {
33679
+ const pause = this.scheduler.checkpoint();
33680
+ if (pause !== void 0) await pause;
33681
+ const end = Math.min(attribute.count, start + vertexChunk);
33682
+ if (packed) {
33683
+ result.push(source.slice(start * 3, end * 3));
33684
+ } else {
33685
+ const values = new Float64Array((end - start) * 3);
33686
+ for (let index = start; index < end; index++) {
33687
+ const offset = (index - start) * 3;
33688
+ values[offset] = attribute.getX(index);
33689
+ values[offset + 1] = attribute.getY(index);
33690
+ values[offset + 2] = attribute.getZ(index);
33691
+ }
33692
+ result.push(values);
33693
+ }
33694
+ }
33695
+ return result;
33696
+ }
33697
+ async copyIndices(attribute) {
33698
+ const result = [];
33699
+ const source = attribute.array;
33700
+ const packed = attribute.itemSize === 1 && !attribute.normalized && (source instanceof Uint16Array || source instanceof Uint32Array);
33701
+ const indexChunk = (packed ? SimulationSnapshotFactory.CHUNK_BYTES : SimulationSnapshotFactory.ATTRIBUTE_CHUNK_BYTES) / 8;
33702
+ for (let start = 0; start < attribute.count; start += indexChunk) {
33703
+ const pause = this.scheduler.checkpoint();
33704
+ if (pause !== void 0) await pause;
33705
+ const end = Math.min(attribute.count, start + indexChunk);
33706
+ if (packed) result.push(source.slice(start, end));
33707
+ else {
33708
+ const values = new Float64Array(end - start);
33709
+ for (let index = start; index < end; index++) {
33710
+ values[index - start] = attribute.getX(index);
33711
+ }
33712
+ result.push(values);
33696
33713
  }
33714
+ }
33715
+ return result;
33716
+ }
33717
+ }
33718
+ class SimulationTaskScheduler {
33719
+ constructor(signal, started) {
33720
+ this.signal = signal;
33721
+ this.started = started;
33722
+ }
33723
+ signal;
33724
+ started;
33725
+ sliceStarted = performance.now();
33726
+ async waitForPaint() {
33727
+ await this.wait(true);
33728
+ this.checkActive();
33729
+ this.sliceStarted = performance.now();
33730
+ }
33731
+ checkpoint() {
33732
+ this.checkActive();
33733
+ if (performance.now() - this.sliceStarted < 4) return void 0;
33734
+ return this.yieldTask();
33735
+ }
33736
+ async yieldTask() {
33737
+ await this.wait(false);
33738
+ this.checkActive();
33739
+ this.sliceStarted = performance.now();
33740
+ }
33741
+ checkActive() {
33742
+ this.signal.throwIfAborted();
33743
+ if (performance.now() - this.started >= 3e4) {
33744
+ throw new Error("Simulation timed out during snapshot capture.");
33745
+ }
33746
+ }
33747
+ wait(paint) {
33748
+ this.checkActive();
33749
+ return new Promise((resolve) => {
33750
+ let frame;
33751
+ let task;
33752
+ const finish = () => {
33753
+ if (frame !== void 0) cancelAnimationFrame(frame);
33754
+ clearTimeout(task);
33755
+ this.signal.removeEventListener("abort", finish);
33756
+ resolve();
33757
+ };
33758
+ this.signal.addEventListener("abort", finish, { once: true });
33759
+ if (paint) {
33760
+ frame = requestAnimationFrame(() => {
33761
+ clearTimeout(task);
33762
+ task = setTimeout(finish, 0);
33763
+ });
33764
+ }
33765
+ task = setTimeout(finish, paint ? 100 : 0);
33697
33766
  });
33698
- return { positions: Float64Array.from(positions), indices: Uint32Array.from(indices) };
33699
33767
  }
33700
33768
  }
33701
33769
  class StabilityMessages {
@@ -33703,7 +33771,7 @@ class StabilityMessages {
33703
33771
  if (!this.record(value) || !this.token(value["token"]) || !this.record(value["input"]))
33704
33772
  return void 0;
33705
33773
  const input = value["input"];
33706
- if (!this.mesh(input["stone"]) || !this.mesh(input["base"]) || !this.nonnegative(input["captureMilliseconds"]))
33774
+ if (!this.role(input["stone"]) || !this.role(input["base"]) || !this.nonnegative(input["captureMilliseconds"]))
33707
33775
  return void 0;
33708
33776
  return value;
33709
33777
  }
@@ -33736,8 +33804,20 @@ class StabilityMessages {
33736
33804
  }
33737
33805
  return value["outcome"] !== "supported" || Number(value["margin"]) > 1e-6 && Number(value["residual"]) <= 1e-7 && Number(value["contactCount"]) > 0;
33738
33806
  }
33807
+ role(value) {
33808
+ return this.array(value) && value.every((mesh) => this.mesh(mesh));
33809
+ }
33810
+ array(value) {
33811
+ return Array.isArray(value);
33812
+ }
33739
33813
  mesh(value) {
33740
- return this.record(value) && value["positions"] instanceof Float64Array && value["indices"] instanceof Uint32Array;
33814
+ return this.record(value) && Array.isArray(value["positions"]) && value["positions"].every(
33815
+ (chunk) => chunk instanceof Float32Array || chunk instanceof Float64Array
33816
+ ) && (value["indices"] === null || Array.isArray(value["indices"]) && value["indices"].every(
33817
+ (chunk) => chunk instanceof Uint16Array || chunk instanceof Uint32Array || chunk instanceof Float64Array
33818
+ )) && this.nonnegative(value["vertexCount"]) && Number.isSafeInteger(value["vertexCount"]) && Array.isArray(value["matrixWorld"]) && value["matrixWorld"].length === 16 && value["matrixWorld"].every(
33819
+ (entry) => typeof entry === "number" && Number.isFinite(entry)
33820
+ );
33741
33821
  }
33742
33822
  record(value) {
33743
33823
  return typeof value === "object" && value !== null;
@@ -33753,7 +33833,7 @@ class StabilityWorkerFactory {
33753
33833
  create() {
33754
33834
  return new Worker(new URL(
33755
33835
  /* @vite-ignore */
33756
- "" + new URL("assets/StabilityWorkerEntry-CXCNaUyw.js", import.meta.url).href,
33836
+ "" + new URL("assets/StabilityWorkerEntry-L0_8iZmQ.js", import.meta.url).href,
33757
33837
  import.meta.url
33758
33838
  ), {
33759
33839
  type: "module"
@@ -33802,12 +33882,16 @@ class StabilityWorkerClient {
33802
33882
  this.fail("Simulation Worker message could not be read.");
33803
33883
  });
33804
33884
  }
33805
- worker.postMessage({ token, input }, [
33806
- input.stone.positions.buffer,
33807
- input.stone.indices.buffer,
33808
- input.base.positions.buffer,
33809
- input.base.indices.buffer
33810
- ]);
33885
+ const transfer = [];
33886
+ for (const role of [input.stone, input.base]) {
33887
+ for (const mesh of role) {
33888
+ for (const chunk of mesh.positions) transfer.push(chunk.buffer);
33889
+ if (mesh.indices !== null) {
33890
+ for (const chunk of mesh.indices) transfer.push(chunk.buffer);
33891
+ }
33892
+ }
33893
+ }
33894
+ worker.postMessage({ token, input }, transfer);
33811
33895
  } catch {
33812
33896
  this.fail("Simulation Worker could not start.");
33813
33897
  }
@@ -35826,6 +35910,7 @@ class Renderer {
35826
35910
  contextLost = false;
35827
35911
  booleanOperationInProgress = false;
35828
35912
  simulationInProgress = false;
35913
+ simulationCapture;
35829
35914
  simulationToken = 0;
35830
35915
  simulationWorker = new StabilityWorkerClient();
35831
35916
  booleanOperationWorkerClient;
@@ -36359,6 +36444,8 @@ class Renderer {
36359
36444
  const started = performance.now();
36360
36445
  const token = ++this.simulationToken;
36361
36446
  const revision = this.sceneRevision;
36447
+ const capture = new AbortController();
36448
+ this.simulationCapture = capture;
36362
36449
  this.simulationInProgress = true;
36363
36450
  try {
36364
36451
  this.gizmoController.end();
@@ -36387,7 +36474,13 @@ class Renderer {
36387
36474
  console.info("[stone-stability]", response2);
36388
36475
  return response2;
36389
36476
  }
36390
- const input = new SimulationSnapshotFactory().create(stone.object, base.object);
36477
+ const scheduler = new SimulationTaskScheduler(capture.signal, started);
36478
+ await scheduler.waitForPaint();
36479
+ const input = await new SimulationSnapshotFactory(scheduler).create(
36480
+ stone.object,
36481
+ base.object
36482
+ );
36483
+ capture.signal.throwIfAborted();
36391
36484
  const response = await this.simulationWorker.run(input, started);
36392
36485
  if (token !== this.simulationToken || revision !== this.sceneRevision || !this.isDualModelSceneReady()) {
36393
36486
  return { status: "cancelled" };
@@ -36395,11 +36488,13 @@ class Renderer {
36395
36488
  console.info("[stone-stability]", response);
36396
36489
  return response;
36397
36490
  } catch (error2) {
36491
+ if (capture.signal.aborted) return { status: "cancelled" };
36398
36492
  const reason = error2 instanceof Error ? error2.message : "Simulation failed.";
36399
36493
  console.info("[stone-stability]", { status: "failed", reason });
36400
36494
  return { status: "failed", reason };
36401
36495
  } finally {
36402
36496
  if (token === this.simulationToken) {
36497
+ this.simulationCapture = void 0;
36403
36498
  this.simulationInProgress = false;
36404
36499
  this.notifyWorkspaceState();
36405
36500
  }
@@ -36407,6 +36502,8 @@ class Renderer {
36407
36502
  }
36408
36503
  cancelSimulation() {
36409
36504
  const wasActive = this.simulationInProgress;
36505
+ this.simulationCapture?.abort();
36506
+ this.simulationCapture = void 0;
36410
36507
  this.simulationToken++;
36411
36508
  this.simulationInProgress = false;
36412
36509
  this.simulationWorker.cancel();
@@ -39914,7 +40011,7 @@ var StoneBasePoseMode = /* @__PURE__ */ ((StoneBasePoseMode2) => {
39914
40011
  StoneBasePoseMode2["PreservePose"] = "preserve-pose";
39915
40012
  return StoneBasePoseMode2;
39916
40013
  })(StoneBasePoseMode || {});
39917
- const studioWorkspaceStyles = ".lingbi-studio-workspace {\n position: relative;\n display: block;\n width: 100%;\n height: 100%;\n min-width: 0;\n min-height: 0;\n overflow: hidden;\n}\n\n.lingbi-studio-viewport {\n width: 100%;\n height: 100%;\n min-width: 0;\n min-height: 0;\n overflow: hidden;\n}\n\n.lingbi-studio-viewport > canvas {\n display: block;\n width: 100%;\n height: 100%;\n min-width: 0;\n min-height: 0;\n overflow: hidden;\n background: #3d3f41;\n}\n\n.lingbi-studio-boolean-operation-overlay {\n position: absolute;\n z-index: 3;\n inset: 0;\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n gap: 12px;\n background: rgba(17, 17, 17, 0.32);\n color: #ffffff;\n cursor: wait;\n}\n\n.lingbi-studio-boolean-operation-overlay[hidden] {\n display: none;\n}\n\n.lingbi-studio-boolean-edit-confirmation {\n position: absolute;\n z-index: 4;\n top: 50%;\n left: 50%;\n display: grid;\n grid-template-columns: 1fr 1fr;\n gap: 12px;\n width: min(420px, calc(100% - 32px));\n padding: 24px;\n border-radius: 8px;\n background: #ffffff;\n box-shadow: 0 8px 32px rgba(0, 0, 0, 0.22);\n transform: translate(-50%, -50%);\n box-sizing: border-box;\n}\n\n.lingbi-studio-boolean-edit-confirmation[hidden] {\n display: none;\n}\n\n.lingbi-studio-boolean-edit-confirmation-text {\n grid-column: 1 / -1;\n margin: 0 0 8px;\n color: #171717;\n font-size: 16px;\n font-weight: 500;\n line-height: 24px;\n text-align: center;\n}\n\n.lingbi-studio-boolean-edit-confirmation-cancel,\n.lingbi-studio-boolean-edit-confirmation-continue {\n height: 40px;\n border: 1px solid #d9d9d9;\n border-radius: 4px;\n font: inherit;\n font-size: 16px;\n font-weight: 500;\n line-height: 24px;\n cursor: pointer;\n}\n\n.lingbi-studio-boolean-edit-confirmation-cancel {\n background: #ffffff;\n color: #171717;\n}\n\n.lingbi-studio-boolean-edit-confirmation-continue {\n border-color: #f27c38;\n background: #f27c38;\n color: #ffffff;\n}\n\n.lingbi-studio-boolean-operation-spinner {\n width: 32px;\n height: 32px;\n border: 3px solid rgba(255, 105, 0, 0.28);\n border-top-color: #ff6900;\n border-radius: 50%;\n animation: lingbi-studio-boolean-operation-spin 0.8s linear infinite;\n box-sizing: border-box;\n}\n\n.lingbi-studio-boolean-operation-status {\n font-size: 14px;\n font-weight: 500;\n line-height: 20px;\n letter-spacing: 0;\n}\n\n.lingbi-studio-bottom-toolbar {\n position: absolute;\n z-index: 1;\n bottom: 36px;\n left: 50%;\n display: grid;\n align-items: center;\n grid-template-columns: minmax(0, 1fr);\n width: min(120px, calc(100% - 32px));\n height: 64px;\n padding: 8px 12px;\n transform: translateX(-50%);\n box-sizing: border-box;\n border-radius: 8px;\n background: #f4f4f4;\n box-shadow: 0 4px 12px rgba(0, 0, 0, 0.14);\n}\n\n.lingbi-studio-bottom-toolbar.has-edit-control {\n grid-template-columns: repeat(4, minmax(0, 1fr));\n width: min(480px, calc(100% - 32px));\n}\n\n.lingbi-studio-bottom-toolbar.has-inventory-control {\n grid-template-columns: repeat(2, minmax(0, 1fr));\n width: min(240px, calc(100% - 32px));\n}\n\n.lingbi-studio-bottom-toolbar.has-edit-control.has-inventory-control {\n grid-template-columns: repeat(5, minmax(0, 1fr));\n width: min(600px, calc(100% - 32px));\n}\n\n.lingbi-studio-bottom-toolbar.has-model-control.has-inventory-control {\n grid-template-columns: repeat(3, minmax(0, 1fr));\n width: min(360px, calc(100% - 32px));\n}\n\n.lingbi-studio-bottom-toolbar.has-model-control.has-edit-control {\n grid-template-columns: repeat(5, minmax(0, 1fr));\n width: min(600px, calc(100% - 32px));\n}\n\n.lingbi-studio-bottom-toolbar.has-model-control.has-edit-control.has-inventory-control {\n grid-template-columns: repeat(6, minmax(0, 1fr));\n width: min(720px, calc(100% - 32px));\n}\n\n.lingbi-studio-model-visibility {\n position: absolute;\n z-index: 1;\n bottom: 108px;\n left: 50%;\n display: grid;\n gap: 8px;\n width: 144px;\n padding: 12px;\n border-radius: 8px;\n background: #f4f4f4;\n box-shadow: 0 4px 12px rgba(0, 0, 0, 0.14);\n transform: translateX(-240px);\n box-sizing: border-box;\n}\n\n.lingbi-studio-model-visibility[hidden] {\n display: none;\n}\n\n.lingbi-studio-model-visibility-title {\n color: #171717;\n font-size: 14px;\n font-weight: 600;\n line-height: 20px;\n}\n\n.lingbi-studio-model-visibility-row {\n display: grid;\n grid-template-columns: minmax(0, 1fr) auto;\n gap: 8px;\n align-items: center;\n color: #171717;\n font-size: 14px;\n line-height: 20px;\n}\n\n.lingbi-studio-model-visibility-button {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n appearance: none;\n width: 20px;\n height: 28px;\n padding: 0;\n border: 0;\n border-radius: 0;\n background: transparent;\n box-shadow: none;\n color: #171717;\n cursor: not-allowed;\n font: inherit;\n line-height: 0;\n outline: none;\n box-sizing: border-box;\n}\n\n.lingbi-studio-model-visibility-icon {\n display: block;\n width: 18px;\n height: 18px;\n flex: 0 0 auto;\n pointer-events: none;\n}\n\n.lingbi-studio-model-visibility-button:not(:disabled) {\n cursor: pointer;\n}\n\n.lingbi-studio-model-visibility-button:disabled {\n color: #8c8c8c;\n}\n\n.lingbi-studio-model-button.is-active,\n.lingbi-studio-model-button.is-active:disabled {\n color: #ff6900;\n font-weight: 600;\n}\n\n.lingbi-studio-boolean-button,\n.lingbi-studio-simulation-button,\n.lingbi-studio-download-button,\n.lingbi-studio-inventory-button,\n.lingbi-studio-edit-button,\n.lingbi-studio-model-button {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 100%;\n min-width: 0;\n height: 48px;\n padding: 0 8px;\n border: 0;\n border-radius: 4px;\n background: transparent;\n color: #171717;\n cursor: not-allowed;\n font: inherit;\n font-size: 18px;\n font-weight: 500;\n line-height: 24px;\n letter-spacing: 0;\n opacity: 1;\n box-sizing: border-box;\n white-space: nowrap;\n transition:\n background-color 140ms ease,\n color 140ms ease;\n}\n\n.lingbi-studio-operation-button-wrapper {\n display: block;\n width: 100%;\n min-width: 0;\n height: 48px;\n box-sizing: border-box;\n}\n\n.lingbi-studio-operation-button-wrapper > button:disabled {\n pointer-events: none;\n}\n\n.lingbi-studio-boolean-button[hidden],\n.lingbi-studio-simulation-button[hidden],\n.lingbi-studio-download-button[hidden],\n.lingbi-studio-inventory-button[hidden],\n.lingbi-studio-edit-button[hidden],\n.lingbi-studio-model-button[hidden] {\n display: none;\n}\n\n.lingbi-studio-boolean-button:not(:disabled),\n.lingbi-studio-simulation-button:not(:disabled),\n.lingbi-studio-download-button:not(:disabled),\n.lingbi-studio-inventory-button:not(:disabled),\n.lingbi-studio-edit-button:not(:disabled),\n.lingbi-studio-model-button:not(:disabled) {\n cursor: pointer;\n}\n\n.lingbi-studio-boolean-button:disabled,\n.lingbi-studio-simulation-button:disabled,\n.lingbi-studio-download-button:disabled,\n.lingbi-studio-inventory-button:disabled,\n.lingbi-studio-edit-button:disabled,\n.lingbi-studio-model-button:disabled,\n.lingbi-studio-model-visibility-button:disabled {\n background: transparent;\n color: #8c8c8c;\n opacity: 1;\n}\n\n.lingbi-studio-edit-button.is-active,\n.lingbi-studio-edit-button.is-active:disabled,\n.lingbi-studio-model-button.is-active,\n.lingbi-studio-model-button.is-active:disabled {\n color: #ff6900;\n font-weight: 600;\n}\n\n.lingbi-studio-download-status {\n position: absolute;\n z-index: 2;\n top: 24px;\n left: 50%;\n max-width: calc(100% - 24px);\n padding: 6px 10px;\n border: 1px solid #f4b5b0;\n border-radius: 4px;\n background: #fff1f0;\n color: #b42318;\n font-size: 13px;\n font-weight: 500;\n line-height: 20px;\n text-align: center;\n transform: translateX(-50%);\n box-sizing: border-box;\n}\n\n.lingbi-studio-download-status[hidden] {\n display: none;\n}\n\n.lingbi-studio-download-success {\n position: absolute;\n z-index: 2;\n top: 24px;\n left: 50%;\n max-width: calc(100% - 24px);\n padding: 8px 16px;\n border: 1px solid #86d39a;\n border-radius: 6px;\n background: #f0fff4;\n color: #176b32;\n font-size: 14px;\n font-weight: 500;\n line-height: 20px;\n text-align: center;\n transform: translateX(-50%);\n box-shadow: 0 4px 12px rgba(0, 0, 0, 0.12);\n box-sizing: border-box;\n}\n\n.lingbi-studio-download-success[hidden] {\n display: none;\n}\n\n.lingbi-studio-edit-status {\n position: absolute;\n z-index: 2;\n bottom: 112px;\n left: 50%;\n display: flex;\n align-items: center;\n gap: 12px;\n max-width: calc(100% - 24px);\n padding: 10px 14px;\n border: 0;\n border-radius: 8px;\n background: #e9e9e9;\n color: #171717;\n transform: translateX(-50%);\n box-sizing: border-box;\n}\n\n.lingbi-studio-edit-status[hidden] {\n display: none;\n}\n\n.lingbi-studio-edit-status-text {\n display: inline-block;\n min-width: 0;\n font-size: 20px;\n line-height: 28px;\n letter-spacing: 0;\n}\n\n.lingbi-studio-exit-button {\n display: inline-flex;\n flex: 0 0 auto;\n align-items: center;\n justify-content: center;\n height: 40px;\n padding: 0 18px;\n border: 1px solid transparent;\n border-radius: 8px;\n background: #f27c38;\n color: #ffffff;\n cursor: pointer;\n font: inherit;\n font-size: 18px;\n font-weight: 600;\n line-height: 24px;\n letter-spacing: 0;\n transition:\n background-color 140ms ease,\n color 140ms ease;\n}\n\n@media (hover: hover) and (pointer: fine) {\n .lingbi-studio-boolean-button:not(:disabled):hover,\n .lingbi-studio-simulation-button:not(:disabled):hover,\n .lingbi-studio-download-button:not(:disabled):hover,\n .lingbi-studio-inventory-button:not(:disabled):hover,\n .lingbi-studio-edit-button:not(:disabled):hover,\n .lingbi-studio-model-button:not(:disabled):hover {\n background: #e8e8e8;\n }\n\n .lingbi-studio-edit-button:not(:disabled):hover {\n color: #f06400;\n }\n\n .lingbi-studio-model-button:not(:disabled):hover {\n color: #f06400;\n }\n\n .lingbi-studio-model-visibility-button:not(:disabled):hover {\n background: transparent;\n }\n\n .lingbi-studio-exit-button:hover {\n background: #ff8a1f;\n }\n\n .lingbi-studio-boolean-edit-confirmation-cancel:hover {\n background: #f5f5f5;\n }\n\n .lingbi-studio-boolean-edit-confirmation-continue:hover {\n background: #ff8a1f;\n }\n}\n\n.lingbi-studio-boolean-button:not(:disabled):active,\n.lingbi-studio-download-button:not(:disabled):active,\n.lingbi-studio-inventory-button:not(:disabled):active,\n.lingbi-studio-edit-button:not(:disabled):active,\n.lingbi-studio-model-button:not(:disabled):active,\n.lingbi-studio-model-visibility-button:not(:disabled):active {\n background: transparent;\n}\n\n.lingbi-studio-exit-button:active {\n background: #e66d00;\n}\n\n@keyframes lingbi-studio-boolean-operation-spin {\n to {\n transform: rotate(360deg);\n }\n}\n\n@media (prefers-reduced-motion: reduce) {\n .lingbi-studio-boolean-button,\n .lingbi-studio-simulation-button,\n .lingbi-studio-download-button,\n .lingbi-studio-inventory-button,\n .lingbi-studio-edit-button,\n .lingbi-studio-model-button,\n .lingbi-studio-exit-button {\n transition: none;\n }\n\n .lingbi-studio-boolean-operation-spinner {\n animation: none;\n }\n}\n\n@media (max-width: 420px) {\n .lingbi-studio-bottom-toolbar.has-edit-control {\n width: calc(100% - 16px);\n padding: 8px 4px;\n }\n\n .lingbi-studio-bottom-toolbar.has-model-control.has-edit-control {\n width: calc(100% - 16px);\n }\n\n .lingbi-studio-bottom-toolbar.has-model-control.has-edit-control.has-inventory-control {\n width: calc(100% - 16px);\n }\n\n .lingbi-studio-model-visibility {\n left: 8px;\n transform: none;\n }\n\n .lingbi-studio-boolean-button,\n .lingbi-studio-simulation-button,\n .lingbi-studio-download-button,\n .lingbi-studio-inventory-button,\n .lingbi-studio-edit-button,\n .lingbi-studio-model-button {\n padding: 0 2px;\n font-size: 16px;\n }\n\n .lingbi-studio-edit-status-text {\n font-size: 16px;\n line-height: 24px;\n }\n\n .lingbi-studio-exit-button {\n height: 36px;\n padding: 0 14px;\n font-size: 16px;\n }\n}\n\n.lingbi-studio-viewport > canvas:focus-visible,\n.lingbi-studio-boolean-button:focus-visible,\n.lingbi-studio-simulation-button:focus-visible,\n.lingbi-studio-download-button:focus-visible,\n.lingbi-studio-inventory-button:focus-visible,\n.lingbi-studio-edit-button:focus-visible,\n.lingbi-studio-model-button:focus-visible,\n.lingbi-studio-model-visibility-button:focus-visible,\n.lingbi-studio-exit-button:focus-visible,\n.lingbi-studio-boolean-edit-confirmation-cancel:focus-visible,\n.lingbi-studio-boolean-edit-confirmation-continue:focus-visible {\n outline: 2px solid #7cc0ff;\n outline-offset: 2px;\n}\n";
40014
+ const studioWorkspaceStyles = ".lingbi-studio-workspace {\n position: relative;\n display: block;\n width: 100%;\n height: 100%;\n min-width: 0;\n min-height: 0;\n overflow: hidden;\n}\n\n.lingbi-studio-viewport {\n width: 100%;\n height: 100%;\n min-width: 0;\n min-height: 0;\n overflow: hidden;\n}\n\n.lingbi-studio-viewport > canvas {\n display: block;\n width: 100%;\n height: 100%;\n min-width: 0;\n min-height: 0;\n overflow: hidden;\n background: #3d3f41;\n}\n\n.lingbi-studio-boolean-operation-overlay {\n position: absolute;\n z-index: 3;\n inset: 0;\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n gap: 12px;\n background: rgba(17, 17, 17, 0.32);\n color: #ffffff;\n cursor: wait;\n}\n\n.lingbi-studio-boolean-operation-overlay[hidden] {\n display: none;\n}\n\n.lingbi-studio-boolean-edit-confirmation {\n position: absolute;\n z-index: 4;\n top: 50%;\n left: 50%;\n display: grid;\n grid-template-columns: 1fr 1fr;\n gap: 12px;\n width: min(420px, calc(100% - 32px));\n padding: 24px;\n border-radius: 8px;\n background: #ffffff;\n box-shadow: 0 8px 32px rgba(0, 0, 0, 0.22);\n transform: translate(-50%, -50%);\n box-sizing: border-box;\n}\n\n.lingbi-studio-boolean-edit-confirmation[hidden] {\n display: none;\n}\n\n.lingbi-studio-boolean-edit-confirmation-text {\n grid-column: 1 / -1;\n margin: 0 0 8px;\n color: #171717;\n font-size: 16px;\n font-weight: 500;\n line-height: 24px;\n text-align: center;\n}\n\n.lingbi-studio-boolean-edit-confirmation-cancel,\n.lingbi-studio-boolean-edit-confirmation-continue {\n height: 40px;\n border: 1px solid #d9d9d9;\n border-radius: 4px;\n font: inherit;\n font-size: 16px;\n font-weight: 500;\n line-height: 24px;\n cursor: pointer;\n}\n\n.lingbi-studio-boolean-edit-confirmation-cancel {\n background: #ffffff;\n color: #171717;\n}\n\n.lingbi-studio-boolean-edit-confirmation-continue {\n border-color: #f27c38;\n background: #f27c38;\n color: #ffffff;\n}\n\n.lingbi-studio-boolean-operation-spinner {\n width: 32px;\n height: 32px;\n border: 3px solid rgba(255, 105, 0, 0.28);\n border-top-color: #ff6900;\n border-radius: 50%;\n animation: lingbi-studio-boolean-operation-spin 0.8s linear infinite;\n box-sizing: border-box;\n}\n\n.lingbi-studio-boolean-operation-status {\n font-size: 14px;\n font-weight: 500;\n line-height: 20px;\n letter-spacing: 0;\n}\n\n.lingbi-studio-bottom-toolbar {\n position: absolute;\n z-index: 1;\n bottom: 36px;\n left: 50%;\n display: grid;\n align-items: center;\n grid-template-columns: minmax(0, 1fr);\n width: min(120px, calc(100% - 32px));\n height: 64px;\n padding: 8px 12px;\n transform: translateX(-50%);\n box-sizing: border-box;\n border-radius: 8px;\n background: #f4f4f4;\n box-shadow: 0 4px 12px rgba(0, 0, 0, 0.14);\n}\n\n.lingbi-studio-bottom-toolbar.has-edit-control {\n grid-template-columns: repeat(4, minmax(0, 1fr));\n width: min(480px, calc(100% - 32px));\n}\n\n.lingbi-studio-bottom-toolbar.has-inventory-control {\n grid-template-columns: repeat(2, minmax(0, 1fr));\n width: min(240px, calc(100% - 32px));\n}\n\n.lingbi-studio-bottom-toolbar.has-edit-control.has-inventory-control {\n grid-template-columns: repeat(5, minmax(0, 1fr));\n width: min(600px, calc(100% - 32px));\n}\n\n.lingbi-studio-bottom-toolbar.has-model-control.has-inventory-control {\n grid-template-columns: repeat(3, minmax(0, 1fr));\n width: min(360px, calc(100% - 32px));\n}\n\n.lingbi-studio-bottom-toolbar.has-model-control.has-edit-control {\n grid-template-columns: repeat(5, minmax(0, 1fr));\n width: min(600px, calc(100% - 32px));\n}\n\n.lingbi-studio-bottom-toolbar.has-model-control.has-edit-control.has-inventory-control {\n grid-template-columns: repeat(6, minmax(0, 1fr));\n width: min(720px, calc(100% - 32px));\n}\n\n.lingbi-studio-model-visibility {\n position: absolute;\n z-index: 1;\n bottom: 108px;\n left: 50%;\n display: grid;\n gap: 8px;\n width: 144px;\n padding: 12px;\n border-radius: 8px;\n background: #f4f4f4;\n box-shadow: 0 4px 12px rgba(0, 0, 0, 0.14);\n transform: translateX(-240px);\n box-sizing: border-box;\n}\n\n.lingbi-studio-model-visibility[hidden] {\n display: none;\n}\n\n.lingbi-studio-model-visibility-title {\n color: #171717;\n font-size: 14px;\n font-weight: 600;\n line-height: 20px;\n}\n\n.lingbi-studio-model-visibility-row {\n display: grid;\n grid-template-columns: minmax(0, 1fr) auto;\n gap: 8px;\n align-items: center;\n color: #171717;\n font-size: 14px;\n line-height: 20px;\n}\n\n.lingbi-studio-model-visibility-button {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n appearance: none;\n width: 20px;\n height: 28px;\n padding: 0;\n border: 0;\n border-radius: 0;\n background: transparent;\n box-shadow: none;\n color: #171717;\n cursor: not-allowed;\n font: inherit;\n line-height: 0;\n outline: none;\n box-sizing: border-box;\n}\n\n.lingbi-studio-model-visibility-icon {\n display: block;\n width: 18px;\n height: 18px;\n flex: 0 0 auto;\n pointer-events: none;\n}\n\n.lingbi-studio-model-visibility-button:not(:disabled) {\n cursor: pointer;\n}\n\n.lingbi-studio-model-visibility-button:disabled {\n color: #8c8c8c;\n}\n\n.lingbi-studio-model-button.is-active,\n.lingbi-studio-model-button.is-active:disabled {\n color: #ff6900;\n font-weight: 600;\n}\n\n.lingbi-studio-boolean-button,\n.lingbi-studio-simulation-button,\n.lingbi-studio-download-button,\n.lingbi-studio-inventory-button,\n.lingbi-studio-edit-button,\n.lingbi-studio-model-button {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 100%;\n min-width: 0;\n height: 48px;\n padding: 0 8px;\n border: 0;\n border-radius: 4px;\n background: transparent;\n color: #171717;\n cursor: not-allowed;\n font: inherit;\n font-size: 18px;\n font-weight: 500;\n line-height: 24px;\n letter-spacing: 0;\n opacity: 1;\n box-sizing: border-box;\n white-space: nowrap;\n transition:\n background-color 140ms ease,\n color 140ms ease;\n}\n\n.lingbi-studio-operation-button-wrapper {\n display: block;\n width: 100%;\n min-width: 0;\n height: 48px;\n box-sizing: border-box;\n}\n\n.lingbi-studio-operation-button-wrapper > button:disabled {\n pointer-events: none;\n}\n\n.lingbi-studio-boolean-button[hidden],\n.lingbi-studio-simulation-button[hidden],\n.lingbi-studio-download-button[hidden],\n.lingbi-studio-inventory-button[hidden],\n.lingbi-studio-edit-button[hidden],\n.lingbi-studio-model-button[hidden] {\n display: none;\n}\n\n.lingbi-studio-boolean-button:not(:disabled),\n.lingbi-studio-simulation-button:not(:disabled),\n.lingbi-studio-download-button:not(:disabled),\n.lingbi-studio-inventory-button:not(:disabled),\n.lingbi-studio-edit-button:not(:disabled),\n.lingbi-studio-model-button:not(:disabled) {\n cursor: pointer;\n}\n\n.lingbi-studio-boolean-button:disabled,\n.lingbi-studio-simulation-button:disabled,\n.lingbi-studio-download-button:disabled,\n.lingbi-studio-inventory-button:disabled,\n.lingbi-studio-edit-button:disabled,\n.lingbi-studio-model-button:disabled,\n.lingbi-studio-model-visibility-button:disabled {\n background: transparent;\n color: #8c8c8c;\n opacity: 1;\n}\n\n.lingbi-studio-edit-button.is-active,\n.lingbi-studio-edit-button.is-active:disabled,\n.lingbi-studio-model-button.is-active,\n.lingbi-studio-model-button.is-active:disabled {\n color: #ff6900;\n font-weight: 600;\n}\n\n.lingbi-studio-download-status {\n position: absolute;\n z-index: 2;\n top: 24px;\n left: 50%;\n max-width: calc(100% - 24px);\n padding: 6px 10px;\n border: 1px solid #f4b5b0;\n border-radius: 4px;\n background: #fff1f0;\n color: #b42318;\n font-size: 13px;\n font-weight: 500;\n line-height: 20px;\n text-align: center;\n transform: translateX(-50%);\n box-sizing: border-box;\n}\n\n.lingbi-studio-download-status[hidden] {\n display: none;\n}\n\n.lingbi-studio-download-success {\n position: absolute;\n z-index: 2;\n top: 24px;\n left: 50%;\n max-width: calc(100% - 24px);\n padding: 8px 16px;\n border: 1px solid #86d39a;\n border-radius: 6px;\n background: #f0fff4;\n color: #176b32;\n font-size: 14px;\n font-weight: 500;\n line-height: 20px;\n text-align: center;\n transform: translateX(-50%);\n box-shadow: 0 4px 12px rgba(0, 0, 0, 0.12);\n box-sizing: border-box;\n}\n\n.lingbi-studio-download-success[hidden] {\n display: none;\n}\n\n.lingbi-studio-edit-status {\n position: absolute;\n z-index: 2;\n bottom: 112px;\n left: 50%;\n display: flex;\n align-items: center;\n gap: 12px;\n max-width: calc(100% - 24px);\n padding: 10px 14px;\n border: 0;\n border-radius: 8px;\n background: #e9e9e9;\n color: #171717;\n transform: translateX(-50%);\n box-sizing: border-box;\n}\n\n.lingbi-studio-edit-status[hidden] {\n display: none;\n}\n\n.lingbi-studio-edit-status-text {\n display: inline-block;\n min-width: 0;\n font-size: 20px;\n line-height: 28px;\n letter-spacing: 0;\n}\n\n.lingbi-studio-exit-button {\n display: inline-flex;\n flex: 0 0 auto;\n align-items: center;\n justify-content: center;\n height: 40px;\n padding: 0 18px;\n border: 1px solid transparent;\n border-radius: 8px;\n background: #f27c38;\n color: #ffffff;\n cursor: pointer;\n font: inherit;\n font-size: 18px;\n font-weight: 600;\n line-height: 24px;\n letter-spacing: 0;\n transition:\n background-color 140ms ease,\n color 140ms ease;\n}\n\n@media (hover: hover) and (pointer: fine) {\n .lingbi-studio-boolean-button:not(:disabled):hover,\n .lingbi-studio-simulation-button:not(:disabled):hover,\n .lingbi-studio-download-button:not(:disabled):hover,\n .lingbi-studio-inventory-button:not(:disabled):hover,\n .lingbi-studio-edit-button:not(:disabled):hover,\n .lingbi-studio-model-button:not(:disabled):hover {\n background: #e8e8e8;\n }\n\n .lingbi-studio-simulation-button:not(:disabled):hover,\n .lingbi-studio-download-button:not(:disabled):hover,\n .lingbi-studio-inventory-button:not(:disabled):hover,\n .lingbi-studio-edit-button:not(:disabled):hover,\n .lingbi-studio-model-button:not(:disabled):hover {\n color: #f06400;\n }\n\n .lingbi-studio-model-visibility-button:not(:disabled):hover {\n background: transparent;\n }\n\n .lingbi-studio-exit-button:hover {\n background: #ff8a1f;\n }\n\n .lingbi-studio-boolean-edit-confirmation-cancel:hover {\n background: #f5f5f5;\n }\n\n .lingbi-studio-boolean-edit-confirmation-continue:hover {\n background: #ff8a1f;\n }\n}\n\n.lingbi-studio-boolean-button:not(:disabled):active,\n.lingbi-studio-download-button:not(:disabled):active,\n.lingbi-studio-inventory-button:not(:disabled):active,\n.lingbi-studio-edit-button:not(:disabled):active,\n.lingbi-studio-model-button:not(:disabled):active,\n.lingbi-studio-model-visibility-button:not(:disabled):active {\n background: transparent;\n}\n\n.lingbi-studio-exit-button:active {\n background: #e66d00;\n}\n\n@keyframes lingbi-studio-boolean-operation-spin {\n to {\n transform: rotate(360deg);\n }\n}\n\n@media (prefers-reduced-motion: reduce) {\n .lingbi-studio-boolean-button,\n .lingbi-studio-simulation-button,\n .lingbi-studio-download-button,\n .lingbi-studio-inventory-button,\n .lingbi-studio-edit-button,\n .lingbi-studio-model-button,\n .lingbi-studio-exit-button {\n transition: none;\n }\n\n .lingbi-studio-boolean-operation-spinner {\n animation: none;\n }\n}\n\n@media (max-width: 420px) {\n .lingbi-studio-bottom-toolbar.has-edit-control {\n width: calc(100% - 16px);\n padding: 8px 4px;\n }\n\n .lingbi-studio-bottom-toolbar.has-model-control.has-edit-control {\n width: calc(100% - 16px);\n }\n\n .lingbi-studio-bottom-toolbar.has-model-control.has-edit-control.has-inventory-control {\n width: calc(100% - 16px);\n }\n\n .lingbi-studio-model-visibility {\n left: 8px;\n transform: none;\n }\n\n .lingbi-studio-boolean-button,\n .lingbi-studio-simulation-button,\n .lingbi-studio-download-button,\n .lingbi-studio-inventory-button,\n .lingbi-studio-edit-button,\n .lingbi-studio-model-button {\n padding: 0 2px;\n font-size: 16px;\n }\n\n .lingbi-studio-edit-status-text {\n font-size: 16px;\n line-height: 24px;\n }\n\n .lingbi-studio-exit-button {\n height: 36px;\n padding: 0 14px;\n font-size: 16px;\n }\n}\n\n.lingbi-studio-viewport > canvas:focus-visible,\n.lingbi-studio-boolean-button:focus-visible,\n.lingbi-studio-simulation-button:focus-visible,\n.lingbi-studio-download-button:focus-visible,\n.lingbi-studio-inventory-button:focus-visible,\n.lingbi-studio-edit-button:focus-visible,\n.lingbi-studio-model-button:focus-visible,\n.lingbi-studio-model-visibility-button:focus-visible,\n.lingbi-studio-exit-button:focus-visible,\n.lingbi-studio-boolean-edit-confirmation-cancel:focus-visible,\n.lingbi-studio-boolean-edit-confirmation-continue:focus-visible {\n outline: 2px solid #7cc0ff;\n outline-offset: 2px;\n}\n";
39918
40015
  class StudioWorkspace {
39919
40016
  canvas;
39920
40017
  static SVG_NAMESPACE = "http://www.w3.org/2000/svg";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lingbi/studio",
3
- "version": "0.8.1",
3
+ "version": "0.8.3",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "https://github.com/genewanggang-sudo/lingbi-shitou.git"