Skip to content

Commit a1d3fcf

Browse files
committed
add openscad element
1 parent c71b72d commit a1d3fcf

14 files changed

Lines changed: 1167 additions & 259 deletions

File tree

.changeset/hot-frogs-train.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
"@hyperbook/markdown": minor
3+
"hyperbook": minor
4+
---
5+
6+
Add openscad element

packages/markdown/assets/directive-openscad/client.js

Lines changed: 571 additions & 74 deletions
Large diffs are not rendered by default.

packages/markdown/assets/directive-openscad/style.css

Lines changed: 218 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,25 @@
88
height: var(--openscad-height, calc(100dvh - 80px));
99
}
1010

11-
.directive-openscad .preview-container {
11+
/* Left side: wraps preview-container + params card */
12+
.directive-openscad .left-side {
1213
width: 100%;
1314
min-height: 120px;
1415
min-width: 120px;
16+
flex: 1 1 0;
17+
display: flex;
18+
flex-direction: column;
19+
gap: 0;
20+
overflow: hidden;
21+
}
22+
23+
.directive-openscad .preview-container {
24+
width: 100%;
25+
min-height: 120px;
1526
border: 1px solid var(--color-spacer);
1627
border-radius: 8px;
1728
overflow: hidden;
18-
background-color: var(--color--background);
29+
background-color: var(--color-background, var(--color--background, #fff));
1930
flex: 1 1 0;
2031
display: flex;
2132
flex-direction: column;
@@ -30,20 +41,149 @@
3041
.directive-openscad .preview-canvas {
3142
width: 100%;
3243
height: 100%;
33-
min-height: 260px;
3444
display: block;
3545
background: linear-gradient(180deg, #f8fafc 0%, #e2e8f0 100%);
3646
}
3747

38-
.directive-openscad .output {
39-
margin: 0;
40-
border-top: 1px solid var(--color-spacer);
41-
padding: 8px 12px;
42-
min-height: 56px;
43-
max-height: 120px;
44-
overflow: auto;
48+
/* Canvas wrapper: relative container for overlay positioning */
49+
.directive-openscad .canvas-wrapper {
50+
position: relative;
51+
flex: 1;
52+
overflow: hidden;
53+
min-height: 80px;
54+
display: flex;
55+
}
56+
57+
/* Canvas overlay: covers the canvas area */
58+
.directive-openscad .canvas-overlay {
59+
position: absolute;
60+
inset: 0;
61+
display: flex;
62+
flex-direction: column;
63+
align-items: center;
64+
justify-content: center;
65+
gap: 12px;
66+
padding: 16px;
67+
box-sizing: border-box;
68+
pointer-events: none;
69+
}
70+
71+
.directive-openscad .canvas-overlay.hidden {
72+
display: none;
73+
}
74+
75+
/* Loading state */
76+
.directive-openscad .canvas-overlay.loading {
77+
background: rgba(0, 0, 0, 0.45);
78+
color: #fff;
79+
}
80+
81+
/* Error state */
82+
.directive-openscad .canvas-overlay.error {
83+
background: rgba(239, 68, 68, 0.12);
84+
color: #991b1b;
85+
align-items: flex-start;
86+
justify-content: flex-start;
87+
overflow-y: auto;
88+
pointer-events: auto;
89+
}
90+
91+
.directive-openscad .canvas-overlay .overlay-message {
92+
font-weight: 600;
93+
font-size: 0.95em;
94+
text-align: center;
95+
}
96+
97+
.directive-openscad .canvas-overlay.error .overlay-message {
4598
white-space: pre-wrap;
99+
word-break: break-word;
100+
font-weight: 400;
101+
text-align: left;
46102
font-family: hyperbook-monospace, monospace;
103+
font-size: 0.85em;
104+
}
105+
106+
.directive-openscad .overlay-dismiss {
107+
padding: 4px 14px;
108+
border: 1px solid currentColor;
109+
border-radius: 4px;
110+
background: transparent;
111+
color: inherit;
112+
cursor: pointer;
113+
font-size: 0.85em;
114+
opacity: 0.8;
115+
flex: 0 0 auto;
116+
}
117+
118+
.directive-openscad .overlay-dismiss:hover {
119+
opacity: 1;
120+
background: rgba(0, 0, 0, 0.06);
121+
}
122+
123+
/* Spinner */
124+
.directive-openscad .canvas-spinner {
125+
width: 32px;
126+
height: 32px;
127+
border: 3px solid rgba(255, 255, 255, 0.3);
128+
border-top-color: #fff;
129+
border-radius: 50%;
130+
animation: openscad-spin 0.75s linear infinite;
131+
}
132+
133+
@keyframes openscad-spin {
134+
to { transform: rotate(360deg); }
135+
}
136+
137+
/* Splitter between canvas and parameters card */
138+
.directive-openscad .canvas-params-splitter {
139+
width: 100%;
140+
height: 4px;
141+
margin: 6px 0;
142+
cursor: row-resize;
143+
background: var(--color-spacer);
144+
border-radius: 999px;
145+
flex-shrink: 0;
146+
touch-action: none;
147+
opacity: 0.45;
148+
transition: opacity 0.15s ease-in-out;
149+
}
150+
151+
.directive-openscad .canvas-params-splitter:hover {
152+
opacity: 0.65;
153+
}
154+
155+
/* Parameters card */
156+
.directive-openscad .parameters-panel {
157+
width: 100%;
158+
min-height: 80px;
159+
border: 1px solid var(--color-spacer);
160+
border-radius: 8px;
161+
overflow: hidden;
162+
background-color: var(--color-background, var(--color--background, #fff));
163+
flex: 1 1 0;
164+
display: flex;
165+
flex-direction: column;
166+
box-sizing: border-box;
167+
}
168+
169+
.directive-openscad .parameters-panel.hidden {
170+
display: none;
171+
}
172+
173+
.directive-openscad .parameters-header {
174+
border-bottom: 1px solid var(--color-spacer);
175+
padding: 8px 16px;
176+
font-weight: 600;
177+
flex-shrink: 0;
178+
}
179+
180+
.directive-openscad .parameters-body {
181+
flex: 1;
182+
overflow-y: auto;
183+
padding: 12px;
184+
display: flex;
185+
flex-direction: column;
186+
gap: 10px;
47187
}
48188

49189
.directive-openscad .editor-container {
@@ -55,6 +195,7 @@
55195
flex: 1 1 0;
56196
}
57197

198+
/* Main left/right splitter */
58199
.directive-openscad .splitter {
59200
background: var(--color-spacer);
60201
border-radius: 999px;
@@ -107,7 +248,7 @@
107248
padding: 8px 16px;
108249
border: none;
109250
border-right: 1px solid var(--color-spacer);
110-
background-color: var(--color--background);
251+
background-color: var(--color-background, var(--color--background, #fff));
111252
color: var(--color-text);
112253
cursor: pointer;
113254
}
@@ -133,19 +274,61 @@
133274
width: 100%;
134275
border: 1px solid var(--color-spacer);
135276
flex: 1;
277+
margin: 0; /* override code-input.min.css default margin: 8px */
136278
}
137279

280+
/* The parameters textarea is always hidden — the form replaces it visually */
138281
.directive-openscad .parameters {
139-
display: none;
140-
box-sizing: border-box;
141-
resize: none;
142-
padding: 12px;
143-
font-family: hyperbook-monospace, monospace;
282+
display: none !important;
144283
}
145284

146-
.directive-openscad .editor:not(.active),
147-
.directive-openscad .parameters:not(.active) {
148-
display: none;
285+
.directive-openscad .param-row {
286+
display: flex;
287+
align-items: center;
288+
gap: 10px;
289+
}
290+
291+
.directive-openscad .param-row label {
292+
flex: 1;
293+
font-weight: 500;
294+
min-width: 0;
295+
word-break: break-word;
296+
}
297+
298+
.directive-openscad .param-row input[type="text"],
299+
.directive-openscad .param-row input[type="number"],
300+
.directive-openscad .param-row select {
301+
flex: 1;
302+
padding: 4px 8px;
303+
border: 1px solid var(--color-spacer);
304+
border-radius: 4px;
305+
background: var(--color-background, var(--color--background, #fff));
306+
color: var(--color-text);
307+
font-family: inherit;
308+
}
309+
310+
.directive-openscad .param-vector {
311+
flex: 1;
312+
display: flex;
313+
gap: 4px;
314+
}
315+
316+
.directive-openscad .param-vector input[type="number"] {
317+
flex: 1;
318+
min-width: 0;
319+
}
320+
321+
.directive-openscad .param-row input[type="checkbox"] {
322+
width: 18px;
323+
height: 18px;
324+
cursor: pointer;
325+
}
326+
327+
.directive-openscad .params-empty {
328+
color: var(--color-text-muted, #888);
329+
font-style: italic;
330+
margin: auto;
331+
text-align: center;
149332
}
150333

151334
.directive-openscad:fullscreen {
@@ -165,9 +348,24 @@
165348
flex-direction: row;
166349
}
167350

168-
.directive-openscad .preview-container,
351+
.directive-openscad .left-side,
169352
.directive-openscad .editor-container {
170353
flex: 1;
171354
height: 100% !important;
172355
}
173356
}
357+
358+
@media (prefers-color-scheme: dark) {
359+
.directive-openscad .preview-canvas {
360+
background: linear-gradient(180deg, #2a2a2a 0%, #1e1e1e 100%);
361+
}
362+
363+
.directive-openscad .canvas-overlay.error {
364+
background: rgba(239, 68, 68, 0.18);
365+
color: #fca5a5;
366+
}
367+
368+
.directive-openscad .overlay-dismiss:hover {
369+
background: rgba(255, 255, 255, 0.1);
370+
}
371+
}

packages/markdown/locales/de.json

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -89,20 +89,19 @@
8989
"typst-binary-files": "Binärdateien",
9090
"typst-no-binary-files": "Keine Binärdateien",
9191
"openscad-preview": "Vorschau",
92-
"openscad-code": "Code",
93-
"openscad-parameters": "Parameter",
9492
"openscad-render": "Rendern",
9593
"openscad-copy": "Kopieren",
9694
"openscad-copy-done": "Code kopiert",
9795
"openscad-download-stl": "STL herunterladen",
98-
"openscad-download-3mf": "3MF herunterladen",
9996
"openscad-download-ready": "Download bereit",
10097
"openscad-reset": "Zurücksetzen",
10198
"openscad-reset-prompt": "Sind Sie sicher, dass Sie den Code zurücksetzen möchten?",
10299
"openscad-rendering": "Rendern ...",
103100
"openscad-render-success": "Rendern abgeschlossen",
104101
"openscad-render-failed": "OpenSCAD-Rendern fehlgeschlagen",
105102
"openscad-params-object": "Parameter müssen ein JSON-Objekt sein",
103+
"openscad-params-loading": "Parameter werden geladen...",
104+
"openscad-parameters": "Parameter",
106105
"user-login-title": "Anmelden",
107106
"user-username": "Benutzername",
108107
"user-password": "Passwort",

packages/markdown/locales/en.json

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -89,20 +89,19 @@
8989
"typst-binary-files": "Binary Files",
9090
"typst-no-binary-files": "No binary files",
9191
"openscad-preview": "Preview",
92-
"openscad-code": "Code",
93-
"openscad-parameters": "Parameters",
9492
"openscad-render": "Render",
9593
"openscad-copy": "Copy",
9694
"openscad-copy-done": "Code copied",
9795
"openscad-download-stl": "Download STL",
98-
"openscad-download-3mf": "Download 3MF",
9996
"openscad-download-ready": "Download ready",
10097
"openscad-reset": "Reset",
10198
"openscad-reset-prompt": "Are you sure you want to reset the code?",
10299
"openscad-rendering": "Rendering ...",
103100
"openscad-render-success": "Render complete",
104101
"openscad-render-failed": "OpenSCAD render failed",
105102
"openscad-params-object": "Parameters must be a JSON object",
103+
"openscad-params-loading": "Loading parameters...",
104+
"openscad-parameters": "Parameters",
106105
"user-login-title": "Login",
107106
"user-username": "Username",
108107
"user-password": "Password",
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"wasmBuild": {
3+
"url": "https://files.openscad.org/playground/OpenSCAD-2025.03.25.wasm24456-WebAssembly-web.zip",
4+
"target": "directive-openscad",
5+
"files": ["openscad.js", "openscad.wasm"]
6+
}
7+
}

packages/markdown/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@
6767
"remark-rehype": "^11.1.2",
6868
"shiki": "^3.21.0",
6969
"sort-keys": "^6.0.0",
70+
"three": "0.170.0",
7071
"unified": "^11.0.5",
7172
"unist-util-find-after": "^5.0.0",
7273
"unist-util-visit": "^5.1.0",

0 commit comments

Comments
 (0)