diff --git a/plugins/glaze-coder/scripts/glaze-dev b/plugins/glaze-coder/scripts/glaze-dev index fa74721..1f84f94 100755 --- a/plugins/glaze-coder/scripts/glaze-dev +++ b/plugins/glaze-coder/scripts/glaze-dev @@ -1,23 +1,23 @@ #!/bin/zsh # ───────────────────────────────────────────────────────────────────────────── -# glaze-dev, Utvikle Glaze (Raycast) desktop-apper GRATIS med din egen Claude Code. +# glaze-dev, develop Glaze (Raycast) desktop apps for FREE with your own Claude Code. # -# Glaze-apper er vanlige Claude Code-prosjekter på disk. Det eneste som koster -# Glaze-kreditter er Glaze sin INNEBYGDE AI-agent. Denne wrapperen lar deg: -# • gå fra ingenting til å kode en ny app med ÉN kommando (`start`) -# • lage en HELT NY app uten en eneste kreditt (`new`) -# • kjøre din EGEN `claude` (ditt Claude-abonnement) i app-mappen → 0 kreditter -# • bygge/kjøre appen lokalt med Glaze sin medfølgende Node → 0 kreditter +# Glaze apps are ordinary Claude Code projects on disk. The only thing that costs +# Glaze credits is Glaze's BUILT-IN AI agent. This wrapper lets you: +# • go from nothing to coding a new app with ONE command (`start`) +# • create a BRAND NEW app without spending a single credit (`new`) +# • run your OWN `claude` (your Claude subscription) in the app folder → 0 credits +# • build/run the app locally with Glaze's bundled Node → 0 credits # -# Glaze sier selv at du eier appen, koden og innholdet. Dette redigerer bare -# den koden med dine egne verktøy i stedet for Glaze sin betalte agent. +# Glaze itself says you own the app, the code and the content. This just edits +# that code with your own tools instead of Glaze's paid agent. # ───────────────────────────────────────────────────────────────────────────── emulate -L zsh set -e setopt null_glob GLAZE_DEV_SELF="${0:A}" # absolute path to this script (captured before function scope, where $0 becomes the function name) -# --- Finn Glaze sin data-mappe (håndterer varianter av profil-navn) ----------- +# --- Find Glaze's data folder (handles profile-name variants) ----------------- GLAZE_BASE="${GLAZE_BASE:-}" if [[ -z "$GLAZE_BASE" ]]; then for cand in \ @@ -27,12 +27,12 @@ if [[ -z "$GLAZE_BASE" ]]; then done fi if [[ -z "$GLAZE_BASE" || ! -d "$GLAZE_BASE/apps" ]]; then - # setup/help skal kunne veilede selv uten Glaze installert. + # setup/help should still be able to guide you even without Glaze installed. case "${1:-}" in setup|doctor|help|-h|--help|"") GLAZE_BASE="" ;; *) - print -u2 "❌ Fant ikke Glaze. Sett GLAZE_BASE manuelt, eller start Glaze én gang." - print -u2 " Ny her? Kjør: glaze-dev setup" + print -u2 "❌ Could not find Glaze. Set GLAZE_BASE manually, or open Glaze once." + print -u2 " New here? Run: glaze-dev setup" exit 1 ;; esac fi @@ -42,15 +42,29 @@ AR_SKILLS="$GLAZE_BASE/agent-resources/current/.claude/skills" AR_GUIDE="$GLAZE_BASE/agent-resources/current/GLAZE-APP-GUIDE.md" NPMRC="$GLAZE_BASE/.npmrc" -# --- Glaze sin medfølgende Node (kreves: node >= 24) -------------------------- +# --- Find Raycast (v2 ships as "Raycast Beta.app", side by side with v1) ------ +# Prints the app bundle path of the preferred install, or nothing if absent. +_raycast_app() { + local d + for d in \ + "/Applications/Raycast Beta.app" \ + "$HOME/Applications/Raycast Beta.app" \ + "/Applications/Raycast.app" \ + "$HOME/Applications/Raycast.app" ; do + [[ -d "$d" ]] && { print -r -- "$d"; return 0; } + done + return 1 +} + +# --- Glaze's bundled Node (required: node >= 24) ------------------------------ node_bins=("$GLAZE_BASE"/node/runtime/*/bin(N)) if (( ${#node_bins} )); then export PATH="${node_bins[1]}:$PATH" fi -# La npm bruke samme policy som Glaze (uten å endre .npmrc) +# Let npm use the same policy as Glaze (without touching .npmrc) [[ -f "$NPMRC" ]] && export NPM_CONFIG_USERCONFIG="$NPMRC" -# --- Hjelpere ----------------------------------------------------------------- +# --- Helpers ------------------------------------------------------------------ _appmeta() { # -> "productName|name|id" local pj="$1/.glaze-sources/package.json" [[ -f "$pj" ]] || return 1 @@ -74,8 +88,8 @@ _resolve() { # -> echoes app source dir (.glaze-sources), or fails _slug() { print -r -- "${1:l}" | tr -cs 'a-z0-9' '-' | sed 's/^-*//;s/-*$//'; } _genid() { LC_ALL=C tr -dc 'a-z0-9' -> sets $SRC and $ROOT, or exits local q="$1" - [[ -n "$q" ]] || { print -u2 "❌ Mangler app-navn. Prøv: glaze-dev list"; exit 1; } - SRC="$(_resolve "$q")" || { print -u2 "❌ Fant ingen app som matcher '$q'. Prøv: glaze-dev list"; exit 1; } + [[ -n "$q" ]] || { print -u2 "❌ Missing app name. Try: glaze-dev list"; exit 1; } + SRC="$(_resolve "$q")" || { print -u2 "❌ Found no app matching '$q'. Try: glaze-dev list"; exit 1; } ROOT="${SRC:h}" } -# Synk appens EGET ikon inn i launcher-bundlen og .glaze/ etter bygg, slik at -# apper klonet fra en mal ikke blir stående med malens ikon. Bevarer -# entitlements ved re-signering. Idempotent (gjør ingenting når ikonet stemmer). +# Sync the app's OWN icon into the launcher bundle and .glaze/ after a build, so +# apps cloned from a template don't keep the template's icon. Preserves +# entitlements when re-signing. Idempotent (does nothing when the icon matches). _sync_icon() { # local src="$1" root="${1:h}" local icns="$src/app-icon.icns" [[ -f "$icns" ]] || return 0 local meta="$(_appmeta "$root")"; local prod="${meta%%|*}" local bundle="/Applications/Glaze/${prod}.app" - # sikkerhetssjekk: bundle må peke tilbake på DENNE appen + # safety check: the bundle must point back at THIS app [[ -d "$bundle" ]] || return 0 local runtime="$(readlink "$bundle/Contents/Resources/glaze-runtime" 2>/dev/null)" [[ "$runtime" == "$root/.glaze" ]] || return 0 - # .glaze/app-icon.icns (Glaze Store-review forventer denne) + # .glaze/app-icon.icns (Glaze Store review expects this one) if [[ -d "$root/.glaze" ]] && ! cmp -s "$icns" "$root/.glaze/app-icon.icns"; then cp "$icns" "$root/.glaze/app-icon.icns" fi - # launcher-bundlens ikon + # the launcher bundle's icon if ! cmp -s "$icns" "$bundle/Contents/Resources/app-icon.icns"; then cp "$icns" "$bundle/Contents/Resources/app-icon.icns" local ent="$(mktemp)" @@ -122,33 +136,33 @@ _sync_icon() { # fi rm -f "$ent" touch "$bundle" - print -r -- "✓ Ikon synket inn i ${bundle:t}" + print -r -- "✓ Icon synced into ${bundle:t}" fi } -# --- Kommandoer --------------------------------------------------------------- +# --- Commands ----------------------------------------------------------------- cmd_list() { - print -r -- "Glaze-apper (i $APPS_DIR):" + print -r -- "Glaze apps (in $APPS_DIR):" local d meta for d in "$APPS_DIR"/*(/N); do meta="$(_appmeta "$d")" || continue - printf " • %-24s [mappe: %s]\n" "${meta%%|*}" "${d:t}" + printf " • %-24s [folder: %s]\n" "${meta%%|*}" "${d:t}" done } -# --- Agenter (Claude Code, Codex, OpenCode, Gemini, desktop-IDE-er) ----------- -# Er en app installert? (etter bundle-id) +# --- Agents (Claude Code, Codex, OpenCode, Gemini, desktop IDEs) -------------- +# Is an app installed? (by bundle id) _app_present() { mdfind "kMDItemCFBundleIdentifier == '$1'" 2>/dev/null | grep -q . ; } -# Bygg en AGENTS.md-bro for agenter som leser den (Codex/OpenCode/Gemini), slik -# at de får samme Glaze-kontekst som CLAUDE.md gir Claude Code. Idempotent. +# Build an AGENTS.md bridge for agents that read it (Codex/OpenCode/Gemini), so +# they get the same Glaze context CLAUDE.md gives Claude Code. Idempotent. _ensure_agents_md() { local src="$1" [[ -e "$src/AGENTS.md" ]] && return 0 [[ -f "$src/CLAUDE.md" ]] && ln -s "CLAUDE.md" "$src/AGENTS.md" 2>/dev/null || true } -# Start valgt agent i app-mappen. $1=agent $2=kildemappe resten=prompt +# Start the chosen agent in the app folder. $1=agent $2=source dir rest=prompt _launch_agent() { local agent="$1" src="$2"; shift 2 local prompt="$*" @@ -156,24 +170,24 @@ _launch_agent() { case "$agent" in claude) _link_skills - print -r -- "▶ Claude Code i: $src (ingen Glaze-kreditter)" + print -r -- "▶ Claude Code in: $src (no Glaze credits)" [[ -n "$prompt" ]] && exec claude "$prompt" || exec claude ;; codex) - _ensure_agents_md "$src"; print -r -- "▶ Codex i: $src" + _ensure_agents_md "$src"; print -r -- "▶ Codex in: $src" [[ -n "$prompt" ]] && exec codex "$prompt" || exec codex ;; opencode) - _ensure_agents_md "$src"; print -r -- "▶ OpenCode i: $src"; exec opencode ;; + _ensure_agents_md "$src"; print -r -- "▶ OpenCode in: $src"; exec opencode ;; gemini) - _ensure_agents_md "$src"; print -r -- "▶ Gemini i: $src"; exec gemini ;; - codex-app) open -b com.openai.codex "$src" && print -r -- "▶ Åpnet Codex-appen på mappen" || print -u2 "❌ Fikk ikke åpnet Codex-appen" ;; - opencode-app) open -b ai.opencode.desktop "$src" && print -r -- "▶ Åpnet OpenCode-appen" || print -u2 "❌ Fikk ikke åpnet OpenCode-appen" ;; - antigravity) open -b com.google.antigravity-ide "$src" && print -r -- "▶ Åpnet Antigravity IDE på mappen" || print -u2 "❌ Fikk ikke åpnet Antigravity" ;; + _ensure_agents_md "$src"; print -r -- "▶ Gemini in: $src"; exec gemini ;; + codex-app) open -b com.openai.codex "$src" && print -r -- "▶ Opened the Codex app on the folder" || print -u2 "❌ Could not open the Codex app" ;; + opencode-app) open -b ai.opencode.desktop "$src" && print -r -- "▶ Opened the OpenCode app" || print -u2 "❌ Could not open the OpenCode app" ;; + antigravity) open -b com.google.antigravity-ide "$src" && print -r -- "▶ Opened Antigravity IDE on the folder" || print -u2 "❌ Could not open Antigravity" ;; zcode) _open_zcode "$src" "${${(s:|:)$(_appmeta "$ROOT")}[1]:-${ROOT:t}}" "$prompt" ;; - *) print -u2 "❌ Ukjent agent: $agent (se: glaze-dev agents)"; exit 1 ;; + *) print -u2 "❌ Unknown agent: $agent (see: glaze-dev agents)"; exit 1 ;; esac } -cmd_code() { # åpne valgt agent i app-mappen (gratis iterasjon) +cmd_code() { # open the chosen agent in the app folder (free iteration) local agent="${GLAZE_AGENT:-claude}" local -a pos while [[ $# -gt 0 ]]; do @@ -189,23 +203,23 @@ cmd_code() { # åpne valgt agent i app-mappen (gratis iterasjon) } cmd_agents() { - print -r -- "Agenter for: glaze-dev code --agent (standard: claude)" + print -r -- "Agents for: glaze-dev code --agent (default: claude)" print -r -- "" local a for a in claude codex opencode gemini; do - command -v "$a" >/dev/null 2>&1 && printf " ✓ %-13s CLI\n" "$a" || printf " - %-13s (ikke funnet)\n" "$a" + command -v "$a" >/dev/null 2>&1 && printf " ✓ %-13s CLI\n" "$a" || printf " - %-13s (not found)\n" "$a" done - _app_present com.openai.codex && printf " ✓ %-13s desktop-app\n" "codex-app" - _app_present ai.opencode.desktop && printf " ✓ %-13s desktop-app\n" "opencode-app" - _app_present com.google.antigravity-ide && printf " ✓ %-13s desktop-IDE (åpner mappen)\n" "antigravity" - _app_present dev.zcode.app && printf " ✓ %-13s desktop-IDE (åpner mappen)\n" "zcode" + _app_present com.openai.codex && printf " ✓ %-13s desktop app\n" "codex-app" + _app_present ai.opencode.desktop && printf " ✓ %-13s desktop app\n" "opencode-app" + _app_present com.google.antigravity-ide && printf " ✓ %-13s desktop IDE (opens the folder)\n" "antigravity" + _app_present dev.zcode.app && printf " ✓ %-13s desktop IDE (opens the folder)\n" "zcode" print -r -- "" - print -r -- "Sett standard agent: export GLAZE_AGENT=codex (legg i ~/.zshrc)" - print -r -- "Codex/OpenCode/Gemini får Glaze-kontekst via en AGENTS.md-bro i app-mappen." + print -r -- "Set the default agent: export GLAZE_AGENT=codex (put it in ~/.zshrc)" + print -r -- "Codex/OpenCode/Gemini get Glaze context via an AGENTS.md bridge in the app folder." } -cmd_build() { _need "$1"; cd "$SRC"; print "▶ Bygger $1..."; npm run build; _sync_icon "$SRC"; } -cmd_dev() { _need "$1"; cd "$SRC"; print "▶ Dev-server for $1 (Ctrl-C for å stoppe)..."; npm run dev; } +cmd_build() { _need "$1"; cd "$SRC"; print "▶ Building $1..."; npm run build; _sync_icon "$SRC"; } +cmd_dev() { _need "$1"; cd "$SRC"; print "▶ Dev server for $1 (Ctrl-C to stop)..."; npm run dev; } cmd_check() { _need "$1"; cd "$SRC"; npm run type-check; } cmd_lint() { _need "$1"; cd "$SRC"; npm run lint; } cmd_run() { _need "$1"; local app=("$ROOT"/*.app(N)); (( ${#app} )) && open "${app[1]}" || open "$SRC/../"*.app; } @@ -358,8 +372,9 @@ cmd_setup() { fi # 7) Raycast (optional) - if [[ -d "/Applications/Raycast.app" || -d "$HOME/Applications/Raycast.app" ]]; then - print -P "$pass Raycast found. Set up the commands with: glaze-dev raycast" + local rc_app; rc_app="$(_raycast_app || true)" + if [[ -n "$rc_app" ]]; then + print -P "$pass ${rc_app:t:r} found. Set up the commands with: glaze-dev raycast" else print -P "$note Raycast not found (optional). Everything works fine without it." fi @@ -386,10 +401,11 @@ cmd_setup() { fi } -# Les ut koede (pausede) prompts fra Glaze-vibekoderens chatvindu for en app. -# Koen finnes bare i Glaze-UIets minne (ikke i db/localStorage), så vi leser den -# fra accessibility-treet. CSS-avkutting kutter ikke DOM-teksten, så vi får -# fullteksten. Best effort: krever at Glaze er åpen med appens chatvindu. +# Read out the queued (paused) prompts from the Glaze vibe coder's chat window +# for an app. The queue only lives in the Glaze UI's memory (not in db/localStorage), +# so we read it from the accessibility tree. CSS truncation does not truncate the +# DOM text, so we get the full text. Best effort: requires Glaze to be open with +# the app's chat window. cmd_queue() { _need "$1" local meta="$(_appmeta "$ROOT")"; local prod="${meta%%|*}" @@ -412,7 +428,7 @@ function run(argv) { let role = '', title = ''; try { role = k.role(); } catch (e) {} try { title = String(k.title() || ''); } catch (e) {} - // Køens markører er knapper ("2 Queued Paused" / "Click to resume queue"). + // The queue's markers are buttons ("2 Queued Paused" / "Click to resume queue"). if (/resume queue|\d+ Queued/i.test(title)) { try { const p = k.position(); if (p && (markerY === null || p[1] < markerY)) markerY = p[1]; } catch (e) {} } @@ -437,29 +453,31 @@ function run(argv) { JXA } -# Handoff-prompten en agent får når vi tar over en app der Glaze-vibekoderen slapp. -# Agent-uavhengig (Claude Code, ZCode, ...). Base-teksten er ett sted; her legger vi -# på en eventuell pauset Glaze-kø. Brukes både herfra og fra Raycast-scriptet. -_handoff_base() { # -> grunntekst uten kø - printf '%s' "Fortsett arbeidet på Glaze-appen '$1' der forrige økt slapp (f.eks. avbrutt av tomme Glaze-kreditter). Les .glaze_memory/PROJECT-CONTEXT.md og PROJECT-HISTORY.md, kjør npm run type-check, se på nylig endrede filer, og fullfør det som var underveis." +# The handoff prompt an agent gets when we take over an app where the Glaze vibe +# coder left off. Agent-independent (Claude Code, ZCode, ...). The base text lives +# in one place; here we add any paused Glaze queue on top. Used both from here and +# from the Raycast script. +_handoff_base() { # -> base text without the queue + printf '%s' "Continue the work on the Glaze app '$1' where the previous session left off (e.g. interrupted by running out of Glaze credits). Read .glaze_memory/PROJECT-CONTEXT.md and PROJECT-HISTORY.md, run npm run type-check, look at recently changed files, and finish whatever was in progress." } -cmd_handoff() { # -> skriv ut full handoff-prompt (grunntekst + evt. kø) +cmd_handoff() { # -> print the full handoff prompt (base text + any queue) _need "$1" local meta="$(_appmeta "$ROOT")"; local prod="${meta%%|*}"; [[ -n "$prod" ]] || prod="$1" local h="$(_handoff_base "$prod")" local q; q="$(cmd_queue "$1" 2>/dev/null)" || q="" case "$q" in GLAZE_NOT_RUNNING|NO_WINDOW|NO_QUEUE|"") ;; - *) h="$h"$'\n\n'"Glaze-vibekoderen hadde disse oppgavene i kø da den stoppet - utfør dem i rekkefølge:"$'\n\n'"$q" ;; + *) h="$h"$'\n\n'"The Glaze vibe coder had these tasks queued up when it stopped - do them in order:"$'\n\n'"$q" ;; esac printf '%s' "$h" } -# Sømløs ZCode-åpning: åpne app-mappen som et ZCode-arbeidsområde via ZCodes egen -# deep link, start en fersk tråd (Cmd+N) og lim handoff-prompten inn i tekstboksen -# (Cmd+V). Vi trykker aldri Enter - brukeren ser over og sender selv. Auto-innliming -# krever Tilgjengelighet-tilgang; uten den faller vi pent tilbake til utklippstavlen. -# $1 = .glaze-sources-sti $2 = visningsnavn $3 = valgfri egendefinert prompt +# Seamless ZCode opening: open the app folder as a ZCode workspace via ZCode's own +# deep link, start a fresh thread (Cmd+N) and paste the handoff prompt into the text +# box (Cmd+V). We never press Enter - the user reviews it and sends it themselves. +# Auto-pasting requires Accessibility access; without it we fall back gracefully to +# the clipboard. +# $1 = .glaze-sources path $2 = display name $3 = optional custom prompt _open_zcode() { local src="$1" name="$2" custom="$3" local handoff @@ -480,11 +498,11 @@ _open_zcode() { while ! pgrep -x ZCode >/dev/null 2>&1 && (( tries < 40 )); do sleep 0.3; (( tries++ )) || true; done (( was_running )) && sleep 0.4 || sleep 2.5 - # Hvorfor dette er mer enn en timer: ZCodes deep link viser først et native - # bekreftelses-ark ("åpne dette arbeidsområdet?") på vinduet. Tekstboksen finnes - # IKKE før brukeren godkjenner. Så vi hever ZCode, VENTER til et ark eventuelt - # dukker opp, og VENTER så til brukeren har lukket det (uansett hvor lang tid det - # tar), og limer først da inn i en fersk tråd. Aldri Enter - brukeren sender selv. + # Why this is more than a timer: ZCode's deep link first shows a native + # confirmation sheet ("open this workspace?") on the window. The text box does + # NOT exist before the user approves. So we raise ZCode, WAIT for a sheet to + # possibly appear, then WAIT until the user has closed it (however long that + # takes), and only then paste into a fresh thread. Never Enter - the user sends. local result result="$(osascript <<'APPLESCRIPT' on hasSheet() @@ -516,8 +534,9 @@ on run end repeat if not frontOk then return "NOFOCUS" - -- Se etter et bekreftelses-ark i ~3 s; dukker det opp, vent (opptil ~2 min) til - -- brukeren har lukket det. Da, og bare da, er arbeidsområdet og tekstboksen klar. + -- Look for a confirmation sheet for ~3 s; if one appears, wait (up to ~2 min) + -- until the user has closed it. Then, and only then, the workspace and text box + -- are ready. set sawSheet to false repeat 20 times if hasSheet() then @@ -553,36 +572,38 @@ APPLESCRIPT )" || result="ERR" if [[ "$result" == PASTED ]]; then - print -r -- "▶ Åpnet ZCode på '$name' med ny tråd og handoff limt inn. Trykk Enter der for å starte." - osascript -e "display notification \"Åpnet $name i ZCode med ny tråd og handoff. Trykk Enter for å sende.\" with title \"Glaze: Edit $name\"" 2>/dev/null || true + print -r -- "▶ Opened ZCode on '$name' with a new thread and the handoff pasted in. Press Enter there to start." + osascript -e "display notification \"Opened $name in ZCode with a new thread and handoff. Press Enter to send.\" with title \"Glaze: Edit $name\"" 2>/dev/null || true else - print -r -- "▶ Åpnet ZCode på '$name'. Handoff ligger på utklippstavlen. Trykk Cmd+N så Cmd+V for å lime den inn." - print -r -- " (Auto-innliming krever Tilgjengelighet-tilgang for appen som kjører dette under Systeminnstillinger > Personvern og sikkerhet.)" - osascript -e "display notification \"Åpnet $name i ZCode. Trykk Cmd+N så Cmd+V for handoff (ligger på utklippstavlen).\" with title \"Glaze: Edit $name\"" 2>/dev/null || true + print -r -- "▶ Opened ZCode on '$name'. The handoff is on the clipboard. Press Cmd+N then Cmd+V to paste it in." + print -r -- " (Auto-pasting requires Accessibility access for the app running this, under System Settings > Privacy & Security.)" + osascript -e "display notification \"Opened $name in ZCode. Press Cmd+N then Cmd+V for the handoff (it is on the clipboard).\" with title \"Glaze: Edit $name\"" 2>/dev/null || true fi } -cmd_skills() { _link_skills; print "✓ Glaze-skills koblet inn i ~/.claude/skills"; } +cmd_skills() { _link_skills; print "✓ Glaze skills linked into ~/.claude/skills"; } -# Hjelp til Raycast-oppsettet (Raycast har ingen deeplink for å legge til en -# script-mappe, så dette gjør det så lite som mulig: kopierer stien, åpner Raycast). +# Help with the Raycast setup (Raycast has no deep link for adding a script +# directory, so this does the least possible: copies the path, opens Raycast). cmd_raycast() { local rc="${GLAZE_DEV_SELF:h}/raycast" [[ -d "$rc" ]] || { print -u2 "❌ Fant ikke Raycast-mappen: $rc"; exit 1; } + local rc_app; rc_app="$(_raycast_app || true)" + local rc_name="${rc_app:t:r}"; : "${rc_name:=Raycast}" command -v pbcopy >/dev/null 2>&1 && printf '%s' "$rc" | pbcopy - open -a Raycast 2>/dev/null || true - osascript -e 'display notification "Add Directories, så Cmd+Shift+G og lim inn" with title "glaze-dev: Raycast"' 2>/dev/null || true - print -r -- "Sti kopiert til utklippstavlen:" + [[ -n "$rc_app" ]] && { open -a "$rc_app" 2>/dev/null || true; } + osascript -e 'display notification "Add Directories, then Cmd+Shift+G and paste" with title "glaze-dev: Raycast"' 2>/dev/null || true + print -r -- "Path copied to the clipboard:" print -r -- " $rc" print -r -- "" - print -r -- "I Raycast: Settings > Extensions > Script Commands > Add Directories," - print -r -- "trykk Cmd+Shift+G, lim inn (Cmd+V), Enter, så Open." + print -r -- "In Raycast: Settings > Extensions > Script Commands > Add Directories," + print -r -- "press Cmd+Shift+G, paste (Cmd+V), Enter, then Open." } -# Generer et enkelt, unikt app-ikon: initialer på en fargegradient utledet -# deterministisk av produktnavnet. Kun macOS-verktøy (qlmanage/sips/iconutil), -# ingen AI, ingen kostnad. Skriver app-icon.png + app-icon.icns i kildemappen; -# _sync_icon tar dem videre inn i launcher-bundlen ved bygg. +# Generate a simple, unique app icon: initials on a color gradient derived +# deterministically from the product name. macOS tools only (qlmanage/sips/iconutil), +# no AI, no cost. Writes app-icon.png + app-icon.icns in the source folder; +# _sync_icon carries them on into the launcher bundle at build time. _gen_icon() { # local src="$1" prod="$2" local tmp; tmp="$(mktemp -d)" || return 1 @@ -611,19 +632,19 @@ EOF cp "$tmp/icon.svg.png" "$src/app-icon.png" cp "$tmp/app-icon.icns" "$src/app-icon.icns" rm -rf "$tmp" - print -r -- "✓ Genererte eget ikon ($init)" + print -r -- "✓ Generated a custom icon ($init)" } -# Regenerer ikonet for en eksisterende app og synk det inn i bundlen. +# Regenerate the icon for an existing app and sync it into the bundle. cmd_icon() { _need "$1" local meta="$(_appmeta "$ROOT")" - _gen_icon "$SRC" "${meta%%|*}" || { print -u2 "❌ Ikon-generering feilet"; exit 1; } + _gen_icon "$SRC" "${meta%%|*}" || { print -u2 "❌ Icon generation failed"; exit 1; } _sync_icon "$SRC" } -# ENKLESTE VEI: fra ingenting til å kode. Lager en blank app (0 kreditter) og -# hopper rett inn i Claude Code med et startbudskap. Én kommando, ferdig. +# SIMPLEST PATH: from nothing to coding. Creates a blank app (0 credits) and jumps +# straight into Claude Code with a starter message. One command, done. cmd_start() { local agent="${GLAZE_AGENT:-claude}" local -a words @@ -635,15 +656,15 @@ cmd_start() { esac done local prod="${words[*]}" - [[ -n "$prod" ]] || { print -u2 'Bruk: glaze-dev start "" [--agent ]'; exit 1; } + [[ -n "$prod" ]] || { print -u2 'Usage: glaze-dev start "" [--agent ]'; exit 1; } cmd_new "$prod" --blank --no-open local name="$(_slug "$prod")" - cmd_code "$name" --agent "$agent" "Dette er en ny, tom Glaze-app kalt '$prod'. Les den lokale Glaze-guiden (CLAUDE.md/AGENTS.md) og relevante skills, spør meg kort hva appen skal gjøre, og bygg den ut. Kun endringer i .glaze-sources, bygg med 'npm run build', og åpne appen for å vise resultatet." + cmd_code "$name" --agent "$agent" "This is a new, empty Glaze app called '$prod'. Read the local Glaze guide (CLAUDE.md/AGENTS.md) and the relevant skills, ask me briefly what the app should do, and build it out. Only change things in .glaze-sources, build with 'npm run build', and open the app to show the result." } -# Lag en HELT NY app uten en eneste Glaze-kreditt: -# klon en eksisterende app du eier (mal) → ny id → bygg lokalt → bygg launcher- -# bundle → ad-hoc-signer → start. Ingen agent-tur = ingen kreditter. +# Create a BRAND NEW app without a single Glaze credit: +# clone an existing app you own (template) → new id → build locally → build the +# launcher bundle → ad-hoc sign → launch. No agent turn = no credits. cmd_new() { local prod="" from="" blank=0 noopen=0 while [[ $# -gt 0 ]]; do @@ -654,21 +675,21 @@ cmd_new() { *) [[ -z "$prod" ]] && prod="$1" || prod="$prod $1"; shift ;; esac done - [[ -n "$prod" ]] || { print -u2 '❌ Bruk: glaze-dev new "" [--from ] [--blank]'; exit 1; } + [[ -n "$prod" ]] || { print -u2 '❌ Usage: glaze-dev new "" [--from ] [--blank]'; exit 1; } - # velg mal-app (en eksisterende app du eier) + # pick a template app (an existing app you own) local tsrc troot if [[ -n "$from" ]]; then - tsrc="$(_resolve "$from")" || { print -u2 "❌ Fant ikke mal-app '$from'"; exit 1; } + tsrc="$(_resolve "$from")" || { print -u2 "❌ Could not find template app '$from'"; exit 1; } else local first=("$APPS_DIR"/*(/N)) - (( ${#first} )) || { print -u2 "❌ Ingen eksisterende Glaze-app å bruke som mal. Lag én i Glaze først (gratisnivået holder)."; exit 1; } + (( ${#first} )) || { print -u2 "❌ No existing Glaze app to use as a template. Create one in Glaze first (the free tier is enough)."; exit 1; } tsrc="${first[1]}/.glaze-sources" fi troot="${tsrc:h}" local tmeta="$(_appmeta "$troot")"; local tprod="${tmeta%%|*}" local tbundle="/Applications/Glaze/${tprod}.app" - [[ -d "$tbundle" ]] || { print -u2 "❌ Fant ikke mal-bundle: $tbundle"; exit 1; } + [[ -d "$tbundle" ]] || { print -u2 "❌ Could not find template bundle: $tbundle"; exit 1; } local name="$(_slug "$prod")"; local id="$(_genid)" local folder="${name}-local-${id}" @@ -677,30 +698,30 @@ cmd_new() { local exe="${prod// /}" local newapp="/Applications/Glaze/${prod}.app" - print -r -- "▶ Ny app: $prod (id=$id, mappe=$folder, mal=$tprod)" + print -r -- "▶ New app: $prod (id=$id, folder=$folder, template=$tprod)" - # 1) klon kilde (APFS copy-on-write), fjern arv vi ikke vil ha + # 1) clone the source (APFS copy-on-write), drop inheritance we don't want rm -rf "$new" 2>/dev/null cp -cR "$troot" "$new" 2>/dev/null || cp -R "$troot" "$new" rm -f "$new"/*.app(N) 2>/dev/null rm -rf "$newsrc/.git" "$new/.glaze" 2>/dev/null rm -f "$newsrc/.glaze_memory/PROJECT-HISTORY.md"(N) 2>/dev/null - # ikon-identitet skal IKKE arves: fjern hash-markøren så Glaze-agenten vet at - # ikonet ikke er generert for denne appen. Selve malikonet blir stående som - # midlertidig plassholder til du bytter app-icon.png/app-icon.icns. + # icon identity must NOT be inherited: remove the hash marker so the Glaze agent + # knows the icon was not generated for this app. The template icon itself stays + # as a temporary placeholder until you swap app-icon.png/app-icon.icns. rm -f "$newsrc/.icon-hash"(N) 2>/dev/null - # personlige Claude Code-tillatelser (kan inneholde absolutte stier med - # brukernavn) og gamle worktrees skal heller ikke arves. + # personal Claude Code permissions (may contain absolute paths with the user + # name) and old worktrees must not be inherited either. rm -f "$newsrc/.claude/settings.local.json"(N) 2>/dev/null rm -rf "$newsrc/.claude/worktrees"(N) 2>/dev/null - # 2) ny identitet i package.json + # 2) new identity in package.json ( cd "$newsrc" && node -e 'const fs=require("fs");const j=JSON.parse(fs.readFileSync("package.json"));const[,id,name,prod]=process.argv;j.id=id;j.name=name;j.productName=prod;j.description="A Glaze app: "+prod;fs.writeFileSync("package.json",JSON.stringify(j,null,2))' "$id" "$name" "$prod" ) - # 2b) eget ikon fra dag én, så ingenting arver malens utseende - _gen_icon "$newsrc" "$prod" || print -r -- "! Ikon-generering feilet, malikonet beholdes inntil videre" + # 2b) a custom icon from day one, so nothing inherits the template's look + _gen_icon "$newsrc" "$prod" || print -r -- "! Icon generation failed, keeping the template icon for now" - # 3) valgfri blanking av UI + # 3) optionally blank out the UI if (( blank )); then local hv="$newsrc/renderer/main/home-view.tsx" if [[ -f "$hv" ]]; then @@ -716,11 +737,11 @@ EOF fi fi - # 4) bygg lokalt (gratis) - print -r -- "▶ Bygger lokalt..." - ( cd "$newsrc" && npm run build ) >/dev/null 2>&1 || { print -u2 "❌ Bygg feilet. Feilsøk: glaze-dev build $name"; exit 1; } + # 4) build locally (free) + print -r -- "▶ Building locally..." + ( cd "$newsrc" && npm run build ) >/dev/null 2>&1 || { print -u2 "❌ Build failed. Debug with: glaze-dev build $name"; exit 1; } - # 5) launcher-bundle (kopier mal, pek symlink, patch identitet) + # 5) launcher bundle (copy the template, point the symlink, patch identity) rm -rf "$newapp" 2>/dev/null cp -cR "$tbundle" "$newapp" 2>/dev/null || cp -R "$tbundle" "$newapp" mv "$newapp/Contents/MacOS/${tprod}" "$newapp/Contents/MacOS/${exe}" 2>/dev/null || true @@ -734,7 +755,7 @@ EOF plutil -replace CFBundleURLTypes.0.CFBundleURLName -string "$bid" "$plist" 2>/dev/null || true plutil -replace CFBundleURLTypes.0.CFBundleURLSchemes.0 -string "glaze-${id}-local" "$plist" 2>/dev/null || true - # 6) ad-hoc-signer (behold entitlements fra malen) + # 6) ad-hoc sign (keep the entitlements from the template) local ent="$(mktemp)" codesign -d --entitlements "$ent" --xml "$tbundle" 2>/dev/null || true codesign --remove-signature "$newapp" 2>/dev/null || true @@ -745,21 +766,21 @@ EOF fi rm -f "$ent" - # 7) synk det genererte ikonet inn i den ferske bundlen + # 7) sync the generated icon into the fresh bundle _sync_icon "$newsrc" || true - print -r -- "✓ Opprettet uten kreditter: $newapp" - print -r -- "Appen fikk et generert ikon. Bytt når som helst: legg nytt app-icon.png +" - print -r -- "app-icon.icns i .glaze-sources (eller kjør glaze-dev icon $name på nytt)." + print -r -- "✓ Created without credits: $newapp" + print -r -- "The app got a generated icon. Swap it any time: drop a new app-icon.png +" + print -r -- "app-icon.icns into .glaze-sources (or run glaze-dev icon $name again)." (( noopen )) || open "$newapp" - print -r -- "Neste: glaze-dev code $name \"bygg ut appen slik:...\" (gratis videreutvikling)" + print -r -- "Next: glaze-dev code $name \"build the app out like this:...\" (free further development)" } -# Fjern en app fullstendig (bundle, kilde, profil, logger). Motstykket til `new`. +# Remove an app completely (bundle, source, profile, logs). The counterpart to `new`. cmd_rm() { _need "$1" local meta="$(_appmeta "$ROOT")"; local prod="${meta%%|*}"; local id="${meta##*|}" - print -r -- "Fjerner '$prod' (mappe=${ROOT:t}, id=$id)..." + print -r -- "Removing '$prod' (folder=${ROOT:t}, id=$id)..." osascript -e "tell application \"$prod\" to quit" 2>/dev/null || true pkill -f "/Applications/Glaze/${prod}.app" 2>/dev/null || true sleep 1 @@ -767,49 +788,49 @@ cmd_rm() { rm -rf "$ROOT" rm -rf "$HOME/Library/Application Support/app.glaze.macos.${id}-local" 2>/dev/null || true rm -rf "$HOME/Library/Logs/app.glaze.macos.${id}-local" 2>/dev/null || true - print -r -- "✓ Fjernet." + print -r -- "✓ Removed." } cmd_help() { cat <<'EOF' -glaze-dev, gratis utvikling av Glaze-apper med din egen Claude Code - -ENKLESTE VEI (fra ingenting til å kode, én kommando): - glaze-dev start "Min App" Lager en tom app (0 kreditter) og åpner Claude - Code der, bare beskriv hva appen skal gjøre. - -ALLE KOMMANDOER: - glaze-dev list List alle Glaze-apper - glaze-dev start "" Ny tom app + hopp rett inn i Claude Code - glaze-dev new "" [--from ] [--blank] [--no-open] - Lag en ny app uten kreditter (uten å åpne claude) - glaze-dev code [--agent ] [prompt] - Åpne app-mappen i en agent (gratis). - Agenter: claude (standard), codex, opencode, +glaze-dev, free development of Glaze apps with your own Claude Code + +SIMPLEST PATH (from nothing to coding, one command): + glaze-dev start "My App" Creates an empty app (0 credits) and opens Claude + Code there, just describe what the app should do. + +ALL COMMANDS: + glaze-dev list List all Glaze apps + glaze-dev start "" New empty app + jump straight into Claude Code + glaze-dev new "" [--from ] [--blank] [--no-open] + Create a new app without credits (without opening claude) + glaze-dev code [--agent ] [prompt] + Open the app folder in an agent (free). + Agents: claude (default), codex, opencode, gemini, codex-app, opencode-app, antigravity, zcode - glaze-dev agents List agentene du har installert - glaze-dev dev Dev-server (live reload) - glaze-dev build Bygg (lokalt, gratis) - glaze-dev run Åpne den bygde appen - glaze-dev br Bygg + kjør + glaze-dev agents List the agents you have installed + glaze-dev dev Dev server (live reload) + glaze-dev build Build (locally, free) + glaze-dev run Open the built app + glaze-dev br Build + run glaze-dev check TypeScript type-check glaze-dev lint ESLint - glaze-dev path Skriv ut sti til .glaze-sources - glaze-dev queue Les ut pausede prompts fra Glaze-vibekoderen - (krever at Glaze er åpen med appens chat) - glaze-dev handoff Skriv ut handoff-prompten (grunntekst + evt. kø) - glaze-dev icon Generer nytt app-ikon (initialer + gradient) - glaze-dev finder Åpne app-mappen i Finder - glaze-dev rm Fjern app fullstendig (bundle+kilde+profil) - glaze-dev raycast Sett opp Raycast (kopier sti + åpne Raycast) - glaze-dev setup Onboarding/helsesjekk av hele oppsettet - glaze-dev skills Koble Glaze-skills inn i ~/.claude/skills - - matcher på mappenavn eller produktnavn (delvis treff er ok). - -NB: nye apper er ad-hoc-signert for eget bruk. Publisering til den offentlige -Glaze Store krever fortsatt Glaze-innlogging (gratis, ikke kreditter). `new`/`start` -bruker en app du allerede eier som mal (--blank gir tomt UI du bygger ut selv). + glaze-dev path Print the path to .glaze-sources + glaze-dev queue Read out paused prompts from the Glaze vibe coder + (requires Glaze to be open with the app's chat) + glaze-dev handoff Print the handoff prompt (base text + any queue) + glaze-dev icon Generate a new app icon (initials + gradient) + glaze-dev finder Open the app folder in Finder + glaze-dev rm Remove the app completely (bundle+source+profile) + glaze-dev raycast Set up Raycast (copy path + open Raycast) + glaze-dev setup Onboarding/health check of the whole setup + glaze-dev skills Link Glaze skills into ~/.claude/skills + + matches on folder name or product name (partial matches are fine). + +Note: new apps are ad-hoc signed for your own use. Publishing to the public +Glaze Store still requires a Glaze login (free, not credits). `new`/`start` +use an app you already own as a template (--blank gives an empty UI you build out). EOF } @@ -837,5 +858,5 @@ case "$cmd" in setup|doctor) cmd_setup "$@" ;; skills|link-skills) cmd_skills ;; help|-h|--help) cmd_help ;; - *) print -u2 "Ukjent kommando: $cmd"; cmd_help; exit 1 ;; + *) print -u2 "Unknown command: $cmd"; cmd_help; exit 1 ;; esac diff --git a/plugins/glaze-coder/scripts/raycast/glaze-edit.sh b/plugins/glaze-coder/scripts/raycast/glaze-edit.sh index f81e921..f20d5d9 100755 --- a/plugins/glaze-coder/scripts/raycast/glaze-edit.sh +++ b/plugins/glaze-coder/scripts/raycast/glaze-edit.sh @@ -5,17 +5,18 @@ # @raycast.packageName Glaze Coder # @raycast.icon 🛠️ # @raycast.argument1 { "type": "text", "placeholder": "app name" } -# @raycast.argument2 { "type": "dropdown", "placeholder": "verktøy", "optional": true, "data": [{"title": "Claude Code", "value": "claude"}, {"title": "ZCode (z.ai)", "value": "zcode"}] } -# @raycast.argument3 { "type": "dropdown", "placeholder": "hvor", "optional": true, "data": [{"title": "Auto (kjørende/sist brukte terminal)", "value": "auto"}, {"title": "Terminal", "value": "terminal"}, {"title": "iTerm", "value": "iterm"}, {"title": "Desktop-appen", "value": "desktop"}] } +# @raycast.argument2 { "type": "dropdown", "placeholder": "tool", "optional": true, "data": [{"title": "Claude Code", "value": "claude"}, {"title": "ZCode (z.ai)", "value": "zcode"}] } +# @raycast.argument3 { "type": "dropdown", "placeholder": "where", "optional": true, "data": [{"title": "Auto (running/last used terminal)", "value": "auto"}, {"title": "Terminal", "value": "terminal"}, {"title": "iTerm", "value": "iterm"}, {"title": "Desktop app", "value": "desktop"}] } # -# Valgene skjer i Raycast-feltene (dropdown), ingen ekstern dialog = aldri fokustrøbbel. -# Tomt verktøy = Claude Code. Tomt sted = auto. Nye terminaler: legg til i data-listen. +# The choices happen in the Raycast fields (dropdowns), no external dialog = never any +# focus trouble. Empty tool = Claude Code. Empty place = auto. New terminals: add them +# to the data list. export PATH="$HOME/.local/bin:/usr/bin:/bin:/usr/sbin:/sbin:$PATH" source "$HOME/glaze-coder/plugins/glaze-coder/scripts/terminal-launch.sh" src="$("$HOME/.local/bin/glaze-dev" path "$1" 2>/dev/null)" if [[ -z "$src" ]]; then - echo "Fant ingen app som matcher '$1'. Prøv 'Glaze: New App' først, eller sjekk navnet." + echo "Found no app matching '$1'. Try 'Glaze: New App' first, or check the name." exit 0 fi @@ -26,14 +27,16 @@ cmd="exec $HOME/.local/bin/glaze-dev code --tool '$tool' '$1'" urlenc() { python3 -c 'import sys,urllib.parse; print(urllib.parse.quote(sys.argv[1], safe=""))' "$1"; } -# Handoff-prompten som limes inn hos agenten. Agent-uavhengig: gjelder både Claude -# Code og ZCode. Bygges ett sted (glaze-dev), som også fletter inn en pauset Glaze-kø. +# The handoff prompt that gets pasted into the agent. Agent-independent: applies to +# both Claude Code and ZCode. Built in one place (glaze-dev), which also merges in a +# paused Glaze queue. build_handoff() { "$HOME/.local/bin/glaze-dev" handoff "$1" 2>/dev/null; } -# ZCode er en GUI-IDE, så "hvor"-valget gjelder ikke: vi gjør alltid den sømløse -# flyten. glaze-dev eier hele den (åpne arbeidsområde via deep link, ny tråd med -# Cmd+N, lim inn handoff med Cmd+V, aldri Enter) slik at Raycast og terminal deler -# nøyaktig samme logikk. Statuslinjene under vises rett i Raycast sitt output-panel. +# ZCode is a GUI IDE, so the "where" choice does not apply: we always do the seamless +# flow. glaze-dev owns all of it (open the workspace via deep link, new thread with +# Cmd+N, paste the handoff with Cmd+V, never Enter) so that Raycast and the terminal +# share exactly the same logic. The status lines below show up right in Raycast's +# output panel. if [[ "$tool" == zcode ]]; then "$HOME/.local/bin/glaze-dev" code --tool zcode "$1" exit 0 @@ -41,23 +44,23 @@ fi case "$where" in desktop) - # Claude Desktop: claude://-deeplink åpner Code-fanen på riktig mappe med - # handoff-prompten ferdig utfylt (bruker trykker Enter selv). Har Glaze- - # vibekoderen en pauset kø (f.eks. tomme kreditter), hentes den inn også. + # Claude Desktop: the claude:// deep link opens the Code tab on the right folder + # with the handoff prompt filled in (the user presses Enter themselves). If the + # Glaze vibe coder has a paused queue (e.g. out of credits), it is pulled in too. handoff="$(build_handoff "$1")" printf '%s' "$handoff" | pbcopy if open "claude://code/new?folder=$(urlenc "$src")&q=$(urlenc "$handoff")" 2>/dev/null; then - osascript -e "display notification \"Åpner $1 i Claude Code med mappe og handoff. Trykk Enter der for å starte.\" with title \"Glaze: Edit $1\"" 2>/dev/null - echo "Åpnet Claude Code (desktop) på '$1' med handoff-prompt. Trykk Enter der for å starte." + osascript -e "display notification \"Opening $1 in Claude Code with the folder and handoff. Press Enter there to start.\" with title \"Glaze: Edit $1\"" 2>/dev/null + echo "Opened Claude Code (desktop) on '$1' with the handoff prompt. Press Enter there to start." else open -a Claude - osascript -e "display notification \"Velg $1 i Code-fanen og lim inn handoff-prompten (Cmd+V).\" with title \"Glaze: Edit $1\"" 2>/dev/null - echo "Åpnet Claude Desktop. Velg '$1' i Code-fanen og lim inn handoff-prompten (ligger på utklippstavlen)." + osascript -e "display notification \"Pick $1 in the Code tab and paste the handoff prompt (Cmd+V).\" with title \"Glaze: Edit $1\"" 2>/dev/null + echo "Opened Claude Desktop. Pick '$1' in the Code tab and paste the handoff prompt (it is on the clipboard)." fi ;; auto) term="$(launch_in_terminal "$cmd")" - echo "Åpnet $label for '$1' i $term (auto)." ;; + echo "Opened $label for '$1' in $term (auto)." ;; *) term="$(GLAZE_TERMINAL="$where" launch_in_terminal "$cmd")" - echo "Åpnet $label for '$1' i $term." ;; + echo "Opened $label for '$1' in $term." ;; esac diff --git a/plugins/glaze-coder/scripts/raycast/glaze-new.sh b/plugins/glaze-coder/scripts/raycast/glaze-new.sh index e1753c5..45db24e 100755 --- a/plugins/glaze-coder/scripts/raycast/glaze-new.sh +++ b/plugins/glaze-coder/scripts/raycast/glaze-new.sh @@ -5,13 +5,14 @@ # @raycast.packageName Glaze Coder # @raycast.icon 🟢 # @raycast.argument1 { "type": "text", "placeholder": "app name" } -# @raycast.argument2 { "type": "dropdown", "placeholder": "verktøy", "optional": true, "data": [{"title": "Claude Code", "value": "claude"}, {"title": "ZCode (z.ai)", "value": "zcode"}] } +# @raycast.argument2 { "type": "dropdown", "placeholder": "tool", "optional": true, "data": [{"title": "Claude Code", "value": "claude"}, {"title": "ZCode (z.ai)", "value": "zcode"}] } # -# Lager en tom app og åpner kodeverktøyet direkte med startprompt (glaze-dev start). -# Verktøy: Claude Code (standard) eller ZCode. Terminal velges automatisk. +# Creates an empty app and opens the coding tool directly with a starter prompt +# (glaze-dev start). Tool: Claude Code (default) or ZCode. The terminal is picked +# automatically. export PATH="$HOME/.local/bin:/usr/bin:/bin:/usr/sbin:/sbin:$PATH" source "$HOME/glaze-coder/plugins/glaze-coder/scripts/terminal-launch.sh" tool="${2:-claude}" [[ "$tool" == zcode ]] && label="ZCode" || label="Claude Code" term="$(launch_in_terminal "exec $HOME/.local/bin/glaze-dev start --tool '$tool' '$1'")" -echo "Oppretter '$1' og åpner $label i $term. Beskriv hva appen skal gjøre der." +echo "Creating '$1' and opening $label in $term. Describe what the app should do there." diff --git a/plugins/glaze-coder/scripts/terminal-launch.sh b/plugins/glaze-coder/scripts/terminal-launch.sh index d0c6e62..e956c5e 100644 --- a/plugins/glaze-coder/scripts/terminal-launch.sh +++ b/plugins/glaze-coder/scripts/terminal-launch.sh @@ -1,9 +1,9 @@ #!/bin/zsh -# terminal-launch.sh - velg og start riktig terminal for interaktive kommandoer. -# Bruk: source terminal-launch.sh; launch_in_terminal "" -# Overstyring: GLAZE_TERMINAL=iterm|terminal|ghostty|kitty|alacritty|wezterm +# terminal-launch.sh - pick and start the right terminal for interactive commands. +# Usage: source terminal-launch.sh; launch_in_terminal "" +# Override: GLAZE_TERMINAL=iterm|terminal|ghostty|kitty|alacritty|wezterm -_tl_installed() { # -> full sti eller tomt +_tl_installed() { # -> full path or empty local a for a in "/Applications/$1" "/Applications/Utilities/$1" "/System/Applications/Utilities/$1" "$HOME/Applications/$1"; do [[ -d "$a" ]] && { print -r -- "$a"; return 0; } @@ -11,14 +11,14 @@ _tl_installed() { # -> full sti eller tomt return 1 } -_tl_lastused() { # -> epoch (0 hvis ukjent) +_tl_lastused() { # -> epoch (0 if unknown) local d="$(mdls -raw -name kMDItemLastUsedDate "$1" 2>/dev/null)" [[ -z "$d" || "$d" == "(null)" ]] && { print 0; return; } date -j -f "%Y-%m-%d %H:%M:%S %z" "$d" +%s 2>/dev/null || print 0 } _tl_pick() { - # 1) Manuelt valg + # 1) Manual choice case "${GLAZE_TERMINAL:l}" in iterm) print iTerm; return ;; terminal) print Terminal; return ;; @@ -27,14 +27,14 @@ _tl_pick() { alacritty) print Alacritty; return ;; wezterm) print WezTerm; return ;; esac - # 2) Kjørende terminal vinner + # 2) A running terminal wins pgrep -qx iTerm2 && { print iTerm; return; } pgrep -qx ghostty && { print Ghostty; return; } pgrep -qx kitty && { print kitty; return; } pgrep -qx alacritty && { print Alacritty; return; } pgrep -qx wezterm-gui && { print WezTerm; return; } pgrep -qx Terminal && { print Terminal; return; } - # 3) Sist brukte av de installerte + # 3) Most recently used of the installed ones local best="Terminal" best_ts=-1 name path ts for name in iTerm Ghostty kitty Alacritty WezTerm Terminal; do path="$(_tl_installed "$name.app")" || continue @@ -44,9 +44,9 @@ _tl_pick() { print -r -- "$best" } -launch_in_terminal() { # +launch_in_terminal() { # local cmd="$1" term="$(_tl_pick)" - local esc="${cmd//\\/\\\\}"; esc="${esc//\"/\\\"}" # escape for AppleScript-streng + local esc="${cmd//\\/\\\\}"; esc="${esc//\"/\\\"}" # escape for an AppleScript string case "$term" in iTerm) osascript -e 'tell application "iTerm" to activate' \