partforge 0.122.0 → 0.124.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.
@@ -1,33 +1,26 @@
1
- // The view cube's chrome: the bottom-right stack (the cube, with the projection
2
- // toggle over its bottom-right corner), and the visually-hidden per-view buttons
3
- // that stand in for the DOM focus a canvas cannot give us. Generated, not
4
- // declared — no part's HTML carries this, and partforge-cloud's scaffold does
5
- // not either (the mobile-tabs.js and animation-controls.js precedent).
1
+ // The view cube's chrome: the bottom-right stack (the cube, and whatever sits
2
+ // over its bottom-right corner), and the visually-hidden per-view buttons that
3
+ // stand in for the DOM focus a canvas cannot give us. Generated, not declared —
4
+ // no part's HTML carries this, and partforge-cloud's scaffold does not either
5
+ // (the mobile-tabs.js and animation-controls.js precedent).
6
6
  //
7
- // The projection button deliberately lives OUTSIDE #viewbar: partforge-cloud's
8
- // sandbox-scaffold test enumerates #viewbar's buttons against what it renders,
9
- // and this one is the framework's own.
10
- //
11
- // The button used to sit in its own `.pf-viewcube-pill` card below the cube,
12
- // borrowing #viewbar's chrome. The 2026-08-20 revision made it a small bare
13
- // circle beside the cube instead (see chrome.css/app.css's viewcube sections),
14
- // so the pill card — which existed only to give a single button somewhere to
15
- // sit — is gone; the button is now a direct child of the stack.
16
- //
17
- // A same-day follow-up took it out of the stack's flex flow entirely and laid
18
- // it OVER the cube's bottom-right corner. The DOM is unchanged (still a direct
19
- // child, still after the cube's wrapper, which is what puts it on top); the
20
- // visible consequence is that the stack is now exactly as wide as the canvas
21
- // rather than `canvas + gap + button`, so the size it publishes below — and
22
- // therefore the crowding decision that reads it — went from 167px to 135
23
- // (101 below the rail's narrow breakpoint).
24
- import { attachButtonTooltips } from "../tooltip.js";
7
+ // Through 2026-09-23 this file also generated the projection toggle (#projection),
8
+ // a small bare circle laid OVER the cube's bottom-right corner — absolutely
9
+ // positioned, so the stack stayed exactly as wide as the canvas (135px, or 101
10
+ // below the rail's narrow breakpoint) and the size published below was the
11
+ // canvas's alone. There is no projection control any more: since 2026-09-24
12
+ // the projection is AUTOMATIC (Fusion 360's "Perspective with Ortho Faces") —
13
+ // a face click, on the canvas or through the keyboard buttons below, settles
14
+ // into orthographic, and rotating off the face returns to perspective (see
15
+ // viewer.js's tweenCameraTo). The view style button (view-style-controls.js)
16
+ // lives in the bottom toolbar (#viewbar); only a host with no #viewbar gets it
17
+ // in this stack, over the cube's bottom-right corner, absolutely positioned so
18
+ // the stack's published size is still the canvas's. History of the toggle's
19
+ // earlier homes (a `.pf-viewcube-pill` card below the cube, then a circle
20
+ // beside it) is in chrome.css/app.css's viewcube sections.
25
21
  import { runCleanupSteps } from "../teardown.js";
26
22
  import { createViewcubeMode } from "./viewcube-mode.js";
27
23
 
28
- const PERSPECTIVE_ICON = `<svg viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 4l18 3v10l-18 3z"/><path d="M3 4v16"/></svg>`;
29
- const ORTHOGRAPHIC_ICON = `<svg viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="3" y="6" width="14" height="12" rx="1"/><path d="M7 6V3h14v12h-4"/></svg>`;
30
-
31
24
  // One hidden button per canonical FACE view. Edges and corners are reachable by
32
25
  // pointer only — six targets is a usable keyboard surface; twenty-six is a
33
26
  // tab-stop thicket.
@@ -40,19 +33,13 @@ const KEY_VIEWS = [
40
33
  ["bottom", "View from the bottom"],
41
34
  ];
42
35
 
