@turmind/halo 1.5.7 → 1.5.9
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/admin-out/404.html +1 -1
- package/admin-out/_next/static/chunks/220.7566bc89f3d98993.js +1 -0
- package/admin-out/_next/static/chunks/597-399e2e1de26aa747.js +1 -0
- package/admin-out/_next/static/chunks/989-c404f6f43c400911.js +1 -0
- package/admin-out/_next/static/chunks/app/page-59bfce35bbb30b05.js +1 -0
- package/admin-out/_next/static/chunks/{webpack-edf248e07333566d.js → webpack-efd27999305b080a.js} +1 -1
- package/admin-out/_next/static/css/27b44c85485eabb9.css +3 -0
- package/admin-out/index.html +1 -1
- package/admin-out/index.txt +5 -5
- package/bundled-docs/design/architecture.md +3 -2
- package/bundled-docs/design/canvas-extensions.md +51 -11
- package/bundled-docs/design/cron.md +6 -6
- package/bundled-docs/design/evolution.md +47 -15
- package/bundled-docs/design/express-self.md +96 -20
- package/bundled-docs/design/storage.md +10 -2
- package/bundled-docs/design/ws.md +1 -0
- package/bundled-docs/dev/add-model-provider.md +16 -7
- package/bundled-docs/dev/api.md +7 -6
- package/bundled-docs/dev/desktop-packaging.md +26 -0
- package/bundled-docs/guide/agents.md +3 -3
- package/bundled-docs/guide/chat.md +3 -0
- package/bundled-docs/guide/cli.md +10 -1
- package/bundled-docs/guide/workspace.md +2 -2
- package/bundled-docs/requirements/agent.md +1 -1
- package/bundled-docs/requirements/chat.md +17 -9
- package/bundled-docs/requirements/editor.md +3 -1
- package/bundled-docs/requirements/explorer.md +4 -2
- package/bundled-docs/requirements/settings.md +11 -3
- package/bundled-docs/requirements/workspace.md +2 -2
- package/dist/index.js +2514 -1669
- package/package.json +2 -1
- package/templates/agents/__evo_agent__/AGENT.md +7 -0
- package/templates/agents/__score__/AGENT.md +183 -71
- package/templates/canvas/self.html +617 -71
- package/templates/models/anthropic.yaml +1 -0
- package/templates/models/aws-bedrock-claude-invoke.yaml +1 -0
- package/templates/models/aws-bedrock-mantle.yaml +1 -0
- package/templates/models/aws-bedrock-openai.yaml +1 -0
- package/templates/models/deepseek.yaml +1 -0
- package/templates/models/doubao.yaml +1 -0
- package/templates/models/hunyuan.yaml +1 -0
- package/templates/models/kimi.yaml +1 -0
- package/templates/models/mimo-token-plan-china.yaml +1 -0
- package/templates/models/minimax.yaml +1 -0
- package/templates/models/openai.yaml +1 -0
- package/templates/models/qwen.yaml +1 -0
- package/templates/models/zhipu.yaml +1 -0
- package/templates/skills/cron/SKILL.md +59 -4
- package/templates/skills/cron/manage-cron.py +158 -24
- package/templates/skills/extension/SKILL.md +30 -2
- package/templates/skills/extension/config.yaml +2 -2
- package/templates/skills/extension/templates/ext.sh +69 -16
- package/templates/skills/self/SKILL.md +79 -17
- package/admin-out/_next/static/chunks/220.e250bbbf65d24094.js +0 -1
- package/admin-out/_next/static/chunks/422-cc74b1fdd3af5415.js +0 -1
- package/admin-out/_next/static/chunks/989-e0faeff1c4a0b0b9.js +0 -1
- package/admin-out/_next/static/chunks/app/page-44f707ea8dad57da.js +0 -1
- package/admin-out/_next/static/css/b8c87b919758c52b.css +0 -3
- /package/admin-out/_next/static/{rTD3zkMvzwtqUwzhz6QJH → RD5w3FmDZ6HtyQGGaUb8t}/_buildManifest.js +0 -0
- /package/admin-out/_next/static/{rTD3zkMvzwtqUwzhz6QJH → RD5w3FmDZ6HtyQGGaUb8t}/_ssgManifest.js +0 -0
|
@@ -8,6 +8,8 @@
|
|
|
8
8
|
# ext.sh install <id | path/to.zip | https://…zip>
|
|
9
9
|
# ext.sh list
|
|
10
10
|
# ext.sh remove <id>
|
|
11
|
+
# ext.sh models update [--yes] # hub's newest models-v* zip → halo models install
|
|
12
|
+
# ext.sh models list # = halo models list
|
|
11
13
|
#
|
|
12
14
|
# `install <id>` reads the hub in $HALO_HUB_REPO (the skill's hub_repo param;
|
|
13
15
|
# empty → https://github.com/turmind/halo-hub). GitHub / Gitea-Forgejo / GitLab
|
|
@@ -153,26 +155,71 @@ hub_git_package() {
|
|
|
153
155
|
echo "$dir"
|
|
154
156
|
}
|
|
155
157
|
|
|
156
|
-
#
|
|
157
|
-
#
|
|
158
|
-
#
|
|
159
|
-
|
|
160
|
-
local
|
|
161
|
-
|
|
162
|
-
if [[ $platform == git ]]; then hub_git_package "$hub" "$id" "$tmp"; return; fi
|
|
158
|
+
# Download the newest `<id>-v*` release zip of <platform> hub $2 into $4 and
|
|
159
|
+
# echo its path. Extra args ($5…) go to every curl (models pass
|
|
160
|
+
# `--proto =https --proto-redir =https`).
|
|
161
|
+
hub_fetch_release() {
|
|
162
|
+
local platform=$1 hub=$2 id=$3 tmp=$4 api url token page
|
|
163
|
+
shift 4
|
|
163
164
|
api=$(hub_releases_api "$platform" "$hub")
|
|
164
165
|
page=$(hub_releases_page "$platform" "$hub")
|
|
165
166
|
case $platform in github) token=GITHUB_TOKEN ;; gitlab) token=GITLAB_TOKEN ;; *) token=GITEA_TOKEN ;; esac
|
|
166
|
-
hub_curl "$platform" "$api" -o "$tmp/releases.json" \
|
|
167
|
+
hub_curl "$platform" "$@" "$api" -o "$tmp/releases.json" \
|
|
167
168
|
|| { echo "$platform release API failed for $hub — rate-limited, private or wrong repo? set $token, or download the zip from $page and run: install <path>" >&2; exit 1; }
|
|
168
169
|
url=$(hub_pick_release "$platform" "$id" <"$tmp/releases.json") \
|
|
169
170
|
|| { echo "no release for $id in $hub — check the id, or download the zip from $page and run: install <path>" >&2; exit 1; }
|
|
170
171
|
# No token here: asset links redirect to other hosts and curl -L would
|
|
171
172
|
# forward a custom header like PRIVATE-TOKEN there.
|
|
172
|
-
curl -fsSL "$url" -o "$tmp/pkg.zip" || { echo "download failed: $url" >&2; exit 1; }
|
|
173
|
+
curl -fsSL "$@" "$url" -o "$tmp/pkg.zip" || { echo "download failed: $url" >&2; exit 1; }
|
|
173
174
|
echo "$tmp/pkg.zip"
|
|
174
175
|
}
|
|
175
176
|
|
|
177
|
+
# Fetch hub package <id> from hub URL $1 into $3: echo a zip path (release
|
|
178
|
+
# API) or the package dir (git tags). API errors never fall back to git — a
|
|
179
|
+
# git checkout of a build-step package would install broken.
|
|
180
|
+
hub_fetch() {
|
|
181
|
+
local hub=$1 id=$2 tmp=$3 platform
|
|
182
|
+
platform=$(hub_platform "$hub")
|
|
183
|
+
if [[ $platform == git ]]; then hub_git_package "$hub" "$id" "$tmp"; return; fi
|
|
184
|
+
hub_fetch_release "$platform" "$hub" "$id" "$tmp"
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
# Unpack zip $1 into the new dir $2 with the same refusals as the server's zip
|
|
188
|
+
# installer: no `..`, no absolute paths, no symlinks (zipinfo mode column
|
|
189
|
+
# starts with `l`).
|
|
190
|
+
safe_unzip() {
|
|
191
|
+
if unzip -Z1 "$1" | grep -Eq '(^|/)\.\.(/|$)|^/'; then echo "zip contains ../ or absolute entries" >&2; exit 1; fi
|
|
192
|
+
if unzip -Z "$1" | grep -q '^l'; then echo "zip contains symlinks" >&2; exit 1; fi
|
|
193
|
+
mkdir -p "$2"
|
|
194
|
+
unzip -q "$1" -d "$2"
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
# Model provider configs: newest `models-v*` release of hub $1, https +
|
|
198
|
+
# release API only (a provider yaml carries endpoints the server will call —
|
|
199
|
+
# no plain-git / local / http source). Validation, planning and the write into
|
|
200
|
+
# ~/.halo/global/models.d/ belong to `halo models install` ($HALO_CLI in dev);
|
|
201
|
+
# its exit code (3 = endpoint changes need --yes) passes through.
|
|
202
|
+
models_update() {
|
|
203
|
+
local hub=$1 platform zip; shift
|
|
204
|
+
if [[ $hub != https://* ]]; then
|
|
205
|
+
echo "model configs need an https release hub (GitHub / Gitea / Forgejo / GitLab) — $hub is not one" >&2; exit 1
|
|
206
|
+
fi
|
|
207
|
+
platform=$(hub_platform "$hub")
|
|
208
|
+
if [[ $platform == git ]]; then
|
|
209
|
+
echo "model configs need a release-API hub (GitHub / Gitea / Forgejo / GitLab) — $hub has no release API" >&2; exit 1
|
|
210
|
+
fi
|
|
211
|
+
zip=$(hub_fetch_release "$platform" "$hub" models "$TMP" --proto =https --proto-redir =https)
|
|
212
|
+
safe_unzip "$zip" "$TMP/models"
|
|
213
|
+
local dir="$TMP/models" only
|
|
214
|
+
# A zip wrapping everything in one directory — descend into it.
|
|
215
|
+
if ! compgen -G "$dir/*.yaml" >/dev/null; then
|
|
216
|
+
only=$(find "$dir" -mindepth 1 -maxdepth 1 | head -2)
|
|
217
|
+
if [[ $(printf '%s\n' "$only" | wc -l) -eq 1 && -d $only ]]; then dir=$only; fi
|
|
218
|
+
fi
|
|
219
|
+
# Last command: under `set -e` its non-zero status (3 = confirm) is the script's.
|
|
220
|
+
"${HALO_CLI:-halo}" models install "$dir" "$@"
|
|
221
|
+
}
|
|
222
|
+
|
|
176
223
|
# Echo a local zip or an unpacked package dir for source $1 (path, https URL, or hub id).
|
|
177
224
|
resolve_source() {
|
|
178
225
|
local src=$1
|
|
@@ -196,12 +243,7 @@ case $cmd in
|
|
|
196
243
|
if [[ -d $pkg ]]; then
|
|
197
244
|
dir=$pkg
|
|
198
245
|
else
|
|
199
|
-
|
|
200
|
-
# no symlinks (zipinfo mode column starts with `l`).
|
|
201
|
-
if unzip -Z1 "$pkg" | grep -Eq '(^|/)\.\.(/|$)|^/'; then echo "zip contains ../ or absolute entries" >&2; exit 1; fi
|
|
202
|
-
if unzip -Z "$pkg" | grep -q '^l'; then echo "zip contains symlinks" >&2; exit 1; fi
|
|
203
|
-
mkdir -p "$TMP/x"
|
|
204
|
-
unzip -q "$pkg" -d "$TMP/x"
|
|
246
|
+
safe_unzip "$pkg" "$TMP/x"
|
|
205
247
|
# `zip -r glb.zip glb/` wraps everything in one directory — descend into it.
|
|
206
248
|
dir="$TMP/x"
|
|
207
249
|
if [[ ! -f $dir/halo-extension.json ]]; then
|
|
@@ -239,6 +281,17 @@ case $cmd in
|
|
|
239
281
|
[[ -d $ROOT/$id ]] || { echo "not installed: $id" >&2; exit 1; }
|
|
240
282
|
rm -rf "$ROOT/$id"
|
|
241
283
|
echo "removed $id" ;;
|
|
284
|
+
models)
|
|
285
|
+
sub=${1:-update}
|
|
286
|
+
shift || true
|
|
287
|
+
case $sub in
|
|
288
|
+
list) exec "${HALO_CLI:-halo}" models list ;;
|
|
289
|
+
update)
|
|
290
|
+
for a in "$@"; do [[ $a == --yes ]] || { echo "usage: ext.sh models update [--yes] | models list" >&2; exit 1; }; done
|
|
291
|
+
TMP=$(mktemp -d); trap 'rm -rf "$TMP"' EXIT
|
|
292
|
+
models_update "$(hub_url "${HALO_HUB_REPO:-}")" "$@" ;;
|
|
293
|
+
*) echo "usage: ext.sh models update [--yes] | models list" >&2; exit 1 ;;
|
|
294
|
+
esac ;;
|
|
242
295
|
*)
|
|
243
|
-
echo "unknown command: $cmd (install | list | remove)" >&2; exit 1 ;;
|
|
296
|
+
echo "unknown command: $cmd (install | list | remove | models)" >&2; exit 1 ;;
|
|
244
297
|
esac
|
|
@@ -38,12 +38,23 @@ expression scenes (`say`/`play`) **queue and play in order**, one after the next
|
|
|
38
38
|
|
|
39
39
|
**The face must be OPEN for any of this to do anything.** `<<<SHOW>>>` is
|
|
40
40
|
forwarded only to a mounted `self.html` preview; if it isn't open, the marker is
|
|
41
|
-
silently dropped — the user sees nothing.
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
41
|
+
silently dropped — the user sees nothing. The signal that it's open is a
|
|
42
|
+
`[Face open: .halo/canvas/self.html …]` line on the user's message: when they
|
|
43
|
+
turn the ✨ switch on in the chat toolbar, every message carries it. (A
|
|
44
|
+
`[Currently viewing: …self.html]` line counts too.) No such line means the face
|
|
45
|
+
is **not** open — don't rely on a marker landing in the void; say what you want
|
|
46
|
+
to express in words and invite them to switch ✨ on.
|
|
47
|
+
|
|
48
|
+
**What comes back.** The face reports to you, but never wakes you: it queues short
|
|
49
|
+
receipts and they arrive riding on the user's *next* message, at the end of the
|
|
50
|
+
`[Face open: …]` line — `· last: js ok, show a.png fail, voice blocked (needs a
|
|
51
|
+
click)`. They describe the previous round, not this one. How to read them:
|
|
52
|
+
`js ok` / `js err: …` — your line ran / threw (fix the code); `show … fail` —
|
|
53
|
+
wrong path or not an image; `voice … playing` / `ended 3.2s` / `fail`;
|
|
54
|
+
`voice blocked (needs a click)` — the browser refused sound until the user
|
|
55
|
+
touches the page: ask them to click the face once (it then plays on its own);
|
|
56
|
+
`user click`, `user closed tr`, `user click → voice resumed` — what the user did
|
|
57
|
+
on the face. No receipt line means nothing noteworthy happened.
|
|
47
58
|
|
|
48
59
|
**Expression is runtime, not a file edit.** You express yourself by *sending*
|
|
49
60
|
`<<<SHOW: …>>>` lines — never by editing `self.html`. The file is your **engine**
|
|
@@ -61,20 +72,25 @@ not per-conversation.)
|
|
|
61
72
|
both form. Emoji are translated to ASCII first (👍→`+1`, ❤→`<3`, 🤔→`...`) —
|
|
62
73
|
the face speaks in cold monospace, not colour bitmaps, so this is a feature,
|
|
63
74
|
not a fallback. Anything untranslated is stripped.
|
|
75
|
+
`self.say("OK", 3000, {pos:"tr"})` sets it small in a margin cell instead of
|
|
76
|
+
across the whole face (see `show` below for the nine cells) — immediate, beside
|
|
77
|
+
whatever is in the centre. Keep corner text to a word.
|
|
64
78
|
- `self.play(score)` — choreograph a sequence; the face keeps the clock so you
|
|
65
79
|
never hand-write `setTimeout` chains. `score` is an array of beats, each one
|
|
66
|
-
of: `{say, hold, pulse, flash, shake, rest, gap}`. Calling it again cancels the
|
|
80
|
+
of: `{say, show, pos, hold, pulse, flash, shake, snap, rest, gap}`. Calling it again cancels the
|
|
67
81
|
running score. Example — a short greeting:
|
|
68
82
|
`self.play([{say:"HI",hold:1800},{say:"...",hold:1200},{say:"OK",hold:1500},{pulse:true}])`
|
|
83
|
+
A `show` beat puts an image on the face (see `self.show`): `{show:"<path>", hold:3000}`.
|
|
69
84
|
- `self.react(event)` — a named vocabulary of honest reactions for the small
|
|
70
85
|
beats of a conversation, so you can answer a moment in one word:
|
|
71
86
|
`'ack'` (a nod), `'yes'`, `'no'` (disagree / that's wrong), `'insight'` (a real
|
|
72
87
|
"oh!"), `'think'` (working on it), `'done'` (finished, together). Unknown
|
|
73
88
|
events no-op. Use these when the feeling is **true**, never to perform.
|
|
74
|
-
- `self.intro()` — the built-in opening (
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
89
|
+
- `self.intro()` — the built-in opening (played once when the user turns the face
|
|
90
|
+
on): one short subtitle in the admin's UI language — "Hi, I'm Halo." / 「你好,我是
|
|
91
|
+
Halo。」 — ~3 s. It names only Halo, the product —
|
|
92
|
+
never a model; your identity in the conversation is still whatever the user
|
|
93
|
+
configured, so don't hard-code "I'm Claude" (or any model) on the face.
|
|
78
94
|
- `self.pulse()` — one bright ripple from the core. A nod.
|
|
79
95
|
- `self.flash(n)` — a hot flicker of the whole field. Emphasis, an exclamation.
|
|
80
96
|
- `self.shake(ms)` — a brief lateral tremor. Negation, a shiver.
|
|
@@ -88,11 +104,56 @@ not per-conversation.)
|
|
|
88
104
|
or pass a projectId. (A full `http(s)://`/`/api/…` URL also works if you have
|
|
89
105
|
one.) It starts immediately (not queued) and owns the matrix until the clip
|
|
90
106
|
ends, then eases home. Drive it the moment the audio is ready, in the same
|
|
91
|
-
reply that delivers the spoken answer.
|
|
107
|
+
reply that delivers the spoken answer. If the browser refuses sound (no user
|
|
108
|
+
gesture yet) the clip waits instead of vanishing: you'll get the receipt
|
|
109
|
+
`voice blocked (needs a click)` — ask the user to click the face once and it plays.
|
|
110
|
+
- `self.voice(path, {cues:[{at, say?, js?}]})` — put words or code on the clip's own
|
|
111
|
+
clock: at `at` seconds into the clip the word forms on the dots (the voice isn't
|
|
112
|
+
cut — the words borrow the field until the next cue, then the wave returns) or
|
|
113
|
+
the `js` runs, each cue once. `self.voice(".halo/tmp/a.mp3", {cues:[{at:0.4,
|
|
114
|
+
say:"HI"},{at:2,js:"self.pulse()"}]})`. Halo has no built-in TTS, so the
|
|
115
|
+
times are yours to supply — from the clip's duration or from timestamps the
|
|
116
|
+
synthesizer gives you (e.g. Polly speech marks). Don't guess them blind.
|
|
117
|
+
- `self.show(path, ms)` — put an image on the face. The dots fly in and gather
|
|
118
|
+
into the picture's outline (~1 s), then the real image fades in over them —
|
|
119
|
+
dots alone can't carry a chart's text, so the real image is always there — holds
|
|
120
|
+
for `ms` (default 6000), fades out, and the dots dissolve back to breathing.
|
|
121
|
+
The image fits ~80% of the window, aspect preserved, centered. Like `voice`,
|
|
122
|
+
pass the **workspace path** (`self.show(".halo/tmp/chart.png", 5000)`); the face
|
|
123
|
+
resolves the URL itself, no projectId. `ms = Infinity` keeps it up until the
|
|
124
|
+
next scene, `self.rest()`, or a click on the page. Formats: png / jpg / jpeg /
|
|
125
|
+
webp / gif / svg only. A missing or non-image file doesn't stall anything — it
|
|
126
|
+
warns, shakes, and the next beat plays. `.excalidraw` / `.drawio` are editor
|
|
127
|
+
sources and can't be shown: **for a diagram, write an SVG file directly and show
|
|
128
|
+
it.** It queues like `say()`, so several `show()`s and `say()`s play in order,
|
|
129
|
+
and a `voice()` clip keeps rippling the field around the picture. A talking
|
|
130
|
+
picture book (word audio + picture + the word on the dots):
|
|
131
|
+
`self.voice(".halo/tmp/apple.mp3")` then
|
|
132
|
+
`self.play([{show:".halo/tmp/apple.png",hold:3000},{say:"APPLE",hold:1500}])`.
|
|
133
|
+
- `self.show(path, ms, {pos})` — the same picture, small, in one cell of a
|
|
134
|
+
nine-grid: `pos` is `tl t tr l r bl b br` (the centre is the default, `'c'`).
|
|
135
|
+
About 40% of the centre size, a few dots frame it, and it is **immediate and
|
|
136
|
+
outside the queue** — it never holds up the `say`/`show` after it. One picture
|
|
137
|
+
per cell; showing into the same cell again replaces it. `ms` works as above
|
|
138
|
+
(`Infinity` = stays). A plain centre `show()` takes all the corner pictures
|
|
139
|
+
down. `self.show(".halo/tmp/logo.png", 8000, {pos:"tr"})`. A click on a
|
|
140
|
+
picture closes just that one. A bad path warns, shakes, and sends `show …@tr fail`.
|
|
141
|
+
- `self.clear(pos?)` — take down the corner picture/text in cell `pos`, or with no
|
|
142
|
+
argument all of them. `self.clear("tr")`.
|
|
143
|
+
- `self.snap()` — look at yourself: the face as the user sees it right now (dots,
|
|
144
|
+
words, centre and corner pictures) comes back to you as an image on your next
|
|
145
|
+
turn. It waits its turn in the queue, so `self.say("OK"); self.snap()` shows
|
|
146
|
+
you the finished word. Use it sparingly — only when you need to confirm how
|
|
147
|
+
something actually looks (a layout, whether the picture landed), at most one
|
|
148
|
+
per round. The snapshot arrives as its own message and the round it starts
|
|
149
|
+
can't snap again (a second one is dropped), so it can't chain. Also a beat:
|
|
150
|
+
`{snap:true}` in `play()`.
|
|
92
151
|
- `self.rest()` — return to the calm breathing state immediately (also stops a
|
|
93
|
-
playing voice clip
|
|
94
|
-
|
|
95
|
-
|
|
152
|
+
playing voice clip and removes the centre image and every corner one, so the face
|
|
153
|
+
never breathes calmly over still-sounding audio).
|
|
154
|
+
- `self.state` — read current `{mode, awake, W, H, speaking, level, showing}` if
|
|
155
|
+
you need it (`speaking` = a voice clip is playing, `level` = its live loudness
|
|
156
|
+
0..1, `showing` = an image is on the face).
|
|
96
157
|
|
|
97
158
|
You can also send *any* JavaScript — `self` is the surface, but the code runs in
|
|
98
159
|
the page, so improvisation beyond these helpers is allowed (it's sandboxed to the
|
|
@@ -102,8 +163,9 @@ preview; it can only ever paint you, and a malformed line just no-ops).
|
|
|
102
163
|
|
|
103
164
|
The honest signal — not a checklist, a judgment:
|
|
104
165
|
|
|
105
|
-
- The
|
|
106
|
-
|
|
166
|
+
- The face is open: the user's message carries `[Face open: .halo/canvas/self.html …]`
|
|
167
|
+
(or `[Currently viewing: …self.html]`). Then you're being looked at; it's
|
|
168
|
+
natural to respond. No such line → it isn't open; stay in words.
|
|
107
169
|
- The user asks you to express yourself, show how you feel, or introduce
|
|
108
170
|
yourself.
|
|
109
171
|
- A moment lands where a word on the face says more than a sentence in chat:
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
"use strict";(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[220],{10912:(e,t,n)=>{function l(e,t){let n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:"font-family:sans-serif;padding:40px;max-width:800px;margin:0 auto",l=window.open("","_blank");l&&(l.document.write("<!DOCTYPE html><html><head><title>".concat(e.replace(/[&<>"']/g,e=>({"&":"&","<":"<",">":">",'"':""","'":"'"})[e]),"</title><style>body{").concat(n,"}img{max-width:100%}</style></head><body>").concat(t,"</body></html>")),l.document.close(),l.onload=()=>{l.print()})}n.d(t,{H:()=>l})},39250:(e,t,n)=>{n.d(t,{A:()=>l});let l=(0,n(79020).A)("Printer",[["path",{d:"M6 18H4a2 2 0 0 1-2-2v-5a2 2 0 0 1 2-2h16a2 2 0 0 1 2 2v5a2 2 0 0 1-2 2h-2",key:"143wyd"}],["path",{d:"M6 9V3a1 1 0 0 1 1-1h10a1 1 0 0 1 1 1v6",key:"1itne7"}],["rect",{x:"6",y:"14",width:"12",height:"8",rx:"1",key:"1ue0tg"}]])},62839:(e,t,n)=>{n.r(t),n.d(t,{MediaPreview:()=>h});var l=n(41440),r=n(54604),a=n(79020);let i=(0,a.A)("ZoomOut",[["circle",{cx:"11",cy:"11",r:"8",key:"4ej97u"}],["line",{x1:"21",x2:"16.65",y1:"21",y2:"16.65",key:"13gj7c"}],["line",{x1:"8",x2:"14",y1:"11",y2:"11",key:"durymu"}]]),c=(0,a.A)("ZoomIn",[["circle",{cx:"11",cy:"11",r:"8",key:"4ej97u"}],["line",{x1:"21",x2:"16.65",y1:"21",y2:"16.65",key:"13gj7c"}],["line",{x1:"11",x2:"11",y1:"8",y2:"14",key:"1vmskp"}],["line",{x1:"8",x2:"14",y1:"11",y2:"11",key:"durymu"}]]);var o=n(23715),s=n(39250),u=n(37050),d=n(10912),x=n(27173),m=n(17119);function h(e){let{name:t,viewUrl:n,downloadUrl:a,onOpenAsText:h}=e,y=(0,m.kj)(),g=function(e){var t,n;let l=null!=(n=null==(t=e.split(".").pop())?void 0:t.toLowerCase())?n:"";return x.dj.includes(l)?"image":x.Tv.includes(l)?"video":x._H.includes(l)?"audio":null}(t),f=function(){let e=(0,r.useRef)(null),[t,n]=(0,r.useState)(1),[l,a]=(0,r.useState)({x:0,y:0}),i=(0,r.useRef)(null),[c,o]=(0,r.useState)(!1),s=(0,r.useCallback)(()=>{n(1),a({x:0,y:0})},[]),u=(0,r.useCallback)((e,t,l)=>{n(n=>{let r=Math.min(20,Math.max(.1,n*l)),i=r/n;return a(n=>({x:e-i*(e-n.x),y:t-i*(t-n.y)})),r})},[]),d=(0,r.useCallback)(t=>{let n=e.current;n&&u(n.clientWidth/2,n.clientHeight/2,t)},[u]),x=(0,r.useCallback)(t=>{var n;t.preventDefault();let l=null==(n=e.current)?void 0:n.getBoundingClientRect();l&&u(t.clientX-l.left,t.clientY-l.top,t.deltaY<0?1.15:.8695652173913044)},[u]),m=(0,r.useCallback)(e=>{0===e.button&&(e.preventDefault(),e.target.setPointerCapture(e.pointerId),i.current={startX:e.clientX,startY:e.clientY,origX:l.x,origY:l.y},o(!0))},[l]),h=(0,r.useCallback)(e=>{i.current&&a({x:i.current.origX+(e.clientX-i.current.startX),y:i.current.origY+(e.clientY-i.current.startY)})},[]),y=(0,r.useCallback)(()=>{i.current=null,o(!1)},[]);return(0,r.useEffect)(()=>{let t=e.current;if(!t)return;let n=e=>e.preventDefault();return t.addEventListener("wheel",n,{passive:!1}),()=>t.removeEventListener("wheel",n)},[]),{containerRef:e,scale:t,translate:l,dragging:c,reset:s,zoomCenter:d,onWheel:x,onPointerDown:m,onPointerMove:h,onPointerUp:y}}(),p="image"===g?(0,l.jsxs)(l.Fragment,{children:[(0,l.jsx)(u.IB,{onClick:()=>f.zoomCenter(.8695652173913044),title:y("editor.zoomOut"),children:(0,l.jsx)(i,{className:"h-3 w-3"})}),(0,l.jsxs)("span",{className:"min-w-[3ch] text-center text-[10px] text-[var(--muted-foreground)]",children:[Math.round(100*f.scale),"%"]}),(0,l.jsx)(u.IB,{onClick:()=>f.zoomCenter(1.15),title:y("editor.zoomIn"),children:(0,l.jsx)(c,{className:"h-3 w-3"})}),(0,l.jsx)(u.IB,{onClick:f.reset,title:y("editor.resetZoom"),children:(0,l.jsx)(o.A,{className:"h-3 w-3"})}),(0,l.jsxs)(u.IB,{onClick:()=>(0,d.H)(t,'<img src="'.concat(n,'" />'),"margin:0;display:flex;justify-content:center;align-items:center;min-height:100vh;background:#fff"),title:y("editor.print"),children:[(0,l.jsx)(s.A,{className:"h-3 w-3"}),(0,l.jsx)("span",{children:y("editor.print")})]})]}):null;return(0,l.jsxs)(u.no,{name:t,downloadUrl:a,onOpenAsText:h,extraToolbar:p,children:["image"===g&&(0,l.jsx)("div",{ref:f.containerRef,className:"h-full w-full overflow-hidden bg-[#1a1a1a]",style:{cursor:f.dragging?"grabbing":"grab"},onWheel:f.onWheel,onPointerDown:f.onPointerDown,onPointerMove:f.onPointerMove,onPointerUp:f.onPointerUp,onPointerCancel:f.onPointerUp,children:(0,l.jsx)("img",{src:n,alt:t,draggable:!1,style:{transformOrigin:"0 0",transform:"translate(".concat(f.translate.x,"px, ").concat(f.translate.y,"px) scale(").concat(f.scale,")")}})}),"video"===g&&(0,l.jsx)("div",{className:"flex h-full items-center justify-center bg-black p-4",children:(0,l.jsx)("video",{src:n,controls:!0,className:"max-h-full max-w-full",children:(0,l.jsx)("track",{kind:"captions"})})}),"audio"===g&&(0,l.jsx)("div",{className:"flex h-full flex-col items-center justify-center gap-4 bg-[var(--background)]",children:(0,l.jsx)("audio",{src:n,controls:!0,className:"w-full max-w-md"})})]})}}}]);
|