mitsupi 1.0.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/LICENSE +201 -0
- package/README.md +95 -0
- package/TODO.md +11 -0
- package/commands/handoff.md +100 -0
- package/commands/make-release.md +75 -0
- package/commands/pickup.md +30 -0
- package/commands/update-changelog.md +78 -0
- package/package.json +22 -0
- package/pi-extensions/answer.ts +527 -0
- package/pi-extensions/codex-tuning.ts +632 -0
- package/pi-extensions/commit.ts +248 -0
- package/pi-extensions/cwd-history.ts +237 -0
- package/pi-extensions/issues.ts +548 -0
- package/pi-extensions/loop.ts +446 -0
- package/pi-extensions/qna.ts +167 -0
- package/pi-extensions/reveal.ts +689 -0
- package/pi-extensions/review.ts +807 -0
- package/pi-themes/armin.json +81 -0
- package/pi-themes/nightowl.json +82 -0
- package/skills/anachb/SKILL.md +183 -0
- package/skills/anachb/departures.sh +79 -0
- package/skills/anachb/disruptions.sh +53 -0
- package/skills/anachb/route.sh +87 -0
- package/skills/anachb/search.sh +43 -0
- package/skills/ghidra/SKILL.md +254 -0
- package/skills/ghidra/scripts/find-ghidra.sh +54 -0
- package/skills/ghidra/scripts/ghidra-analyze.sh +239 -0
- package/skills/ghidra/scripts/ghidra_scripts/ExportAll.java +278 -0
- package/skills/ghidra/scripts/ghidra_scripts/ExportCalls.java +148 -0
- package/skills/ghidra/scripts/ghidra_scripts/ExportDecompiled.java +84 -0
- package/skills/ghidra/scripts/ghidra_scripts/ExportFunctions.java +114 -0
- package/skills/ghidra/scripts/ghidra_scripts/ExportStrings.java +123 -0
- package/skills/ghidra/scripts/ghidra_scripts/ExportSymbols.java +135 -0
- package/skills/github/SKILL.md +47 -0
- package/skills/improve-skill/SKILL.md +155 -0
- package/skills/improve-skill/scripts/extract-session.js +349 -0
- package/skills/oebb-scotty/SKILL.md +429 -0
- package/skills/oebb-scotty/arrivals.sh +83 -0
- package/skills/oebb-scotty/departures.sh +83 -0
- package/skills/oebb-scotty/disruptions.sh +33 -0
- package/skills/oebb-scotty/search-station.sh +36 -0
- package/skills/oebb-scotty/trip.sh +119 -0
- package/skills/openscad/SKILL.md +232 -0
- package/skills/openscad/examples/parametric_box.scad +92 -0
- package/skills/openscad/examples/phone_stand.scad +95 -0
- package/skills/openscad/tools/common.sh +50 -0
- package/skills/openscad/tools/export-stl.sh +56 -0
- package/skills/openscad/tools/extract-params.sh +147 -0
- package/skills/openscad/tools/multi-preview.sh +68 -0
- package/skills/openscad/tools/preview.sh +74 -0
- package/skills/openscad/tools/render-with-params.sh +91 -0
- package/skills/openscad/tools/validate.sh +46 -0
- package/skills/pi-share/SKILL.md +105 -0
- package/skills/pi-share/fetch-session.mjs +322 -0
- package/skills/sentry/SKILL.md +239 -0
- package/skills/sentry/lib/auth.js +99 -0
- package/skills/sentry/scripts/fetch-event.js +329 -0
- package/skills/sentry/scripts/fetch-issue.js +356 -0
- package/skills/sentry/scripts/list-issues.js +239 -0
- package/skills/sentry/scripts/search-events.js +291 -0
- package/skills/sentry/scripts/search-logs.js +240 -0
- package/skills/tmux/SKILL.md +105 -0
- package/skills/tmux/scripts/find-sessions.sh +112 -0
- package/skills/tmux/scripts/wait-for-text.sh +83 -0
- package/skills/web-browser/SKILL.md +91 -0
- package/skills/web-browser/scripts/cdp.js +210 -0
- package/skills/web-browser/scripts/dismiss-cookies.js +373 -0
- package/skills/web-browser/scripts/eval.js +68 -0
- package/skills/web-browser/scripts/logs-tail.js +69 -0
- package/skills/web-browser/scripts/nav.js +65 -0
- package/skills/web-browser/scripts/net-summary.js +94 -0
- package/skills/web-browser/scripts/package-lock.json +33 -0
- package/skills/web-browser/scripts/package.json +6 -0
- package/skills/web-browser/scripts/pick.js +165 -0
- package/skills/web-browser/scripts/screenshot.js +52 -0
- package/skills/web-browser/scripts/start.js +80 -0
- package/skills/web-browser/scripts/watch.js +266 -0
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# Search for train connections between two stations
|
|
3
|
+
# Usage: ./trip.sh <from-station> <to-station> [date] [time] [num-results]
|
|
4
|
+
#
|
|
5
|
+
# Date format: YYYYMMDD (default: today)
|
|
6
|
+
# Time format: HHMM (default: now)
|
|
7
|
+
# Example: ./trip.sh "Wien Hbf" "Salzburg Hbf" 20260109 0800 5
|
|
8
|
+
|
|
9
|
+
FROM="${1:-}"
|
|
10
|
+
TO="${2:-}"
|
|
11
|
+
DATE="${3:-$(date +%Y%m%d)}"
|
|
12
|
+
TIME="${4:-$(date +%H%M)}00"
|
|
13
|
+
NUM="${5:-5}"
|
|
14
|
+
|
|
15
|
+
if [ -z "$FROM" ] || [ -z "$TO" ]; then
|
|
16
|
+
echo "Usage: $0 <from-station> <to-station> [date] [time] [num-results]"
|
|
17
|
+
echo "Example: $0 'Wien Hbf' 'Salzburg Hbf' 20260109 0800 5"
|
|
18
|
+
exit 1
|
|
19
|
+
fi
|
|
20
|
+
|
|
21
|
+
# Ensure time has seconds
|
|
22
|
+
if [ ${#TIME} -eq 4 ]; then
|
|
23
|
+
TIME="${TIME}00"
|
|
24
|
+
fi
|
|
25
|
+
|
|
26
|
+
# First, resolve station names to LIDs
|
|
27
|
+
FROM_LID=$(curl -s -X POST "https://fahrplan.oebb.at/bin/mgate.exe" \
|
|
28
|
+
-H "Content-Type: application/json" \
|
|
29
|
+
-d '{
|
|
30
|
+
"id":"1","ver":"1.67","lang":"deu",
|
|
31
|
+
"auth":{"type":"AID","aid":"OWDL4fE4ixNiPBBm"},
|
|
32
|
+
"client":{"id":"OEBB","type":"WEB","name":"webapp","l":"vs_webapp"},
|
|
33
|
+
"formatted":false,
|
|
34
|
+
"svcReqL":[{
|
|
35
|
+
"req":{"input":{"field":"S","loc":{"name":"'"$FROM"'","type":"S"},"maxLoc":1}},
|
|
36
|
+
"meth":"LocMatch"
|
|
37
|
+
}]
|
|
38
|
+
}' | jq -r '.svcResL[0].res.match.locL[0].lid // empty')
|
|
39
|
+
|
|
40
|
+
TO_LID=$(curl -s -X POST "https://fahrplan.oebb.at/bin/mgate.exe" \
|
|
41
|
+
-H "Content-Type: application/json" \
|
|
42
|
+
-d '{
|
|
43
|
+
"id":"1","ver":"1.67","lang":"deu",
|
|
44
|
+
"auth":{"type":"AID","aid":"OWDL4fE4ixNiPBBm"},
|
|
45
|
+
"client":{"id":"OEBB","type":"WEB","name":"webapp","l":"vs_webapp"},
|
|
46
|
+
"formatted":false,
|
|
47
|
+
"svcReqL":[{
|
|
48
|
+
"req":{"input":{"field":"S","loc":{"name":"'"$TO"'","type":"S"},"maxLoc":1}},
|
|
49
|
+
"meth":"LocMatch"
|
|
50
|
+
}]
|
|
51
|
+
}' | jq -r '.svcResL[0].res.match.locL[0].lid // empty')
|
|
52
|
+
|
|
53
|
+
if [ -z "$FROM_LID" ]; then
|
|
54
|
+
echo "Error: Could not find station: $FROM"
|
|
55
|
+
exit 1
|
|
56
|
+
fi
|
|
57
|
+
|
|
58
|
+
if [ -z "$TO_LID" ]; then
|
|
59
|
+
echo "Error: Could not find station: $TO"
|
|
60
|
+
exit 1
|
|
61
|
+
fi
|
|
62
|
+
|
|
63
|
+
# Now search for trips
|
|
64
|
+
curl -s -X POST "https://fahrplan.oebb.at/bin/mgate.exe" \
|
|
65
|
+
-H "Content-Type: application/json" \
|
|
66
|
+
-d '{
|
|
67
|
+
"id":"1","ver":"1.67","lang":"deu",
|
|
68
|
+
"auth":{"type":"AID","aid":"OWDL4fE4ixNiPBBm"},
|
|
69
|
+
"client":{"id":"OEBB","type":"WEB","name":"webapp","l":"vs_webapp"},
|
|
70
|
+
"formatted":false,
|
|
71
|
+
"svcReqL":[{
|
|
72
|
+
"req":{
|
|
73
|
+
"depLocL":[{"lid":"'"$FROM_LID"'","type":"S"}],
|
|
74
|
+
"arrLocL":[{"lid":"'"$TO_LID"'","type":"S"}],
|
|
75
|
+
"jnyFltrL":[{"type":"PROD","mode":"INC","value":"1023"}],
|
|
76
|
+
"getPolyline":false,
|
|
77
|
+
"getPasslist":true,
|
|
78
|
+
"outDate":"'"$DATE"'",
|
|
79
|
+
"outTime":"'"$TIME"'",
|
|
80
|
+
"outFrwd":true,
|
|
81
|
+
"numF":'"$NUM"'
|
|
82
|
+
},
|
|
83
|
+
"meth":"TripSearch"
|
|
84
|
+
}]
|
|
85
|
+
}' | jq '
|
|
86
|
+
.svcResL[0].res as $res |
|
|
87
|
+
$res.outConL[] | {
|
|
88
|
+
date: .date,
|
|
89
|
+
departure: {
|
|
90
|
+
time: (.dep.dTimeS | "\(.[0:2]):\(.[2:4])"),
|
|
91
|
+
timeReal: (if .dep.dTimeR then (.dep.dTimeR | "\(.[0:2]):\(.[2:4])") else null end),
|
|
92
|
+
platform: .dep.dPltfS.txt,
|
|
93
|
+
station: ($res.common.locL[.dep.locX].name)
|
|
94
|
+
},
|
|
95
|
+
arrival: {
|
|
96
|
+
time: (.arr.aTimeS | "\(.[0:2]):\(.[2:4])"),
|
|
97
|
+
timeReal: (if .arr.aTimeR then (.arr.aTimeR | "\(.[0:2]):\(.[2:4])") else null end),
|
|
98
|
+
platform: .arr.aPltfS.txt,
|
|
99
|
+
station: ($res.common.locL[.arr.locX].name)
|
|
100
|
+
},
|
|
101
|
+
duration: (.dur | "\(.[0:2])h \(.[2:4])m"),
|
|
102
|
+
changes: .chg,
|
|
103
|
+
legs: [.secL[] | select(.type == "JNY") | {
|
|
104
|
+
train: ($res.common.prodL[.jny.prodX].name // "Train"),
|
|
105
|
+
category: ($res.common.prodL[.jny.prodX].prodCtx.catOutL // ""),
|
|
106
|
+
direction: .jny.dirTxt,
|
|
107
|
+
departure: {
|
|
108
|
+
time: (.dep.dTimeS | "\(.[0:2]):\(.[2:4])"),
|
|
109
|
+
station: ($res.common.locL[.dep.locX].name),
|
|
110
|
+
platform: .dep.dPltfS.txt
|
|
111
|
+
},
|
|
112
|
+
arrival: {
|
|
113
|
+
time: (.arr.aTimeS | "\(.[0:2]):\(.[2:4])"),
|
|
114
|
+
station: ($res.common.locL[.arr.locX].name),
|
|
115
|
+
platform: .arr.aPltfS.txt
|
|
116
|
+
}
|
|
117
|
+
}]
|
|
118
|
+
}
|
|
119
|
+
'
|
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: openscad
|
|
3
|
+
description: "Create and render OpenSCAD 3D models. Generate preview images from multiple angles, extract customizable parameters, validate syntax, and export STL files for 3D printing platforms like MakerWorld."
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# OpenSCAD Skill
|
|
7
|
+
|
|
8
|
+
Create, validate, and export OpenSCAD 3D models. Supports parameter customization, visual preview from multiple angles, and STL export for 3D printing platforms like MakerWorld.
|
|
9
|
+
|
|
10
|
+
## Prerequisites
|
|
11
|
+
|
|
12
|
+
OpenSCAD must be installed. Install via Homebrew:
|
|
13
|
+
```bash
|
|
14
|
+
brew install openscad
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Tools
|
|
18
|
+
|
|
19
|
+
This skill provides several tools in the `tools/` directory:
|
|
20
|
+
|
|
21
|
+
### Preview Generation
|
|
22
|
+
```bash
|
|
23
|
+
# Generate a single preview image
|
|
24
|
+
./tools/preview.sh model.scad output.png [--camera=x,y,z,tx,ty,tz,dist] [--size=800x600]
|
|
25
|
+
|
|
26
|
+
# Generate multi-angle preview (front, back, left, right, top, iso)
|
|
27
|
+
./tools/multi-preview.sh model.scad output_dir/
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
### STL Export
|
|
31
|
+
```bash
|
|
32
|
+
# Export to STL for 3D printing
|
|
33
|
+
./tools/export-stl.sh model.scad output.stl [-D 'param=value']
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
### Parameter Extraction
|
|
37
|
+
```bash
|
|
38
|
+
# Extract customizable parameters from an OpenSCAD file
|
|
39
|
+
./tools/extract-params.sh model.scad
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
### Validation
|
|
43
|
+
```bash
|
|
44
|
+
# Check for syntax errors and warnings
|
|
45
|
+
./tools/validate.sh model.scad
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## Visual Validation (Required)
|
|
49
|
+
|
|
50
|
+
**Always validate your OpenSCAD models visually after creating or modifying them.**
|
|
51
|
+
|
|
52
|
+
After writing or editing any OpenSCAD file:
|
|
53
|
+
|
|
54
|
+
1. **Generate multi-angle previews** using `multi-preview.sh`
|
|
55
|
+
2. **View each generated image** using the `read` tool
|
|
56
|
+
3. **Check for issues** from multiple perspectives:
|
|
57
|
+
- Front/back: Verify symmetry, features, and proportions
|
|
58
|
+
- Left/right: Check depth and side profiles
|
|
59
|
+
- Top: Ensure top features are correct
|
|
60
|
+
- Isometric: Overall shape validation
|
|
61
|
+
4. **Iterate if needed**: If something looks wrong, fix the code and re-validate
|
|
62
|
+
|
|
63
|
+
This catches issues that syntax validation alone cannot detect:
|
|
64
|
+
- Inverted normals or inside-out geometry
|
|
65
|
+
- Misaligned features or incorrect boolean operations
|
|
66
|
+
- Proportions that don't match the intended design
|
|
67
|
+
- Missing or floating geometry
|
|
68
|
+
- Z-fighting or overlapping surfaces
|
|
69
|
+
|
|
70
|
+
**Never deliver an OpenSCAD model without visually confirming it looks correct from multiple angles.**
|
|
71
|
+
|
|
72
|
+
## Workflow
|
|
73
|
+
|
|
74
|
+
### 1. Creating an OpenSCAD Model
|
|
75
|
+
|
|
76
|
+
Write OpenSCAD code with customizable parameters at the top:
|
|
77
|
+
|
|
78
|
+
```openscad
|
|
79
|
+
// Customizable parameters
|
|
80
|
+
wall_thickness = 2; // [1:0.5:5] Wall thickness in mm
|
|
81
|
+
width = 50; // [20:100] Width in mm
|
|
82
|
+
height = 30; // [10:80] Height in mm
|
|
83
|
+
rounded = true; // Add rounded corners
|
|
84
|
+
|
|
85
|
+
// Model code below
|
|
86
|
+
module main_shape() {
|
|
87
|
+
if (rounded) {
|
|
88
|
+
minkowski() {
|
|
89
|
+
cube([width - 4, width - 4, height - 2]);
|
|
90
|
+
sphere(r = 2);
|
|
91
|
+
}
|
|
92
|
+
} else {
|
|
93
|
+
cube([width, width, height]);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
difference() {
|
|
98
|
+
main_shape();
|
|
99
|
+
translate([wall_thickness, wall_thickness, wall_thickness])
|
|
100
|
+
scale([1 - 2*wall_thickness/width, 1 - 2*wall_thickness/width, 1])
|
|
101
|
+
main_shape();
|
|
102
|
+
}
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
Parameter comment format:
|
|
106
|
+
- `// [min:max]` - numeric range
|
|
107
|
+
- `// [min:step:max]` - numeric range with step
|
|
108
|
+
- `// [opt1, opt2, opt3]` - dropdown options
|
|
109
|
+
- `// Description text` - plain description
|
|
110
|
+
|
|
111
|
+
### 2. Validate the Model
|
|
112
|
+
```bash
|
|
113
|
+
./tools/validate.sh model.scad
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
### 3. Generate Previews
|
|
117
|
+
|
|
118
|
+
Generate preview images to visually validate the model:
|
|
119
|
+
```bash
|
|
120
|
+
./tools/multi-preview.sh model.scad ./previews/
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
This creates PNG images from multiple angles. Use the `read` tool to view them.
|
|
124
|
+
|
|
125
|
+
### 4. Export to STL
|
|
126
|
+
```bash
|
|
127
|
+
./tools/export-stl.sh model.scad output.stl
|
|
128
|
+
# With custom parameters:
|
|
129
|
+
./tools/export-stl.sh model.scad output.stl -D 'width=60' -D 'height=40'
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
## Camera Positions
|
|
133
|
+
|
|
134
|
+
Common camera angles for previews:
|
|
135
|
+
- **Isometric**: `--camera=0,0,0,45,0,45,200`
|
|
136
|
+
- **Front**: `--camera=0,0,0,90,0,0,200`
|
|
137
|
+
- **Top**: `--camera=0,0,0,0,0,0,200`
|
|
138
|
+
- **Right**: `--camera=0,0,0,90,0,90,200`
|
|
139
|
+
|
|
140
|
+
Format: `x,y,z,rotx,roty,rotz,distance`
|
|
141
|
+
|
|
142
|
+
## MakerWorld Publishing
|
|
143
|
+
|
|
144
|
+
For MakerWorld, you typically need:
|
|
145
|
+
1. STL file(s) exported via `export-stl.sh`
|
|
146
|
+
2. Preview images (at least one good isometric view)
|
|
147
|
+
3. A description of customizable parameters
|
|
148
|
+
|
|
149
|
+
Consider creating a `model.json` with metadata:
|
|
150
|
+
```json
|
|
151
|
+
{
|
|
152
|
+
"name": "Model Name",
|
|
153
|
+
"description": "Description for MakerWorld",
|
|
154
|
+
"parameters": [...],
|
|
155
|
+
"tags": ["functional", "container", "organizer"]
|
|
156
|
+
}
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
## Example: Full Workflow
|
|
160
|
+
|
|
161
|
+
```bash
|
|
162
|
+
# 1. Create the model (write .scad file)
|
|
163
|
+
|
|
164
|
+
# 2. Validate syntax
|
|
165
|
+
./tools/validate.sh box.scad
|
|
166
|
+
|
|
167
|
+
# 3. Generate multi-angle previews
|
|
168
|
+
./tools/multi-preview.sh box.scad ./previews/
|
|
169
|
+
|
|
170
|
+
# 4. IMPORTANT: View and validate ALL preview images
|
|
171
|
+
# Use the read tool on each PNG file to visually inspect:
|
|
172
|
+
# - previews/box_front.png
|
|
173
|
+
# - previews/box_back.png
|
|
174
|
+
# - previews/box_left.png
|
|
175
|
+
# - previews/box_right.png
|
|
176
|
+
# - previews/box_top.png
|
|
177
|
+
# - previews/box_iso.png
|
|
178
|
+
# Look for geometry issues, misalignments, or unexpected results.
|
|
179
|
+
# If anything looks wrong, go back to step 1 and fix it!
|
|
180
|
+
|
|
181
|
+
# 5. Extract and review parameters
|
|
182
|
+
./tools/extract-params.sh box.scad
|
|
183
|
+
|
|
184
|
+
# 6. Export STL with default parameters
|
|
185
|
+
./tools/export-stl.sh box.scad box.stl
|
|
186
|
+
|
|
187
|
+
# 7. Export STL with custom parameters
|
|
188
|
+
./tools/export-stl.sh box.scad box_large.stl -D 'width=80' -D 'height=60'
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
**Remember**: Never skip the visual validation step. Many issues (wrong dimensions, boolean operation errors, inverted geometry) are only visible when you actually look at the rendered model.
|
|
192
|
+
|
|
193
|
+
## OpenSCAD Quick Reference
|
|
194
|
+
|
|
195
|
+
### Basic Shapes
|
|
196
|
+
```openscad
|
|
197
|
+
cube([x, y, z]);
|
|
198
|
+
sphere(r = radius);
|
|
199
|
+
cylinder(h = height, r = radius);
|
|
200
|
+
cylinder(h = height, r1 = bottom_r, r2 = top_r); // cone
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
### Transformations
|
|
204
|
+
```openscad
|
|
205
|
+
translate([x, y, z]) object();
|
|
206
|
+
rotate([rx, ry, rz]) object();
|
|
207
|
+
scale([sx, sy, sz]) object();
|
|
208
|
+
mirror([x, y, z]) object();
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
### Boolean Operations
|
|
212
|
+
```openscad
|
|
213
|
+
union() { a(); b(); } // combine
|
|
214
|
+
difference() { a(); b(); } // subtract b from a
|
|
215
|
+
intersection() { a(); b(); } // overlap only
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
### Advanced
|
|
219
|
+
```openscad
|
|
220
|
+
linear_extrude(height) 2d_shape();
|
|
221
|
+
rotate_extrude() 2d_shape();
|
|
222
|
+
hull() { objects(); } // convex hull
|
|
223
|
+
minkowski() { a(); b(); } // minkowski sum (rounding)
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
### 2D Shapes
|
|
227
|
+
```openscad
|
|
228
|
+
circle(r = radius);
|
|
229
|
+
square([x, y]);
|
|
230
|
+
polygon(points = [[x1,y1], [x2,y2], ...]);
|
|
231
|
+
text("string", size = 10);
|
|
232
|
+
```
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
// Parametric Box with Lid
|
|
2
|
+
// A customizable storage box for 3D printing
|
|
3
|
+
|
|
4
|
+
// === Box Parameters ===
|
|
5
|
+
width = 60; // [20:200] Width in mm
|
|
6
|
+
depth = 40; // [20:200] Depth in mm
|
|
7
|
+
height = 30; // [10:150] Height in mm
|
|
8
|
+
wall_thickness = 2; // [1:0.5:5] Wall thickness in mm
|
|
9
|
+
|
|
10
|
+
// === Lid Parameters ===
|
|
11
|
+
include_lid = true; // Include a separate lid
|
|
12
|
+
lid_height = 8; // [5:30] Lid height in mm
|
|
13
|
+
lid_tolerance = 0.3; // [0.1:0.1:0.8] Gap for lid fit
|
|
14
|
+
|
|
15
|
+
// === Style Options ===
|
|
16
|
+
corner_radius = 3; // [0:10] Corner rounding radius
|
|
17
|
+
add_grip = true; // Add grip indents to lid
|
|
18
|
+
|
|
19
|
+
// === Internal ===
|
|
20
|
+
$fn = 32; // Smoothness
|
|
21
|
+
|
|
22
|
+
// Rounded box module
|
|
23
|
+
module rounded_box(w, d, h, r) {
|
|
24
|
+
if (r > 0) {
|
|
25
|
+
hull() {
|
|
26
|
+
for (x = [r, w-r]) {
|
|
27
|
+
for (y = [r, d-r]) {
|
|
28
|
+
translate([x, y, 0])
|
|
29
|
+
cylinder(h = h, r = r);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
} else {
|
|
34
|
+
cube([w, d, h]);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Main box body
|
|
39
|
+
module box_body() {
|
|
40
|
+
difference() {
|
|
41
|
+
rounded_box(width, depth, height, corner_radius);
|
|
42
|
+
|
|
43
|
+
// Hollow inside
|
|
44
|
+
translate([wall_thickness, wall_thickness, wall_thickness])
|
|
45
|
+
rounded_box(
|
|
46
|
+
width - 2*wall_thickness,
|
|
47
|
+
depth - 2*wall_thickness,
|
|
48
|
+
height, // Open top
|
|
49
|
+
max(0, corner_radius - wall_thickness)
|
|
50
|
+
);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Lid
|
|
55
|
+
module lid() {
|
|
56
|
+
inner_w = width - 2*wall_thickness - 2*lid_tolerance;
|
|
57
|
+
inner_d = depth - 2*wall_thickness - 2*lid_tolerance;
|
|
58
|
+
lip_height = lid_height * 0.6;
|
|
59
|
+
|
|
60
|
+
difference() {
|
|
61
|
+
union() {
|
|
62
|
+
// Top cap
|
|
63
|
+
rounded_box(width, depth, wall_thickness, corner_radius);
|
|
64
|
+
|
|
65
|
+
// Inner lip
|
|
66
|
+
translate([wall_thickness + lid_tolerance, wall_thickness + lid_tolerance, -lip_height + wall_thickness])
|
|
67
|
+
rounded_box(inner_w, inner_d, lip_height, max(0, corner_radius - wall_thickness));
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// Grip indents
|
|
71
|
+
if (add_grip) {
|
|
72
|
+
for (x = [width * 0.3, width * 0.7]) {
|
|
73
|
+
translate([x, -1, wall_thickness/2])
|
|
74
|
+
rotate([-90, 0, 0])
|
|
75
|
+
cylinder(h = 5, r = 3, $fn = 16);
|
|
76
|
+
translate([x, depth - 4, wall_thickness/2])
|
|
77
|
+
rotate([-90, 0, 0])
|
|
78
|
+
cylinder(h = 5, r = 3, $fn = 16);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// Render
|
|
85
|
+
box_body();
|
|
86
|
+
|
|
87
|
+
if (include_lid) {
|
|
88
|
+
// Position lid next to box for printing
|
|
89
|
+
translate([width + 10, 0, lid_height - wall_thickness])
|
|
90
|
+
rotate([180, 0, 0])
|
|
91
|
+
lid();
|
|
92
|
+
}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
// Adjustable Phone/Tablet Stand
|
|
2
|
+
// Parametric stand with customizable angle and size
|
|
3
|
+
|
|
4
|
+
// === Device Parameters ===
|
|
5
|
+
device_width = 80; // [50:200] Device width in mm
|
|
6
|
+
device_thickness = 12; // [6:20] Device thickness (with case)
|
|
7
|
+
|
|
8
|
+
// === Stand Parameters ===
|
|
9
|
+
stand_angle = 65; // [45:85] Viewing angle in degrees
|
|
10
|
+
stand_depth = 80; // [50:150] Base depth in mm
|
|
11
|
+
stand_height = 100; // [60:200] Back support height in mm
|
|
12
|
+
|
|
13
|
+
// === Construction ===
|
|
14
|
+
material_thickness = 4; // [2:0.5:8] Material thickness
|
|
15
|
+
slot_depth = 15; // [10:30] How deep device sits in slot
|
|
16
|
+
|
|
17
|
+
// === Features ===
|
|
18
|
+
cable_hole = true; // Add cable pass-through hole
|
|
19
|
+
cable_diameter = 15; // [8:25] Cable hole diameter
|
|
20
|
+
add_feet = true; // Add anti-slip feet
|
|
21
|
+
|
|
22
|
+
// === Quality ===
|
|
23
|
+
$fn = 48;
|
|
24
|
+
|
|
25
|
+
module stand_profile() {
|
|
26
|
+
// 2D profile of the stand side
|
|
27
|
+
polygon([
|
|
28
|
+
[0, 0], // Front bottom
|
|
29
|
+
[stand_depth, 0], // Back bottom
|
|
30
|
+
[stand_depth, material_thickness], // Back bottom inner
|
|
31
|
+
[stand_depth - material_thickness, material_thickness], // Base top back
|
|
32
|
+
[slot_depth + material_thickness, material_thickness], // Base top front (behind slot)
|
|
33
|
+
[slot_depth + material_thickness, slot_depth * tan(90 - stand_angle) + material_thickness], // Slot back
|
|
34
|
+
[material_thickness, slot_depth * tan(90 - stand_angle) + material_thickness + device_thickness / sin(stand_angle)], // Slot front top
|
|
35
|
+
[0, slot_depth * tan(90 - stand_angle) + material_thickness], // Front face bottom of slot
|
|
36
|
+
[0, 0] // Close
|
|
37
|
+
]);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
module back_support() {
|
|
41
|
+
// Back angled support
|
|
42
|
+
translate([stand_depth - material_thickness, 0, material_thickness]) {
|
|
43
|
+
rotate([0, -90 + stand_angle, 0]) {
|
|
44
|
+
cube([stand_height, device_width, material_thickness]);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
module cable_cutout() {
|
|
50
|
+
if (cable_hole) {
|
|
51
|
+
translate([stand_depth/2, device_width/2, -1])
|
|
52
|
+
cylinder(h = material_thickness + 2, d = cable_diameter);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
module foot() {
|
|
57
|
+
cylinder(h = 2, d1 = 10, d2 = 8);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
module stand() {
|
|
61
|
+
difference() {
|
|
62
|
+
union() {
|
|
63
|
+
// Left side
|
|
64
|
+
linear_extrude(material_thickness)
|
|
65
|
+
stand_profile();
|
|
66
|
+
|
|
67
|
+
// Right side
|
|
68
|
+
translate([0, device_width - material_thickness, 0])
|
|
69
|
+
linear_extrude(material_thickness)
|
|
70
|
+
stand_profile();
|
|
71
|
+
|
|
72
|
+
// Base plate
|
|
73
|
+
cube([stand_depth, device_width, material_thickness]);
|
|
74
|
+
|
|
75
|
+
// Front lip
|
|
76
|
+
cube([material_thickness, device_width, slot_depth * tan(90 - stand_angle) + material_thickness]);
|
|
77
|
+
|
|
78
|
+
// Back support
|
|
79
|
+
back_support();
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// Cable hole
|
|
83
|
+
cable_cutout();
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// Feet
|
|
87
|
+
if (add_feet) {
|
|
88
|
+
translate([10, 10, 0]) foot();
|
|
89
|
+
translate([10, device_width - 10, 0]) foot();
|
|
90
|
+
translate([stand_depth - 10, 10, 0]) foot();
|
|
91
|
+
translate([stand_depth - 10, device_width - 10, 0]) foot();
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
stand();
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# Common utilities for OpenSCAD tools
|
|
3
|
+
|
|
4
|
+
# Find OpenSCAD executable
|
|
5
|
+
find_openscad() {
|
|
6
|
+
# Check common locations
|
|
7
|
+
if command -v openscad &> /dev/null; then
|
|
8
|
+
echo "openscad"
|
|
9
|
+
return 0
|
|
10
|
+
fi
|
|
11
|
+
|
|
12
|
+
# macOS Application bundle
|
|
13
|
+
if [ -d "/Applications/OpenSCAD.app" ]; then
|
|
14
|
+
echo "/Applications/OpenSCAD.app/Contents/MacOS/OpenSCAD"
|
|
15
|
+
return 0
|
|
16
|
+
fi
|
|
17
|
+
|
|
18
|
+
# Homebrew on Apple Silicon
|
|
19
|
+
if [ -x "/opt/homebrew/bin/openscad" ]; then
|
|
20
|
+
echo "/opt/homebrew/bin/openscad"
|
|
21
|
+
return 0
|
|
22
|
+
fi
|
|
23
|
+
|
|
24
|
+
# Homebrew on Intel
|
|
25
|
+
if [ -x "/usr/local/bin/openscad" ]; then
|
|
26
|
+
echo "/usr/local/bin/openscad"
|
|
27
|
+
return 0
|
|
28
|
+
fi
|
|
29
|
+
|
|
30
|
+
return 1
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
# Check if OpenSCAD is available
|
|
34
|
+
check_openscad() {
|
|
35
|
+
OPENSCAD=$(find_openscad) || {
|
|
36
|
+
echo "Error: OpenSCAD not found!"
|
|
37
|
+
echo ""
|
|
38
|
+
echo "Install OpenSCAD using one of:"
|
|
39
|
+
echo " brew install openscad"
|
|
40
|
+
echo " Download from https://openscad.org/downloads.html"
|
|
41
|
+
exit 1
|
|
42
|
+
}
|
|
43
|
+
export OPENSCAD
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
# Get version info
|
|
47
|
+
openscad_version() {
|
|
48
|
+
check_openscad
|
|
49
|
+
$OPENSCAD --version 2>&1
|
|
50
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# Export OpenSCAD file to STL
|
|
3
|
+
# Usage: export-stl.sh input.scad output.stl [-D 'var=value' ...]
|
|
4
|
+
|
|
5
|
+
set -e
|
|
6
|
+
|
|
7
|
+
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
8
|
+
source "$SCRIPT_DIR/common.sh"
|
|
9
|
+
|
|
10
|
+
check_openscad
|
|
11
|
+
|
|
12
|
+
if [ $# -lt 2 ]; then
|
|
13
|
+
echo "Usage: $0 input.scad output.stl [-D 'var=value' ...]"
|
|
14
|
+
echo ""
|
|
15
|
+
echo "Examples:"
|
|
16
|
+
echo " $0 box.scad box.stl"
|
|
17
|
+
echo " $0 box.scad box_large.stl -D 'width=80' -D 'height=60'"
|
|
18
|
+
exit 1
|
|
19
|
+
fi
|
|
20
|
+
|
|
21
|
+
INPUT="$1"
|
|
22
|
+
OUTPUT="$2"
|
|
23
|
+
shift 2
|
|
24
|
+
|
|
25
|
+
# Collect -D parameters
|
|
26
|
+
DEFINES=()
|
|
27
|
+
while [ $# -gt 0 ]; do
|
|
28
|
+
case "$1" in
|
|
29
|
+
-D)
|
|
30
|
+
shift
|
|
31
|
+
DEFINES+=("-D" "$1")
|
|
32
|
+
;;
|
|
33
|
+
*)
|
|
34
|
+
echo "Unknown option: $1"
|
|
35
|
+
exit 1
|
|
36
|
+
;;
|
|
37
|
+
esac
|
|
38
|
+
shift
|
|
39
|
+
done
|
|
40
|
+
|
|
41
|
+
# Ensure output directory exists
|
|
42
|
+
mkdir -p "$(dirname "$OUTPUT")"
|
|
43
|
+
|
|
44
|
+
echo "Exporting STL: $INPUT -> $OUTPUT"
|
|
45
|
+
if [ ${#DEFINES[@]} -gt 0 ]; then
|
|
46
|
+
echo "Parameters: ${DEFINES[*]}"
|
|
47
|
+
fi
|
|
48
|
+
|
|
49
|
+
$OPENSCAD \
|
|
50
|
+
"${DEFINES[@]}" \
|
|
51
|
+
-o "$OUTPUT" \
|
|
52
|
+
"$INPUT"
|
|
53
|
+
|
|
54
|
+
# Show file info
|
|
55
|
+
SIZE=$(ls -lh "$OUTPUT" | awk '{print $5}')
|
|
56
|
+
echo "STL exported: $OUTPUT ($SIZE)"
|