43
- export function attachViewcubeControls(viewer, { stage } = {}, { tooltip } = {}) {
36
+ export function attachViewcubeControls(viewer, { stage } = {}) {
44
37
  const stack = document.createElement("div");
45
38
  stack.className = "pf-viewcube-stack";
46
39
  stage.appendChild(stack);
47
40
 
48
41
  const mode = createViewcubeMode(viewer, { host: stack });
49
42
 
50
- const button = document.createElement("button");
51
- button.type = "button";
52
- button.id = "projection";
53
- button.className = "pf-viewcube-toggle";
54
- stack.appendChild(button);
55
-
56
43
  const keys = document.createElement("div");
57
44
  keys.className = "pf-viewcube-key";
58
45
  const keyButtons = KEY_VIEWS.map(([view, label]) => {
@@ -66,40 +53,15 @@ export function attachViewcubeControls(viewer, { stage } = {}, { tooltip } = {})
66
53
  });
67
54
  stack.appendChild(keys);
68
55
 
69
- const tooltipBinding = tooltip
70
- ? attachButtonTooltips(tooltip, [{ element: button }])
71
- : null;
72
-
73
- function sync() {
74
- const ortho = viewer.getProjection() === "orthographic";
75
- button.innerHTML = ortho ? ORTHOGRAPHIC_ICON : PERSPECTIVE_ICON;
76
- button.classList.toggle("on", ortho);
77
- button.setAttribute("aria-pressed", String(ortho));
78
- const label = ortho ? "Switch to perspective view" : "Switch to orthographic view";
79
- button.setAttribute("aria-label", label);
80
- if (!tooltip) button.title = label;
81
- tooltipBinding?.sync();
82
- }
83
-
84
- const onToggle = () => {
85
- viewer.setProjection(viewer.getProjection() === "orthographic" ? "perspective" : "orthographic");
86
- sync();
87
- };
88
- button.addEventListener("click", onToggle);
89
-
90
56
  const keyHandlers = keyButtons.map((b) => {
91
57
  // Same user intent as clicking the face on the canvas, so the same
92
- // `refit` — these buttons ARE the keyboard route to that click.
93
- const handler = () => viewer.tweenCameraTo(b.dataset.view, { duration: 0.6, refit: true });
58
+ // `refit` and `autoProjection` (a face view settles into orthographic) —
59
+ // these buttons ARE the keyboard route to that click.
60
+ const handler = () => viewer.tweenCameraTo(b.dataset.view, { duration: 0.6, refit: true, autoProjection: true });
94
61
  b.addEventListener("click", handler);
95
62
  return handler;
96
63
  });
97
64
 
98
- // A host or another mode can flip projection without going through this
99
- // button; the chrome follows rather than drifting out of sync.
100
- const offProjection = viewer.onProjectionChange(sync);
101
- sync();
102
-
103
65
  // Publish the stack's size on the element itself, in the data-pf-* convention
104
66
  // the shell already uses (data-pf-pane). animation-controls.js reads it to
105
67
  // decide whether the transport bar is crowded, and that decision has to come
@@ -144,11 +106,8 @@ export function attachViewcubeControls(viewer, { stage } = {}, { tooltip } = {})
144
106
  if (detached) return;
145
107
  detached = true;
146
108
  runCleanupSteps([
147
- offProjection,
148
109
  () => sizeObserver?.disconnect(),
149
- () => button.removeEventListener("click", onToggle),
150
110
  ...keyButtons.map((b, i) => () => b.removeEventListener("click", keyHandlers[i])),
151
- () => tooltipBinding?.detach(),
152
111
  () => mode.detach(),
153
112
  () => stack.remove(),
154
113
  ], "viewcube control cleanup failed");
@@ -128,9 +128,11 @@ export function createViewcubeMode(viewer, {
128
128
  const id = hitRegion(...localPoint(event), projected);
129
129
  // refit: clicking a region is now the only reframe control the framework's
130
130
  // own pages ship, so it has to actually refit — under orthographic a tween
131
- // alone changes the angle and leaves the user's dolly in place. See
132
- // viewer.js's tweenCameraTo.
133
- if (id) viewer.tweenCameraTo(id, { duration: 0.6, refit: true });
131
+ // alone changes the angle and leaves the user's dolly in place.
132
+ // autoProjection: a FACE click settles into orthographic, an edge or corner
133
+ // click is perspective (Fusion 360's "Perspective with Ortho Faces"). Only
134
+ // the cube passes it — animation cues never do. See viewer.js's tweenCameraTo.
135
+ if (id) viewer.tweenCameraTo(id, { duration: 0.6, refit: true, autoProjection: true });
134
136
  };
135
137
 
136
138
  const onPointerLeave = () => {