partforge 0.150.0 → 0.150.1

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "partforge",
3
- "version": "0.150.0",
3
+ "version": "0.150.1",
4
4
  "description": "Turn a declarative part definition into a parametric-CAD web app (three.js + Manifold/Replicad). Requires a Vite-based consumer.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -835,14 +835,16 @@ button.action:focus-visible, .adv-toggle:focus-visible, .sec-title:focus-visible
835
835
  .pf-card-lab-row { display: flex; justify-content: space-between; align-items: baseline; }
836
836
  .pf-card-lab { font-size: 12px; color: var(--pf-muted); }
837
837
  .pf-card-val { font-size: 12px; color: var(--pf-text-2); font-variant-numeric: tabular-nums; }
838
- .pf-card-tiles { display: grid; grid-template-columns: repeat(6, minmax(0, 1fr)); gap: 6px; }
839
- .pf-card-tile { display: grid; justify-items: center; gap: 3px; border: 0; background: none; padding: 3px 0; border-radius: 8px; cursor: pointer; font: inherit; color: var(--pf-muted); }
838
+ .pf-card-val.preview { color: var(--pf-text); }
839
+ .pf-card-tiles { display: grid; grid-template-columns: repeat(5, minmax(0, 1fr)); gap: 4px 2px; }
840
+ .pf-card-tile { display: grid; justify-items: center; align-content: start; gap: 4px; border: 0; background: none; padding: 4px 1px 3px; border-radius: 8px; cursor: pointer; font: inherit; color: var(--pf-text-2); }
840
841
  .pf-card-tile:hover { background: var(--pf-surface-2); }
841
842
  .pf-card-tile[aria-pressed="true"] { background: var(--pf-accent-soft); color: var(--pf-accent); }
842
- .pf-card-ball { position: relative; width: 30px; height: 30px; border-radius: 50%; overflow: hidden; background: radial-gradient(circle at 35% 30%, color-mix(in oklab, var(--pf-swatch) 72%, white) 0, var(--pf-swatch) 48%, color-mix(in oklab, var(--pf-swatch) 68%, black) 100%); box-shadow: inset 0 -1px 2px rgb(0 0 0 / .25); }
843
+ .pf-card-ball { position: relative; width: 36px; height: 36px; border-radius: 50%; overflow: hidden; background: radial-gradient(circle at 35% 30%, color-mix(in oklab, var(--pf-swatch) 72%, white) 0, var(--pf-swatch) 48%, color-mix(in oklab, var(--pf-swatch) 68%, black) 100%); box-shadow: inset 0 -1px 2px rgb(0 0 0 / .25); }
843
844
  .pf-card-ball img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
844
845
  .pf-card-ball img[hidden] { display: none; }
845
- .pf-card-tile-label { font-size: 9.5px; line-height: 1.1; max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
846
+ /* Two lines at most; the Material row names the hovered tile in full. */
847
+ .pf-card-tile-label { font-size: 10.5px; line-height: 1.15; max-width: 100%; min-height: 2.3em; text-align: center; overflow: hidden; display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2; line-clamp: 2; }
846
848
  .pf-card-unknown { margin: 0; font-size: 11px; color: var(--pf-err); font-family: var(--pf-mono); }
847
849
  .pf-card-unknown[hidden] { display: none; }
848
850
  .pf-card-colors { display: grid; grid-template-columns: repeat(10, minmax(0, 1fr)); gap: 6px; }
@@ -206,6 +206,30 @@ export function attachAppearanceCard(parts, { onBack, onClose, onGone, getSaveSt
206
206
  const ownColor = known(id) && !PRESETS[id].tintable && typeof parts.getState(name)?.override?.color === "number";
207
207
  parts.setDisplay(name, ownColor ? { material: id, color: null } : { material: id });
208
208
  });
209
+ // The Material row names the tile under the pointer (or keyboard focus),
210
+ // then the part's material again: a tile's own label clamps to two lines,
211
+ // so this is where a long name reads in full. A touch names nothing — a tap
212
+ // applies the tile, and the row then names it as the material anyway.
213
+ let previewLabel = null;
214
+ const showMaterialName = () => {
215
+ matVal.textContent = previewLabel ?? row?.materialLabel ?? "None";
216
+ matVal.classList.toggle("preview", previewLabel !== null);
217
+ };
218
+ const previewFrom = (e) => {
219
+ const t = e.target.closest?.(".pf-card-tile");
220
+ previewLabel = t ? t.title : null;
221
+ showMaterialName();
222
+ };
223
+ const previewEnd = (e) => {
224
+ const t = e.target.closest?.(".pf-card-tile");
225
+ if (!t || t.contains(e.relatedTarget)) return;
226
+ previewLabel = null;
227
+ showMaterialName();
228
+ };
229
+ tiles.addEventListener("pointerover", (e) => { if (e.pointerType !== "touch") previewFrom(e); });
230
+ tiles.addEventListener("pointerout", (e) => { if (e.pointerType !== "touch") previewEnd(e); });
231
+ tiles.addEventListener("focusin", previewFrom);
232
+ tiles.addEventListener("focusout", previewEnd);
209
233
  colors.addEventListener("click", (e) => {
210
234
  const b = e.target.closest(".pf-card-color");
211
235
  if (!b || !name) return;
@@ -238,7 +262,7 @@ export function attachAppearanceCard(parts, { onBack, onClose, onGone, getSaveSt
238
262
  see.value = String(pct); seeVal.textContent = `${pct}%`;
239
263
  const m = state.effective.material;
240
264
  for (const t of tiles.children) t.setAttribute("aria-pressed", String(known(m) && t.dataset.material === m));
241
- matVal.textContent = row.materialLabel ?? "None";
265
+ showMaterialName();
242
266
  unknown.hidden = !(m != null && !known(m));
243
267
  unknown.textContent = unknown.hidden ? "" : String(m);
244
268
  const color = row.color;
@@ -320,7 +344,7 @@ export function attachAppearanceCard(parts, { onBack, onClose, onGone, getSaveSt
320
344
  element: root,
321
345
  show(next) {
322
346
  if (!parts.list().some((r) => r.name === next)) return false;
323
- name = next;
347
+ name = next; previewLabel = null;
324
348
  render();
325
349
  return true;
326
350
  },