shipwright-cli 1.10.0 → 2.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/README.md +114 -36
- package/completions/_shipwright +212 -32
- package/completions/shipwright.bash +97 -25
- package/docs/strategy/01-market-research.md +619 -0
- package/docs/strategy/02-mission-and-brand.md +587 -0
- package/docs/strategy/03-gtm-and-roadmap.md +759 -0
- package/docs/strategy/QUICK-START.txt +289 -0
- package/docs/strategy/README.md +172 -0
- package/package.json +4 -2
- package/scripts/sw +208 -1
- package/scripts/sw-activity.sh +500 -0
- package/scripts/sw-adaptive.sh +925 -0
- package/scripts/sw-adversarial.sh +1 -1
- package/scripts/sw-architecture-enforcer.sh +1 -1
- package/scripts/sw-auth.sh +613 -0
- package/scripts/sw-autonomous.sh +664 -0
- package/scripts/sw-changelog.sh +704 -0
- package/scripts/sw-checkpoint.sh +1 -1
- package/scripts/sw-ci.sh +602 -0
- package/scripts/sw-cleanup.sh +1 -1
- package/scripts/sw-code-review.sh +637 -0
- package/scripts/sw-connect.sh +1 -1
- package/scripts/sw-context.sh +605 -0
- package/scripts/sw-cost.sh +1 -1
- package/scripts/sw-daemon.sh +432 -130
- package/scripts/sw-dashboard.sh +1 -1
- package/scripts/sw-db.sh +540 -0
- package/scripts/sw-decompose.sh +539 -0
- package/scripts/sw-deps.sh +551 -0
- package/scripts/sw-developer-simulation.sh +1 -1
- package/scripts/sw-discovery.sh +412 -0
- package/scripts/sw-docs-agent.sh +539 -0
- package/scripts/sw-docs.sh +1 -1
- package/scripts/sw-doctor.sh +59 -1
- package/scripts/sw-dora.sh +615 -0
- package/scripts/sw-durable.sh +710 -0
- package/scripts/sw-e2e-orchestrator.sh +535 -0
- package/scripts/sw-eventbus.sh +393 -0
- package/scripts/sw-feedback.sh +471 -0
- package/scripts/sw-fix.sh +1 -1
- package/scripts/sw-fleet-discover.sh +567 -0
- package/scripts/sw-fleet-viz.sh +404 -0
- package/scripts/sw-fleet.sh +8 -1
- package/scripts/sw-github-app.sh +596 -0
- package/scripts/sw-github-checks.sh +1 -1
- package/scripts/sw-github-deploy.sh +1 -1
- package/scripts/sw-github-graphql.sh +1 -1
- package/scripts/sw-guild.sh +569 -0
- package/scripts/sw-heartbeat.sh +1 -1
- package/scripts/sw-hygiene.sh +559 -0
- package/scripts/sw-incident.sh +617 -0
- package/scripts/sw-init.sh +88 -1
- package/scripts/sw-instrument.sh +699 -0
- package/scripts/sw-intelligence.sh +1 -1
- package/scripts/sw-jira.sh +1 -1
- package/scripts/sw-launchd.sh +363 -28
- package/scripts/sw-linear.sh +1 -1
- package/scripts/sw-logs.sh +1 -1
- package/scripts/sw-loop.sh +64 -3
- package/scripts/sw-memory.sh +1 -1
- package/scripts/sw-mission-control.sh +487 -0
- package/scripts/sw-model-router.sh +545 -0
- package/scripts/sw-otel.sh +596 -0
- package/scripts/sw-oversight.sh +689 -0
- package/scripts/sw-pipeline-composer.sh +1 -1
- package/scripts/sw-pipeline-vitals.sh +1 -1
- package/scripts/sw-pipeline.sh +687 -24
- package/scripts/sw-pm.sh +693 -0
- package/scripts/sw-pr-lifecycle.sh +522 -0
- package/scripts/sw-predictive.sh +1 -1
- package/scripts/sw-prep.sh +1 -1
- package/scripts/sw-ps.sh +1 -1
- package/scripts/sw-public-dashboard.sh +798 -0
- package/scripts/sw-quality.sh +595 -0
- package/scripts/sw-reaper.sh +1 -1
- package/scripts/sw-recruit.sh +573 -0
- package/scripts/sw-regression.sh +642 -0
- package/scripts/sw-release-manager.sh +736 -0
- package/scripts/sw-release.sh +706 -0
- package/scripts/sw-remote.sh +1 -1
- package/scripts/sw-replay.sh +520 -0
- package/scripts/sw-retro.sh +691 -0
- package/scripts/sw-scale.sh +444 -0
- package/scripts/sw-security-audit.sh +505 -0
- package/scripts/sw-self-optimize.sh +1 -1
- package/scripts/sw-session.sh +1 -1
- package/scripts/sw-setup.sh +1 -1
- package/scripts/sw-standup.sh +712 -0
- package/scripts/sw-status.sh +1 -1
- package/scripts/sw-strategic.sh +658 -0
- package/scripts/sw-stream.sh +450 -0
- package/scripts/sw-swarm.sh +583 -0
- package/scripts/sw-team-stages.sh +511 -0
- package/scripts/sw-templates.sh +1 -1
- package/scripts/sw-testgen.sh +515 -0
- package/scripts/sw-tmux-pipeline.sh +554 -0
- package/scripts/sw-tmux.sh +1 -1
- package/scripts/sw-trace.sh +485 -0
- package/scripts/sw-tracker-github.sh +188 -0
- package/scripts/sw-tracker-jira.sh +172 -0
- package/scripts/sw-tracker-linear.sh +251 -0
- package/scripts/sw-tracker.sh +117 -2
- package/scripts/sw-triage.sh +603 -0
- package/scripts/sw-upgrade.sh +1 -1
- package/scripts/sw-ux.sh +677 -0
- package/scripts/sw-webhook.sh +627 -0
- package/scripts/sw-widgets.sh +530 -0
- package/scripts/sw-worktree.sh +1 -1
package/scripts/sw-ux.sh
ADDED
|
@@ -0,0 +1,677 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# ╔═══════════════════════════════════════════════════════════════════════════╗
|
|
3
|
+
# ║ shipwright ux — Premium UX Enhancement Layer ║
|
|
4
|
+
# ║ Themes · Animations · Keyboard Shortcuts · Accessibility · Formatting ║
|
|
5
|
+
# ╚═══════════════════════════════════════════════════════════════════════════╝
|
|
6
|
+
set -euo pipefail
|
|
7
|
+
trap 'echo "ERROR: $BASH_SOURCE:$LINENO exited with status $?" >&2' ERR
|
|
8
|
+
|
|
9
|
+
VERSION="2.0.0"
|
|
10
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
11
|
+
|
|
12
|
+
# ─── Colors (matches Seth's tmux theme) ─────────────────────────────────────
|
|
13
|
+
CYAN='\033[38;2;0;212;255m' # #00d4ff — primary accent
|
|
14
|
+
PURPLE='\033[38;2;124;58;237m' # #7c3aed — secondary
|
|
15
|
+
BLUE='\033[38;2;0;102;255m' # #0066ff — tertiary
|
|
16
|
+
GREEN='\033[38;2;74;222;128m' # success
|
|
17
|
+
YELLOW='\033[38;2;250;204;21m' # warning
|
|
18
|
+
RED='\033[38;2;248;113;113m' # error
|
|
19
|
+
DIM='\033[2m'
|
|
20
|
+
BOLD='\033[1m'
|
|
21
|
+
RESET='\033[0m'
|
|
22
|
+
|
|
23
|
+
# ─── Cross-platform compatibility ──────────────────────────────────────────
|
|
24
|
+
_COMPAT="$SCRIPT_DIR/lib/compat.sh"
|
|
25
|
+
# shellcheck source=lib/compat.sh
|
|
26
|
+
[[ -f "$_COMPAT" ]] && source "$_COMPAT"
|
|
27
|
+
|
|
28
|
+
# ─── Output Helpers ────────────────────────────────────────────────────────
|
|
29
|
+
info() { echo -e "${CYAN}${BOLD}▸${RESET} $*"; }
|
|
30
|
+
success() { echo -e "${GREEN}${BOLD}✓${RESET} $*"; }
|
|
31
|
+
warn() { echo -e "${YELLOW}${BOLD}⚠${RESET} $*"; }
|
|
32
|
+
error() { echo -e "${RED}${BOLD}✗${RESET} $*" >&2; }
|
|
33
|
+
|
|
34
|
+
# ─── Structured Event Log ──────────────────────────────────────────────────
|
|
35
|
+
EVENTS_FILE="${HOME}/.shipwright/events.jsonl"
|
|
36
|
+
|
|
37
|
+
emit_event() {
|
|
38
|
+
local event_type="$1"
|
|
39
|
+
shift
|
|
40
|
+
local json_fields=""
|
|
41
|
+
for kv in "$@"; do
|
|
42
|
+
local key="${kv%%=*}"
|
|
43
|
+
local val="${kv#*=}"
|
|
44
|
+
if [[ "$val" =~ ^-?[0-9]+\.?[0-9]*$ ]]; then
|
|
45
|
+
json_fields="${json_fields},\"${key}\":${val}"
|
|
46
|
+
else
|
|
47
|
+
val="${val//\"/\\\"}"
|
|
48
|
+
json_fields="${json_fields},\"${key}\":\"${val}\""
|
|
49
|
+
fi
|
|
50
|
+
done
|
|
51
|
+
mkdir -p "${HOME}/.shipwright"
|
|
52
|
+
echo "{\"ts\":\"$(date -u +"%Y-%m-%dT%H:%M:%SZ")\",\"type\":\"${event_type}\"${json_fields}}" >> "$EVENTS_FILE" || true
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
# ─── UX Configuration ──────────────────────────────────────────────────────
|
|
56
|
+
UX_CONFIG_GLOBAL="${HOME}/.shipwright/ux-config.json"
|
|
57
|
+
UX_CONFIG_REPO="./.claude/ux-config.json"
|
|
58
|
+
|
|
59
|
+
# Detect accessibility modes
|
|
60
|
+
should_disable_colors() {
|
|
61
|
+
[[ -n "${NO_COLOR:-}" ]] || [[ -n "${CLICOLOR_FORCE:-}" && "$CLICOLOR_FORCE" == "0" ]]
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
has_reduced_motion() {
|
|
65
|
+
[[ -n "${PREFERS_REDUCED_MOTION:-}" ]]
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
# ─── Initialize UX Config ──────────────────────────────────────────────────
|
|
69
|
+
init_ux_config() {
|
|
70
|
+
local config_file="$1"
|
|
71
|
+
mkdir -p "$(dirname "$config_file")"
|
|
72
|
+
|
|
73
|
+
if [[ ! -f "$config_file" ]]; then
|
|
74
|
+
cat > "$config_file" <<'EOF'
|
|
75
|
+
{
|
|
76
|
+
"theme": "dark",
|
|
77
|
+
"spinner": "dots",
|
|
78
|
+
"animations_enabled": true,
|
|
79
|
+
"sound_enabled": false,
|
|
80
|
+
"reduced_motion": false,
|
|
81
|
+
"high_contrast": false,
|
|
82
|
+
"theme_custom": {}
|
|
83
|
+
}
|
|
84
|
+
EOF
|
|
85
|
+
fi
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
# ─── Theme System ──────────────────────────────────────────────────────────
|
|
89
|
+
|
|
90
|
+
# Theme data using functions (bash 3.2 compatible - no associative arrays)
|
|
91
|
+
theme_dark() {
|
|
92
|
+
echo "primary=#00d4ff"
|
|
93
|
+
echo "secondary=#7c3aed"
|
|
94
|
+
echo "tertiary=#0066ff"
|
|
95
|
+
echo "success=#4ade80"
|
|
96
|
+
echo "warning=#f59e0b"
|
|
97
|
+
echo "error=#f87171"
|
|
98
|
+
echo "bg=#0f172a"
|
|
99
|
+
echo "fg=#f1f5f9"
|
|
100
|
+
echo "dim=#64748b"
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
theme_light() {
|
|
104
|
+
echo "primary=#0066ff"
|
|
105
|
+
echo "secondary=#7c3aed"
|
|
106
|
+
echo "tertiary=#06b6d4"
|
|
107
|
+
echo "success=#059669"
|
|
108
|
+
echo "warning=#d97706"
|
|
109
|
+
echo "error=#dc2626"
|
|
110
|
+
echo "bg=#ffffff"
|
|
111
|
+
echo "fg=#1e293b"
|
|
112
|
+
echo "dim=#94a3b8"
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
theme_minimal() {
|
|
116
|
+
echo "primary=#000000"
|
|
117
|
+
echo "secondary=#808080"
|
|
118
|
+
echo "tertiary=#808080"
|
|
119
|
+
echo "success=#000000"
|
|
120
|
+
echo "warning=#000000"
|
|
121
|
+
echo "error=#000000"
|
|
122
|
+
echo "bg=#ffffff"
|
|
123
|
+
echo "fg=#000000"
|
|
124
|
+
echo "dim=#cccccc"
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
theme_cyberpunk() {
|
|
128
|
+
echo "primary=#ff006e"
|
|
129
|
+
echo "secondary=#00f5ff"
|
|
130
|
+
echo "tertiary=#ffbe0b"
|
|
131
|
+
echo "success=#00ff41"
|
|
132
|
+
echo "warning=#ffbe0b"
|
|
133
|
+
echo "error=#ff006e"
|
|
134
|
+
echo "bg=#0a0e27"
|
|
135
|
+
echo "fg=#00f5ff"
|
|
136
|
+
echo "dim=#555580"
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
theme_ocean() {
|
|
140
|
+
echo "primary=#0ea5e9"
|
|
141
|
+
echo "secondary=#06b6d4"
|
|
142
|
+
echo "tertiary=#0891b2"
|
|
143
|
+
echo "success=#10b981"
|
|
144
|
+
echo "warning=#f59e0b"
|
|
145
|
+
echo "error=#ef4444"
|
|
146
|
+
echo "bg=#001f3f"
|
|
147
|
+
echo "fg=#e0f2fe"
|
|
148
|
+
echo "dim=#38bdf8"
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
get_theme() {
|
|
152
|
+
local theme_name="${1:-dark}"
|
|
153
|
+
case "$theme_name" in
|
|
154
|
+
light) theme_light ;;
|
|
155
|
+
minimal) theme_minimal ;;
|
|
156
|
+
cyberpunk) theme_cyberpunk ;;
|
|
157
|
+
ocean) theme_ocean ;;
|
|
158
|
+
*) theme_dark ;;
|
|
159
|
+
esac
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
hex_to_rgb() {
|
|
163
|
+
local hex="${1#\#}"
|
|
164
|
+
printf '%d;%d;%d' 0x"${hex:0:2}" 0x"${hex:2:2}" 0x"${hex:4:2}"
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
# Get color code from theme
|
|
168
|
+
get_color() {
|
|
169
|
+
local color_name="$1"
|
|
170
|
+
local theme="${2:-dark}"
|
|
171
|
+
|
|
172
|
+
if should_disable_colors; then
|
|
173
|
+
return 0
|
|
174
|
+
fi
|
|
175
|
+
|
|
176
|
+
local hex
|
|
177
|
+
hex=$(get_theme "$theme" | grep "^${color_name}=" | cut -d= -f2)
|
|
178
|
+
|
|
179
|
+
if [[ -n "$hex" ]]; then
|
|
180
|
+
local rgb
|
|
181
|
+
rgb=$(hex_to_rgb "$hex")
|
|
182
|
+
echo -ne "\033[38;2;${rgb}m"
|
|
183
|
+
fi
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
list_themes() {
|
|
187
|
+
info "Available themes:"
|
|
188
|
+
for theme in dark light minimal cyberpunk ocean; do
|
|
189
|
+
echo " ${CYAN}${theme}${RESET}"
|
|
190
|
+
done
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
set_theme() {
|
|
194
|
+
local theme="$1"
|
|
195
|
+
|
|
196
|
+
init_ux_config "$UX_CONFIG_GLOBAL"
|
|
197
|
+
|
|
198
|
+
if jq --arg theme "$theme" '.theme = $theme' "$UX_CONFIG_GLOBAL" > "${UX_CONFIG_GLOBAL}.tmp" 2>/dev/null; then
|
|
199
|
+
mv "${UX_CONFIG_GLOBAL}.tmp" "$UX_CONFIG_GLOBAL"
|
|
200
|
+
success "Theme set to: ${CYAN}${theme}${RESET}"
|
|
201
|
+
emit_event "ux_theme_changed" "theme=$theme"
|
|
202
|
+
else
|
|
203
|
+
error "Failed to set theme"
|
|
204
|
+
return 1
|
|
205
|
+
fi
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
preview_theme() {
|
|
209
|
+
local theme="${1:-dark}"
|
|
210
|
+
|
|
211
|
+
echo ""
|
|
212
|
+
info "Preview of ${CYAN}${theme}${RESET} theme:"
|
|
213
|
+
echo ""
|
|
214
|
+
|
|
215
|
+
while IFS='=' read -r key value; do
|
|
216
|
+
[[ -z "$key" ]] && continue
|
|
217
|
+
local rgb
|
|
218
|
+
rgb=$(hex_to_rgb "$value")
|
|
219
|
+
printf " %-12s ${value} \033[38;2;${rgb}m████████████${RESET}\n" "$key"
|
|
220
|
+
done < <(get_theme "$theme")
|
|
221
|
+
echo ""
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
# ─── Spinner Animations ────────────────────────────────────────────────────
|
|
225
|
+
|
|
226
|
+
spinner_dots() {
|
|
227
|
+
echo "⠋ ⠙ ⠹ ⠸ ⠼ ⠴ ⠦ ⠧ ⠇ ⠏"
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
spinner_braille() {
|
|
231
|
+
echo "⠋ ⠙ ⠚ ⠒ ⠂ ⠂ ⠒ ⠲ ⠴ ⠦"
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
spinner_moon() {
|
|
235
|
+
echo "🌑 🌒 🌓 🌔 🌕 🌖 🌗 🌘"
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
spinner_arrows() {
|
|
239
|
+
echo "← ↖ ↑ ↗ → ↘ ↓ ↙"
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
spinner_bounce() {
|
|
243
|
+
echo "⠁ ⠂ ⠄ ⠂"
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
get_spinner() {
|
|
247
|
+
local style="${1:-dots}"
|
|
248
|
+
case "$style" in
|
|
249
|
+
braille) spinner_braille ;;
|
|
250
|
+
moon) spinner_moon ;;
|
|
251
|
+
arrows) spinner_arrows ;;
|
|
252
|
+
bounce) spinner_bounce ;;
|
|
253
|
+
*) spinner_dots ;;
|
|
254
|
+
esac
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
animate_spinner() {
|
|
258
|
+
local message="$1"
|
|
259
|
+
local duration="${2:-10}"
|
|
260
|
+
local style="${3:-dots}"
|
|
261
|
+
|
|
262
|
+
if has_reduced_motion; then
|
|
263
|
+
echo "⏳ $message"
|
|
264
|
+
sleep "$duration"
|
|
265
|
+
return 0
|
|
266
|
+
fi
|
|
267
|
+
|
|
268
|
+
local spinner_str
|
|
269
|
+
spinner_str="$(get_spinner "$style")"
|
|
270
|
+
local i=0
|
|
271
|
+
local end_time=$(($(date +%s) + duration))
|
|
272
|
+
|
|
273
|
+
while [[ $(date +%s) -lt $end_time ]]; do
|
|
274
|
+
# Parse space-separated spinner frames
|
|
275
|
+
local frame
|
|
276
|
+
frame=$(echo "$spinner_str" | awk '{for(n=1;n<=NF;n++) if((n-1)==('$((i % 10))')) print $n}')
|
|
277
|
+
printf "\r${frame} $message"
|
|
278
|
+
i=$((i + 1))
|
|
279
|
+
sleep 0.1
|
|
280
|
+
done
|
|
281
|
+
printf "\r%-80s\r" ""
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
list_spinners() {
|
|
285
|
+
info "Available spinners:"
|
|
286
|
+
for style in dots braille moon arrows bounce; do
|
|
287
|
+
printf " ${CYAN}%-12s${RESET} "
|
|
288
|
+
local spinner_str
|
|
289
|
+
spinner_str="$(get_spinner "$style")"
|
|
290
|
+
echo "$spinner_str" | awk '{for(i=1;i<=3 && i<=NF;i++) printf "%s ", $i; print "..."}'
|
|
291
|
+
done
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
# ─── Progress Bar ──────────────────────────────────────────────────────────
|
|
295
|
+
show_progress() {
|
|
296
|
+
local current="$1"
|
|
297
|
+
local total="$2"
|
|
298
|
+
local label="${3:-Progress}"
|
|
299
|
+
|
|
300
|
+
local percent=$((current * 100 / total))
|
|
301
|
+
local filled=$((percent / 5))
|
|
302
|
+
local empty=$((20 - filled))
|
|
303
|
+
|
|
304
|
+
printf "\r${CYAN}${label}${RESET} ["
|
|
305
|
+
printf "%${filled}s" | tr ' ' '='
|
|
306
|
+
printf "%${empty}s" | tr ' ' '-'
|
|
307
|
+
printf "] %3d%% " "$percent"
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
# ─── Box Drawing Helpers ───────────────────────────────────────────────────
|
|
311
|
+
box_title() {
|
|
312
|
+
local title="$1"
|
|
313
|
+
local width="${2:-70}"
|
|
314
|
+
|
|
315
|
+
echo "╔$(printf '═%.0s' $(seq 1 $((width-2))))╗"
|
|
316
|
+
printf "║ %-$((width-3))s║\n" "$title"
|
|
317
|
+
echo "╚$(printf '═%.0s' $(seq 1 $((width-2))))╝"
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
box_section() {
|
|
321
|
+
local title="$1"
|
|
322
|
+
local width="${2:-70}"
|
|
323
|
+
|
|
324
|
+
echo "┌─ ${CYAN}${title}${RESET}"
|
|
325
|
+
echo "│"
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
box_end() {
|
|
329
|
+
echo "└─"
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
# ─── Table Formatting ──────────────────────────────────────────────────────
|
|
333
|
+
table_header() {
|
|
334
|
+
local cols=("$@")
|
|
335
|
+
local widths=()
|
|
336
|
+
|
|
337
|
+
# Calculate column widths
|
|
338
|
+
for col in "${cols[@]}"; do
|
|
339
|
+
widths+=($((${#col} + 2)))
|
|
340
|
+
done
|
|
341
|
+
|
|
342
|
+
# Print header
|
|
343
|
+
for i in "${!cols[@]}"; do
|
|
344
|
+
printf "%-${widths[$i]}s" "${cols[$i]}"
|
|
345
|
+
done
|
|
346
|
+
echo ""
|
|
347
|
+
|
|
348
|
+
# Print separator
|
|
349
|
+
for w in "${widths[@]}"; do
|
|
350
|
+
printf '%*s' "$w" | tr ' ' '─'
|
|
351
|
+
done
|
|
352
|
+
echo ""
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
table_row() {
|
|
356
|
+
local cols=("$@")
|
|
357
|
+
local widths=()
|
|
358
|
+
|
|
359
|
+
# This is a simple implementation; in production you'd track column widths
|
|
360
|
+
for col in "${cols[@]}"; do
|
|
361
|
+
printf "%-20s" "$col"
|
|
362
|
+
done
|
|
363
|
+
echo ""
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
# ─── Keyboard Shortcuts System ─────────────────────────────────────────────
|
|
367
|
+
|
|
368
|
+
show_shortcuts() {
|
|
369
|
+
box_title "Keyboard Shortcuts"
|
|
370
|
+
echo ""
|
|
371
|
+
|
|
372
|
+
# Define shortcuts as separate lines (bash 3.2 compatible)
|
|
373
|
+
printf " ${CYAN}%-20s${RESET} %s\n" "q" "Exit the current menu"
|
|
374
|
+
printf " ${CYAN}%-20s${RESET} %s\n" "h" "Show help overlay"
|
|
375
|
+
printf " ${CYAN}%-20s${RESET} %s\n" "t" "Cycle through themes"
|
|
376
|
+
printf " ${CYAN}%-20s${RESET} %s\n" "s" "Toggle notification sound"
|
|
377
|
+
printf " ${CYAN}%-20s${RESET} %s\n" "m" "Toggle animation/reduced motion"
|
|
378
|
+
printf " ${CYAN}%-20s${RESET} %s\n" "c" "Toggle high-contrast mode"
|
|
379
|
+
printf " ${CYAN}%-20s${RESET} %s\n" "1-5" "Jump to pipeline stage 1-5"
|
|
380
|
+
printf " ${CYAN}%-20s${RESET} %s\n" "space" "Pause or resume pipeline"
|
|
381
|
+
printf " ${CYAN}%-20s${RESET} %s\n" "r" "Reload configuration"
|
|
382
|
+
|
|
383
|
+
box_end
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
# ─── Accessibility Support ────────────────────────────────────────────────
|
|
387
|
+
set_high_contrast() {
|
|
388
|
+
init_ux_config "$UX_CONFIG_GLOBAL"
|
|
389
|
+
|
|
390
|
+
if jq '.high_contrast = true' "$UX_CONFIG_GLOBAL" > "${UX_CONFIG_GLOBAL}.tmp" 2>/dev/null; then
|
|
391
|
+
mv "${UX_CONFIG_GLOBAL}.tmp" "$UX_CONFIG_GLOBAL"
|
|
392
|
+
success "High contrast mode enabled"
|
|
393
|
+
emit_event "ux_accessibility" "feature=high_contrast"
|
|
394
|
+
fi
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
set_reduced_motion() {
|
|
398
|
+
init_ux_config "$UX_CONFIG_GLOBAL"
|
|
399
|
+
export PREFERS_REDUCED_MOTION=1
|
|
400
|
+
|
|
401
|
+
if jq '.reduced_motion = true' "$UX_CONFIG_GLOBAL" > "${UX_CONFIG_GLOBAL}.tmp" 2>/dev/null; then
|
|
402
|
+
mv "${UX_CONFIG_GLOBAL}.tmp" "$UX_CONFIG_GLOBAL"
|
|
403
|
+
success "Reduced motion mode enabled"
|
|
404
|
+
emit_event "ux_accessibility" "feature=reduced_motion"
|
|
405
|
+
fi
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
enable_screen_reader_mode() {
|
|
409
|
+
# Disable colors and animations for screen reader compatibility
|
|
410
|
+
export NO_COLOR=1
|
|
411
|
+
export PREFERS_REDUCED_MOTION=1
|
|
412
|
+
success "Screen reader mode enabled (colors and animations disabled)"
|
|
413
|
+
emit_event "ux_accessibility" "feature=screen_reader"
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
# ─── Formatting Helpers ───────────────────────────────────────────────────
|
|
417
|
+
format_diff_line() {
|
|
418
|
+
local line="$1"
|
|
419
|
+
|
|
420
|
+
case "$line" in
|
|
421
|
+
+*) echo -e "${GREEN}${line}${RESET}" ;;
|
|
422
|
+
-*) echo -e "${RED}${line}${RESET}" ;;
|
|
423
|
+
@@*) echo -e "${BLUE}${line}${RESET}" ;;
|
|
424
|
+
*) echo "$line" ;;
|
|
425
|
+
esac
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
format_tree() {
|
|
429
|
+
local depth="$1"
|
|
430
|
+
local name="$2"
|
|
431
|
+
local is_last="${3:-true}"
|
|
432
|
+
|
|
433
|
+
local indent=""
|
|
434
|
+
for _ in $(seq 1 "$depth"); do
|
|
435
|
+
indent+=" "
|
|
436
|
+
done
|
|
437
|
+
|
|
438
|
+
local prefix="├─"
|
|
439
|
+
[[ "$is_last" == "true" ]] && prefix="└─"
|
|
440
|
+
|
|
441
|
+
echo "${indent}${prefix} ${name}"
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
# ─── Notification Sounds ───────────────────────────────────────────────────
|
|
445
|
+
notify_complete() {
|
|
446
|
+
local message="${1:-Complete}"
|
|
447
|
+
|
|
448
|
+
init_ux_config "$UX_CONFIG_GLOBAL"
|
|
449
|
+
local sound_enabled
|
|
450
|
+
sound_enabled=$(jq -r '.sound_enabled // false' "$UX_CONFIG_GLOBAL" 2>/dev/null || echo "false")
|
|
451
|
+
|
|
452
|
+
if [[ "$sound_enabled" == "true" ]]; then
|
|
453
|
+
# Use system beep (works on macOS and Linux)
|
|
454
|
+
printf '\a'
|
|
455
|
+
fi
|
|
456
|
+
|
|
457
|
+
success "$message"
|
|
458
|
+
emit_event "ux_notification" "type=complete" "message=$message"
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
notify_error() {
|
|
462
|
+
local message="${1:-Error occurred}"
|
|
463
|
+
|
|
464
|
+
init_ux_config "$UX_CONFIG_GLOBAL"
|
|
465
|
+
local sound_enabled
|
|
466
|
+
sound_enabled=$(jq -r '.sound_enabled // false' "$UX_CONFIG_GLOBAL" 2>/dev/null || echo "false")
|
|
467
|
+
|
|
468
|
+
if [[ "$sound_enabled" == "true" ]]; then
|
|
469
|
+
printf '\a\a' # Double beep for errors
|
|
470
|
+
fi
|
|
471
|
+
|
|
472
|
+
error "$message"
|
|
473
|
+
emit_event "ux_notification" "type=error" "message=$message"
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
# ─── Configuration Management ──────────────────────────────────────────────
|
|
477
|
+
show_config() {
|
|
478
|
+
init_ux_config "$UX_CONFIG_GLOBAL"
|
|
479
|
+
|
|
480
|
+
info "UX Configuration:"
|
|
481
|
+
if [[ -f "$UX_CONFIG_GLOBAL" ]]; then
|
|
482
|
+
jq '.' "$UX_CONFIG_GLOBAL" 2>/dev/null || cat "$UX_CONFIG_GLOBAL"
|
|
483
|
+
else
|
|
484
|
+
echo "No configuration found"
|
|
485
|
+
fi
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
reset_config() {
|
|
489
|
+
rm -f "$UX_CONFIG_GLOBAL"
|
|
490
|
+
init_ux_config "$UX_CONFIG_GLOBAL"
|
|
491
|
+
success "UX configuration reset to defaults"
|
|
492
|
+
emit_event "ux_reset" "scope=global"
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
# ─── Demo Mode ──────────────────────────────────────────────────────────────
|
|
496
|
+
run_demo() {
|
|
497
|
+
clear
|
|
498
|
+
|
|
499
|
+
box_title "Shipwright UX Enhancement System Demo"
|
|
500
|
+
echo ""
|
|
501
|
+
|
|
502
|
+
# Demo themes
|
|
503
|
+
info "Theme Previews:"
|
|
504
|
+
for theme in dark light cyberpunk ocean; do
|
|
505
|
+
preview_theme "$theme"
|
|
506
|
+
sleep 0.5
|
|
507
|
+
done
|
|
508
|
+
|
|
509
|
+
# Demo spinners
|
|
510
|
+
info "Spinner Styles:"
|
|
511
|
+
for style in dots braille moon arrows; do
|
|
512
|
+
printf " ${CYAN}%-12s${RESET} " "$style"
|
|
513
|
+
animate_spinner "Loading" 2 "$style"
|
|
514
|
+
echo " ${GREEN}✓${RESET}"
|
|
515
|
+
done
|
|
516
|
+
|
|
517
|
+
echo ""
|
|
518
|
+
info "Progress Bar Demo:"
|
|
519
|
+
for i in {0..100..10}; do
|
|
520
|
+
show_progress "$i" 100 "Building"
|
|
521
|
+
sleep 0.3
|
|
522
|
+
done
|
|
523
|
+
printf "\n"
|
|
524
|
+
|
|
525
|
+
# Demo box drawing
|
|
526
|
+
echo ""
|
|
527
|
+
info "Box Drawing Helpers:"
|
|
528
|
+
box_title "Example Section"
|
|
529
|
+
box_section "Content Area"
|
|
530
|
+
echo " This is sample content with structured formatting"
|
|
531
|
+
box_end
|
|
532
|
+
|
|
533
|
+
echo ""
|
|
534
|
+
show_shortcuts
|
|
535
|
+
|
|
536
|
+
echo ""
|
|
537
|
+
success "Demo complete!"
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
# ─── Help Text ──────────────────────────────────────────────────────────────
|
|
541
|
+
show_help() {
|
|
542
|
+
cat <<'EOF'
|
|
543
|
+
shipwright ux — Premium UX Enhancement Layer
|
|
544
|
+
|
|
545
|
+
USAGE
|
|
546
|
+
shipwright ux <subcommand> [options]
|
|
547
|
+
|
|
548
|
+
SUBCOMMANDS
|
|
549
|
+
theme <name> Set theme (dark, light, minimal, cyberpunk, ocean)
|
|
550
|
+
theme list Show available themes
|
|
551
|
+
theme preview [name] Preview a theme
|
|
552
|
+
|
|
553
|
+
spinner [style] Show spinner style (dots, braille, moon, arrows, bounce)
|
|
554
|
+
spinner list Show all spinner styles
|
|
555
|
+
|
|
556
|
+
config show Display current UX configuration
|
|
557
|
+
config reset Reset to default configuration
|
|
558
|
+
|
|
559
|
+
shortcuts Show keyboard shortcut reference
|
|
560
|
+
|
|
561
|
+
accessibility Configure accessibility options
|
|
562
|
+
--high-contrast Enable high contrast mode
|
|
563
|
+
--reduced-motion Enable reduced motion (no animations)
|
|
564
|
+
--screen-reader Full screen reader mode
|
|
565
|
+
|
|
566
|
+
demo Run interactive UX feature demo
|
|
567
|
+
help Show this help message
|
|
568
|
+
|
|
569
|
+
OPTIONS
|
|
570
|
+
--no-color Disable all color output
|
|
571
|
+
--prefers-reduced-motion Disable animations
|
|
572
|
+
|
|
573
|
+
EXAMPLES
|
|
574
|
+
shipwright ux theme dark
|
|
575
|
+
shipwright ux theme preview cyberpunk
|
|
576
|
+
shipwright ux spinner list
|
|
577
|
+
shipwright ux accessibility --high-contrast
|
|
578
|
+
shipwright ux demo
|
|
579
|
+
|
|
580
|
+
CONFIGURATION FILES
|
|
581
|
+
Global: ~/.shipwright/ux-config.json
|
|
582
|
+
Project: ./.claude/ux-config.json (overrides global)
|
|
583
|
+
|
|
584
|
+
EOF
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
# ─── Main Command Router ──────────────────────────────────────────────────
|
|
588
|
+
main() {
|
|
589
|
+
local cmd="${1:-help}"
|
|
590
|
+
shift 2>/dev/null || true
|
|
591
|
+
|
|
592
|
+
case "$cmd" in
|
|
593
|
+
theme)
|
|
594
|
+
local theme_cmd="${1:-list}"
|
|
595
|
+
shift 2>/dev/null || true
|
|
596
|
+
case "$theme_cmd" in
|
|
597
|
+
list)
|
|
598
|
+
list_themes
|
|
599
|
+
;;
|
|
600
|
+
preview)
|
|
601
|
+
preview_theme "${1:-dark}"
|
|
602
|
+
;;
|
|
603
|
+
*)
|
|
604
|
+
set_theme "$theme_cmd"
|
|
605
|
+
;;
|
|
606
|
+
esac
|
|
607
|
+
;;
|
|
608
|
+
spinner)
|
|
609
|
+
local spinner_cmd="${1:-dots}"
|
|
610
|
+
case "$spinner_cmd" in
|
|
611
|
+
list)
|
|
612
|
+
list_spinners
|
|
613
|
+
;;
|
|
614
|
+
*)
|
|
615
|
+
animate_spinner "Demo animation" 3 "$spinner_cmd"
|
|
616
|
+
echo ""
|
|
617
|
+
success "Spinner demo complete"
|
|
618
|
+
;;
|
|
619
|
+
esac
|
|
620
|
+
;;
|
|
621
|
+
config)
|
|
622
|
+
local config_cmd="${1:-show}"
|
|
623
|
+
case "$config_cmd" in
|
|
624
|
+
show)
|
|
625
|
+
show_config
|
|
626
|
+
;;
|
|
627
|
+
reset)
|
|
628
|
+
reset_config
|
|
629
|
+
;;
|
|
630
|
+
*)
|
|
631
|
+
error "Unknown config command: $config_cmd"
|
|
632
|
+
exit 1
|
|
633
|
+
;;
|
|
634
|
+
esac
|
|
635
|
+
;;
|
|
636
|
+
shortcuts)
|
|
637
|
+
show_shortcuts
|
|
638
|
+
;;
|
|
639
|
+
accessibility)
|
|
640
|
+
while [[ $# -gt 0 ]]; do
|
|
641
|
+
case "$1" in
|
|
642
|
+
--high-contrast)
|
|
643
|
+
set_high_contrast
|
|
644
|
+
;;
|
|
645
|
+
--reduced-motion)
|
|
646
|
+
set_reduced_motion
|
|
647
|
+
;;
|
|
648
|
+
--screen-reader)
|
|
649
|
+
enable_screen_reader_mode
|
|
650
|
+
;;
|
|
651
|
+
*)
|
|
652
|
+
error "Unknown accessibility option: $1"
|
|
653
|
+
exit 1
|
|
654
|
+
;;
|
|
655
|
+
esac
|
|
656
|
+
shift
|
|
657
|
+
done
|
|
658
|
+
;;
|
|
659
|
+
demo)
|
|
660
|
+
run_demo
|
|
661
|
+
;;
|
|
662
|
+
help|--help|-h)
|
|
663
|
+
show_help
|
|
664
|
+
;;
|
|
665
|
+
*)
|
|
666
|
+
error "Unknown command: $cmd"
|
|
667
|
+
echo ""
|
|
668
|
+
show_help
|
|
669
|
+
exit 1
|
|
670
|
+
;;
|
|
671
|
+
esac
|
|
672
|
+
}
|
|
673
|
+
|
|
674
|
+
# Source guard: allow direct execution and sourcing
|
|
675
|
+
if [[ "${BASH_SOURCE[0]}" == "$0" ]]; then
|
|
676
|
+
main "$@"
|
|
677
|
+
fi
|