mlx-bun 0.5.0 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1449 -109
- package/THIRD_PARTY_NOTICES.md +882 -0
- package/bin/mlx-bun.convert.mjs +4 -0
- package/bin/mlx-bun.fuse.mjs +4 -0
- package/bin/mlx-bun.generate.mjs +4 -0
- package/bin/mlx-bun.lora.mjs +4 -0
- package/bin/mlx-bun.mjs +13 -21
- package/bin/mlx-bun.server.mjs +4 -0
- package/bin/mlx-bun.upload.mjs +4 -0
- package/dist/web/app.js +9584 -0
- package/package.json +50 -53
- package/src/cli/ane-install.ts +76 -0
- package/src/cli/args.ts +179 -0
- package/src/cli/engine-entry.ts +68 -0
- package/src/cli/inference.ts +214 -0
- package/src/cli/job-entry.ts +97 -0
- package/src/cli/library-host.ts +117 -0
- package/src/cli/main.ts +57 -0
- package/src/cli/memory-engine.ts +163 -0
- package/src/cli/memory.ts +36 -0
- package/src/cli/mlx-lm-aliases.ts +309 -0
- package/src/cli/model-choice.ts +33 -0
- package/src/cli/model-selection.ts +84 -0
- package/src/cli/module-host.ts +11 -0
- package/src/cli/module-verbs.ts +50 -0
- package/src/cli/numerical-policy.ts +12 -0
- package/src/cli/perplexity.ts +89 -0
- package/src/cli/serve-host.ts +423 -0
- package/src/cli/serve-isolated.ts +217 -0
- package/src/cli/serve-options.ts +106 -0
- package/src/cli/serve-state.ts +205 -0
- package/src/cli/serve.ts +500 -0
- package/src/cli/served-model-host.ts +102 -0
- package/src/cli/server-entry.ts +148 -0
- package/src/cli/serving-unit.ts +177 -0
- package/src/{tui.ts → cli/terminal.ts} +13 -8
- package/src/cli/worker-adapters.ts +34 -0
- package/src/cli/worker-entry.ts +272 -0
- package/src/cli/worker-unit.ts +179 -0
- package/src/engine/adapter-operation.ts +26 -0
- package/src/engine/cache-services.ts +114 -0
- package/src/engine/completion.ts +39 -0
- package/src/engine/generation-gateway.ts +358 -0
- package/src/engine/index.ts +59 -0
- package/src/engine/kv-budget.ts +120 -0
- package/src/engine/media-preparation.ts +232 -0
- package/src/engine/model-binding.ts +24 -0
- package/src/engine/model-host.ts +513 -0
- package/src/engine/model-serving.ts +89 -0
- package/src/{serve → engine}/preparation.ts +7 -5
- package/src/{serve → engine}/session-completion-engine.ts +6 -6
- package/src/engine/telemetry.ts +109 -0
- package/src/hub/downloads.ts +116 -0
- package/src/jobs/db.ts +16 -7
- package/src/jobs/events.ts +29 -0
- package/src/jobs/executable.ts +5 -0
- package/src/jobs/host.ts +51 -0
- package/src/jobs/in-process.ts +55 -0
- package/src/{contracts/jobs.ts → jobs/protocol.ts} +7 -9
- package/src/jobs/runner.ts +150 -231
- package/src/jobs/service.ts +75 -0
- package/src/jobs/sse.ts +59 -23
- package/src/jobs/worker-memory.ts +46 -0
- package/src/jobs/worker-process.ts +163 -0
- package/src/jobs/worker-supervisor.ts +258 -0
- package/src/modules.ts +58 -0
- package/src/publishing/catalog-hub.ts +18 -0
- package/src/publishing/credentials.ts +31 -0
- package/src/publishing/upload.ts +37 -0
- package/src/residency/leased-adapters.ts +21 -0
- package/src/residency/local-records.ts +16 -0
- package/src/residency/model-residency.ts +405 -0
- package/src/residency/record-index.ts +28 -0
- package/src/residency/resident-estimate.ts +50 -0
- package/src/{anthropic.ts → server/anthropic.ts} +3 -81
- package/src/server/cache-routes.ts +36 -0
- package/src/{serve → server}/chat-request.ts +10 -10
- package/src/{serve → server}/chat-stage.ts +14 -14
- package/src/{client.ts → server/client.ts} +28 -2
- package/src/{serve → server}/completion-executor.ts +33 -41
- package/src/server/decision-body.ts +55 -0
- package/src/server/decision-routes.ts +134 -0
- package/src/server/decision-wire.ts +228 -0
- package/src/{serve → server}/discovery-routes.ts +25 -82
- package/src/{serve → server}/http.ts +25 -13
- package/src/{serve → server}/inference-request.ts +5 -5
- package/src/server/job-routes.ts +20 -0
- package/src/server/media-prompt.ts +63 -0
- package/src/server/memory-completion-client.ts +46 -0
- package/src/server/model-routes.ts +192 -0
- package/src/{serve → server}/openai-wire.ts +3 -2
- package/src/{serve → server}/pipeline.ts +1 -1
- package/src/server/prompt-builder.ts +13 -0
- package/src/server/prompt-contracts.ts +17 -0
- package/src/server/proxy-routes.ts +187 -0
- package/src/server/publishing-routes.ts +41 -0
- package/src/{serve → server}/request-plan.ts +6 -8
- package/src/{serve → server}/request-prep.ts +47 -50
- package/src/{serve → server}/responses-client.ts +30 -13
- package/src/{responses.ts → server/responses.ts} +12 -93
- package/src/server/routes.ts +205 -0
- package/src/server/start.ts +72 -0
- package/src/server/status-routes.ts +108 -0
- package/src/{serve → server}/text-completion-stage.ts +25 -4
- package/src/server/text-prompt.ts +12 -0
- package/src/{serve → server}/token-streams.ts +36 -38
- package/src/server/worker-routes.ts +311 -0
- package/src/storage/paths.ts +61 -0
- package/src/web/assets.ts +37 -0
- package/src/web/{src → browser}/api.ts +5 -23
- package/src/web/browser/chat-host.ts +17 -0
- package/src/web/browser/installed-panels.ts +6 -0
- package/src/web/browser/main.ts +55 -0
- package/src/web/browser/markdown.ts +279 -0
- package/src/web/browser/memory-panel.ts +9 -0
- package/src/web/browser/palette.ts +64 -0
- package/src/web/browser/protocol.ts +13 -0
- package/src/web/browser/shell.ts +467 -0
- package/src/web/{src → browser}/status.ts +32 -36
- package/src/web/build.ts +51 -0
- package/src/web/public/app.html +1054 -0
- package/src/web/{sw.js → public/sw.js} +4 -3
- package/src/web/{vendor → public/vendor}/hljs-theme.css +2 -2
- package/src/web/{vendor → public/vendor}/hljs.js +2 -2
- package/THIRD_PARTY_LICENSES.md +0 -51
- package/docs/README.md +0 -107
- package/docs/archive/hlg-sampling.md +0 -350
- package/docs/archive/investigations/audio-input-plan.md +0 -356
- package/docs/archive/investigations/benchmark-ledger-through-v0.4.0.md +0 -3617
- package/docs/archive/investigations/chunk-finetune-experiment.md +0 -84
- package/docs/archive/investigations/colibri-glm52-port.md +0 -1486
- package/docs/archive/investigations/colibri-oracle-pin.md +0 -1284
- package/docs/archive/investigations/curve-bisector-routing.md +0 -123
- package/docs/archive/investigations/decode-roofline-lookagain.md +0 -348
- package/docs/archive/investigations/diffusion-gemma-port.md +0 -602
- package/docs/archive/investigations/dspark-handoff.md +0 -224
- package/docs/archive/investigations/expert-offload-single-user-moe.md +0 -292
- package/docs/archive/investigations/faithful-l1-consolidation.md +0 -310
- package/docs/archive/investigations/hlg-sampling-investigation.md +0 -970
- package/docs/archive/investigations/indexshare-performance-spike.md +0 -357
- package/docs/archive/investigations/kernel-perf-review-2026-07.md +0 -171
- package/docs/archive/investigations/lab-build-journal.md +0 -157
- package/docs/archive/investigations/minicpm5-decode-megakernel.md +0 -489
- package/docs/archive/investigations/openjarvis-fit.md +0 -198
- package/docs/archive/investigations/openwiki-evaluation.md +0 -249
- package/docs/archive/investigations/optimization_plan.md +0 -346
- package/docs/archive/investigations/orpo-base-uf-experiment-and-directions.md +0 -120
- package/docs/archive/investigations/orpo-flash-cce-pin-leak.md +0 -167
- package/docs/archive/investigations/orpo-uf-testing-handoff.md +0 -126
- package/docs/archive/investigations/pareto-specialized-runtime-findings.md +0 -92
- package/docs/archive/investigations/pi-builtin-investigation.md +0 -308
- package/docs/archive/investigations/pre-colibri-stabilization.md +0 -329
- package/docs/archive/investigations/qwen38-27b-campaign-log.md +0 -1092
- package/docs/archive/investigations/segmented-backward-handoff.md +0 -148
- package/docs/archive/investigations/starter-model-port-handoff.md +0 -342
- package/docs/archive/investigations/steel-flash-cce-handoff.md +0 -489
- package/docs/archive/investigations/the-dreaming-handoff.md +0 -141
- package/docs/archive/investigations/trainer-validation-experiment.md +0 -173
- package/docs/archive/planning/chat-ui-vision-memory-first.md +0 -199
- package/docs/archive/planning/chat-ui-vision.md +0 -50
- package/docs/archive/planning/chunk-task-roadmap.md +0 -77
- package/docs/archive/planning/curve-sampler-research-plan.md +0 -331
- package/docs/archive/planning/journal.md +0 -316
- package/docs/archive/planning/memory-docs-and-dag-plan.md +0 -194
- package/docs/archive/planning/pi-integration-review.md +0 -178
- package/docs/archive/planning/release-notes-v0.0.10.md +0 -57
- package/docs/archive/planning/release-notes-v0.0.11.md +0 -239
- package/docs/archive/planning/release-notes-v0.0.12.md +0 -80
- package/docs/archive/planning/release-notes-v0.0.13.md +0 -77
- package/docs/archive/planning/release-notes-v0.0.9.md +0 -122
- package/docs/archive/planning/release-notes-v0.2.0.md +0 -49
- package/docs/archive/planning/release-notes-v0.3.0.md +0 -35
- package/docs/archive/planning/release-notes-v0.4.0.md +0 -45
- package/docs/archive/planning/release-notes-v0.5.0.md +0 -37
- package/docs/archive/planning/web-ui-pass-plan.md +0 -213
- package/docs/archive/planning/website-readme-pass-plan.md +0 -224
- package/docs/design/batching.md +0 -2054
- package/docs/design/decode-speed-program.md +0 -2404
- package/docs/design/dreaming-nightly-pipeline.md +0 -684
- package/docs/design/generic-model-support.md +0 -821
- package/docs/design/kv-cache.md +0 -993
- package/docs/design/orpo-training.md +0 -680
- package/docs/design/speculative-decoding.md +0 -2129
- package/docs/design/turboquant.md +0 -2225
- package/docs/design/unified-engine-frontier-plan.md +0 -1987
- package/docs/design/web-chat-redesign.md +0 -695
- package/docs/planning/PRODUCT_ROADMAP.md +0 -593
- package/docs/planning/ResearchTopics.md +0 -119
- package/docs/planning/public-readiness-handoff.md +0 -244
- package/docs/planning/public-readiness.md +0 -149
- package/docs/planning/video-series.md +0 -421
- package/docs/reference/benchmarks.md +0 -3587
- package/docs/reference/cli.md +0 -646
- package/docs/reference/distribution.md +0 -380
- package/docs/reference/environment.md +0 -494
- package/docs/reference/glossary.md +0 -39
- package/docs/reference/library-api.md +0 -688
- package/docs/reference/memory.md +0 -260
- package/docs/reference/models.md +0 -346
- package/docs/reference/server-api.md +0 -1945
- package/docs/reference/server-config.md +0 -973
- package/docs/reference/training.md +0 -462
- package/docs/reference/troubleshooting.md +0 -158
- package/src/admission-plan.ts +0 -18
- package/src/audio/audiotoolbox.ts +0 -112
- package/src/audio/conformer.ts +0 -655
- package/src/audio/decode.ts +0 -133
- package/src/audio/features.ts +0 -202
- package/src/audio/mic-capture.ts +0 -98
- package/src/audio/silero-vad.ts +0 -379
- package/src/audio/transcode.ts +0 -67
- package/src/audio/whisper-decode.ts +0 -774
- package/src/audio/whisper-fast.ts +0 -732
- package/src/audio/whisper-format.ts +0 -73
- package/src/audio/whisper-mel.ts +0 -185
- package/src/audio/whisper-timing.ts +0 -274
- package/src/audio/whisper-tokenizer.ts +0 -239
- package/src/audio/whisper-transcribe.ts +0 -410
- package/src/backends/mlx/assistant-target.ts +0 -46
- package/src/backends/mlx/autoregressive-draft-rows.ts +0 -120
- package/src/backends/mlx/autoregressive.ts +0 -129
- package/src/backends/mlx/batch-group.ts +0 -1874
- package/src/backends/mlx/cache-attachment.ts +0 -67
- package/src/backends/mlx/cache-layout.ts +0 -31
- package/src/backends/mlx/checkpoint-state.ts +0 -53
- package/src/backends/mlx/continuation-persistence.ts +0 -107
- package/src/backends/mlx/continuation-request.ts +0 -43
- package/src/backends/mlx/continuation.ts +0 -77
- package/src/backends/mlx/diffusion.ts +0 -39
- package/src/backends/mlx/draft-target.ts +0 -32
- package/src/backends/mlx/encoder-cache.ts +0 -25
- package/src/backends/mlx/evaluation.ts +0 -105
- package/src/backends/mlx/fill-append.ts +0 -58
- package/src/backends/mlx/fill-group.ts +0 -497
- package/src/backends/mlx/full-kv-row-append.ts +0 -32
- package/src/backends/mlx/full-kv-row-donor.ts +0 -54
- package/src/backends/mlx/full-transitioning-kv-rows.ts +0 -65
- package/src/backends/mlx/gateway-binding.ts +0 -174
- package/src/backends/mlx/graph.ts +0 -38
- package/src/backends/mlx/kv-codec.ts +0 -9
- package/src/backends/mlx/kv-maintenance.ts +0 -161
- package/src/backends/mlx/kv-tensor-rows.ts +0 -212
- package/src/backends/mlx/memory-client.ts +0 -189
- package/src/backends/mlx/methods.ts +0 -90
- package/src/backends/mlx/mixed-iteration.ts +0 -62
- package/src/backends/mlx/model-prompt.ts +0 -178
- package/src/backends/mlx/model-serving.ts +0 -80
- package/src/backends/mlx/paged-cache-codec.ts +0 -27
- package/src/backends/mlx/prefill-cohort.ts +0 -100
- package/src/backends/mlx/prefill-policy.ts +0 -26
- package/src/backends/mlx/prefill-rows.ts +0 -244
- package/src/backends/mlx/prefill.ts +0 -33
- package/src/backends/mlx/projected-context-rows.ts +0 -72
- package/src/backends/mlx/prompt-input.ts +0 -29
- package/src/backends/mlx/qwen-prompt-input.ts +0 -40
- package/src/backends/mlx/request-policy.ts +0 -20
- package/src/backends/mlx/request-state-policy.ts +0 -54
- package/src/backends/mlx/rollback.ts +0 -92
- package/src/backends/mlx/row-sampling.ts +0 -49
- package/src/backends/mlx/serial-executor.ts +0 -251
- package/src/backends/mlx/speculative-group.ts +0 -438
- package/src/backends/mlx/speculative-round.ts +0 -161
- package/src/backends/mlx/speculative.ts +0 -131
- package/src/backends/mlx/state-rows.ts +0 -104
- package/src/backends/mlx/state-views.ts +0 -46
- package/src/backends/mlx/target-layout-capability.ts +0 -35
- package/src/backends/mlx/text-engine.ts +0 -73
- package/src/backends/mlx/transitioning-kv-rows.ts +0 -173
- package/src/chat-template.ts +0 -382
- package/src/cli.ts +0 -3490
- package/src/config.ts +0 -399
- package/src/contracts/completion.ts +0 -16
- package/src/contracts/execution.ts +0 -70
- package/src/contracts/generation.ts +0 -103
- package/src/contracts/host.ts +0 -7
- package/src/contracts/object-cache.ts +0 -8
- package/src/contracts/pi-provider.ts +0 -30
- package/src/contracts/pi.ts +0 -261
- package/src/contracts/prefix-cache.ts +0 -33
- package/src/contracts/resources.ts +0 -25
- package/src/contracts/sampling.ts +0 -23
- package/src/contracts/scheduling.ts +0 -54
- package/src/contracts/tsconfig.json +0 -14
- package/src/dataset/generators.ts +0 -1087
- package/src/dataset/index.ts +0 -12
- package/src/dataset/job.ts +0 -81
- package/src/dataset/llm.ts +0 -71
- package/src/dataset/registry.ts +0 -528
- package/src/diffusion/diffusion-generate.ts +0 -507
- package/src/download.ts +0 -505
- package/src/embed.ts +0 -77
- package/src/engine/admission.ts +0 -59
- package/src/engine/cancellation.ts +0 -38
- package/src/engine/engine.ts +0 -56
- package/src/engine/execution-coordinator.ts +0 -70
- package/src/engine/execution-plan.ts +0 -64
- package/src/engine/execution-tasks.ts +0 -44
- package/src/engine/namespaced-cache.ts +0 -13
- package/src/engine/resources.ts +0 -53
- package/src/engine/scheduler.ts +0 -92
- package/src/engine/session.ts +0 -247
- package/src/eval/README.md +0 -130
- package/src/eval/ifeval.ts +0 -81
- package/src/eval/kl-prompts.ts +0 -29
- package/src/eval/kl.ts +0 -590
- package/src/eval/perplexity.ts +0 -164
- package/src/eval/quality-db.ts +0 -92
- package/src/eval/reference-logits.ts +0 -207
- package/src/eval/runner.ts +0 -124
- package/src/eval/score.ts +0 -49
- package/src/eval/tasks/bfcl.ts +0 -535
- package/src/eval/tasks/gsm8k.ts +0 -98
- package/src/eval/tasks/hashhop.ts +0 -139
- package/src/eval/tasks/humaneval.ts +0 -275
- package/src/eval/tasks/ifeval.ts +0 -525
- package/src/eval/tasks/mmlu.ts +0 -157
- package/src/eval/ultrafeedback.ts +0 -60
- package/src/evaldb.ts +0 -199
- package/src/expert-io.ts +0 -393
- package/src/expert-offload-build.ts +0 -77
- package/src/expert-offload.ts +0 -76
- package/src/expert-residency.ts +0 -1134
- package/src/expert-usage.ts +0 -415
- package/src/fill/echo-index.ts +0 -188
- package/src/fill/fill-session.ts +0 -463
- package/src/fill/proposal.ts +0 -68
- package/src/fill/schema-rows.ts +0 -506
- package/src/fill/tool-boundary.ts +0 -103
- package/src/fit.ts +0 -249
- package/src/generate.ts +0 -1352
- package/src/grammar.ts +0 -604
- package/src/harness-pi.ts +0 -184
- package/src/hf-push.ts +0 -628
- package/src/hub-rest.ts +0 -242
- package/src/index.ts +0 -63
- package/src/inference/denoising.ts +0 -17
- package/src/inference/draft-acceptance.ts +0 -41
- package/src/inference/draft-output.ts +0 -49
- package/src/inference/graph.ts +0 -33
- package/src/inference/prefill.ts +0 -45
- package/src/inference/rollback.ts +0 -7
- package/src/jobs/index.ts +0 -31
- package/src/jobs/job-entry.ts +0 -107
- package/src/jobs/task-client.ts +0 -19
- package/src/jobs/types.ts +0 -2
- package/src/kv-scheme.ts +0 -279
- package/src/kv-store.ts +0 -1135
- package/src/lab/README.md +0 -22
- package/src/lab/curve/curve-designer.html +0 -303
- package/src/lab/curve/curve-sampler.ts +0 -122
- package/src/lab/expert-trace/expert-trace.ts +0 -69
- package/src/lab/paged-kv/paged-attention.ts +0 -117
- package/src/lab/paged-kv/paged-kv-rows.ts +0 -111
- package/src/lab/paged-kv/paged-kv.ts +0 -336
- package/src/library.ts +0 -40
- package/src/lora.ts +0 -408
- package/src/md-modules.d.ts +0 -54
- package/src/media-fetch.ts +0 -442
- package/src/memory/article.ts +0 -558
- package/src/memory/chunk-validation.ts +0 -150
- package/src/memory/chunk.ts +0 -318
- package/src/memory/cluster.ts +0 -351
- package/src/memory/coverage.ts +0 -134
- package/src/memory/crosslink.ts +0 -0
- package/src/memory/db.ts +0 -253
- package/src/memory/entity.ts +0 -203
- package/src/memory/gate.ts +0 -187
- package/src/memory/history.ts +0 -152
- package/src/memory/ingest.ts +0 -335
- package/src/memory/model.ts +0 -183
- package/src/memory/normalize.ts +0 -671
- package/src/memory/parse.ts +0 -22
- package/src/memory/pipeline.ts +0 -275
- package/src/memory/prompts.ts +0 -65
- package/src/memory/query.ts +0 -334
- package/src/memory/reconcile.ts +0 -734
- package/src/memory/redirect.ts +0 -108
- package/src/memory/reindex.ts +0 -325
- package/src/memory/resolve.ts +0 -307
- package/src/memory/rest.ts +0 -245
- package/src/memory/route.ts +0 -272
- package/src/memory/schedule.ts +0 -165
- package/src/memory/stages.ts +0 -777
- package/src/memory/synthesize.ts +0 -1638
- package/src/memory/tools.ts +0 -749
- package/src/memory/vault.ts +0 -1194
- package/src/memory/wikify.ts +0 -1064
- package/src/mlx/array.ts +0 -425
- package/src/mlx/autograd.ts +0 -304
- package/src/mlx/checkpoint.ts +0 -149
- package/src/mlx/compile.ts +0 -168
- package/src/mlx/custom-vjp.ts +0 -188
- package/src/mlx/ffi.ts +0 -531
- package/src/mlx/materialize.ts +0 -23
- package/src/mlx/metal-kernel.ts +0 -159
- package/src/mlx/normalized-argmax.ts +0 -116
- package/src/mlx/ops.ts +0 -1108
- package/src/mlx/quantized-rows.ts +0 -36
- package/src/mlx/token-bitmask.ts +0 -52
- package/src/mlx/turboquant-kv-decode.ts +0 -82
- package/src/mlx/turboquant-kv-inverse.ts +0 -122
- package/src/mlx/turboquant-ops.ts +0 -861
- package/src/mlx/turboquant-tables.ts +0 -237
- package/src/mmap.ts +0 -90
- package/src/model/artifact-identity.ts +0 -73
- package/src/model/batched-kv.ts +0 -235
- package/src/model/batched-mask.ts +0 -279
- package/src/model/batched-quant.ts +0 -212
- package/src/model/batched-quantized-kv.ts +0 -144
- package/src/model/batched-rotating-quant.ts +0 -353
- package/src/model/batched-rotating-state.ts +0 -149
- package/src/model/batched-rotating.ts +0 -338
- package/src/model/batched-row-storage.ts +0 -179
- package/src/model/batched-ssm.ts +0 -52
- package/src/model/batched-turboquant-kv.ts +0 -94
- package/src/model/compiled-decode.ts +0 -712
- package/src/model/decoded-kv-donor.ts +0 -12
- package/src/model/delayed-quantized-kv.ts +0 -72
- package/src/model/delayed-rotating-quantized-kv.ts +0 -93
- package/src/model/delayed-turboquant-kv.ts +0 -57
- package/src/model/diffusion-gemma.ts +0 -1026
- package/src/model/factory.ts +0 -215
- package/src/model/fingerprint.ts +0 -45
- package/src/model/flash-attention.ts +0 -694
- package/src/model/full-prefill-padding.ts +0 -56
- package/src/model/full-prefill-row.ts +0 -105
- package/src/model/gemma4-base.ts +0 -2370
- package/src/model/gemma4.ts +0 -1395
- package/src/model/generated/gemma4-12b.ts +0 -429
- package/src/model/generated/gemma4-26b.ts +0 -448
- package/src/model/generated/gemma4-e4b.ts +0 -717
- package/src/model/generated/index.ts +0 -18
- package/src/model/glm52-atlas.ts +0 -511
- package/src/model/glm52-cache.ts +0 -792
- package/src/model/glm52-config.ts +0 -262
- package/src/model/glm52-container.ts +0 -305
- package/src/model/glm52-coupling.ts +0 -388
- package/src/model/glm52-dsa.ts +0 -606
- package/src/model/glm52-expert-layout.ts +0 -253
- package/src/model/glm52-layout.ts +0 -152
- package/src/model/glm52-memory.ts +0 -500
- package/src/model/glm52-mla.ts +0 -792
- package/src/model/glm52-moe.ts +0 -223
- package/src/model/glm52-pilot.ts +0 -537
- package/src/model/glm52-reference.ts +0 -445
- package/src/model/glm52-residency.ts +0 -557
- package/src/model/glm52-resident-weights.ts +0 -135
- package/src/model/glm52-streamed-experts.ts +0 -453
- package/src/model/glm52-streamed-metal.ts +0 -610
- package/src/model/glm52-weights.ts +0 -503
- package/src/model/glm52.ts +0 -1132
- package/src/model/implementation.ts +0 -69
- package/src/model/kv-attention-view.ts +0 -70
- package/src/model/minicpm5.ts +0 -376
- package/src/model/profile.ts +0 -468
- package/src/model/quantized-append-attention.ts +0 -31
- package/src/model/qwen-append.ts +0 -12
- package/src/model/qwen-conv.ts +0 -52
- package/src/model/qwen3-delta-state.ts +0 -74
- package/src/model/qwen3-delta.ts +0 -451
- package/src/model/qwen3-moe.ts +0 -404
- package/src/model/qwen3-mrope.ts +0 -250
- package/src/model/qwen3.ts +0 -277
- package/src/model/qwen3_5-checkpoint.ts +0 -164
- package/src/model/qwen3_5.ts +0 -881
- package/src/model/rotating-kv-donor.ts +0 -35
- package/src/model/rotating-kv-layout.ts +0 -262
- package/src/model/rotating-row-transaction.ts +0 -35
- package/src/model/speculative-rotating-kv.ts +0 -101
- package/src/model/ssm-prefill-padding.ts +0 -39
- package/src/model/support.ts +0 -110
- package/src/model/token-groups.ts +0 -66
- package/src/model/trellis-balanced-scatter.ts +0 -51
- package/src/model/trellis-linear.ts +0 -672
- package/src/model/trellis-shared-m.ts +0 -151
- package/src/model/trellis-shared-scatter.ts +0 -144
- package/src/model/trellis-splitk-prefill.ts +0 -158
- package/src/model/trellis-tiled-prefill.ts +0 -136
- package/src/model/trellis-vector-expand.ts +0 -62
- package/src/model/trellis-wide-prefill.ts +0 -94
- package/src/model/turboquant-codec.ts +0 -89
- package/src/model/universal/archs.ts +0 -332
- package/src/model/universal/dense.ts +0 -612
- package/src/model/universal/modules.ts +0 -170
- package/src/model/universal/rope.ts +0 -381
- package/src/model/whisper.ts +0 -435
- package/src/native/expert_io.c +0 -523
- package/src/native/frame_extract.swift +0 -140
- package/src/native/mic_capture.swift +0 -92
- package/src/native-pack.ts +0 -163
- package/src/pi-provider.ts +0 -53
- package/src/pi-session.ts +0 -81
- package/src/pi-terminal.ts +0 -274
- package/src/pi-web.ts +0 -1940
- package/src/preflight.ts +0 -158
- package/src/prompt-cache.ts +0 -682
- package/src/quantize/allocator.ts +0 -627
- package/src/quantize/atomic-output.ts +0 -38
- package/src/quantize/calibration.ts +0 -181
- package/src/quantize/config-writer.ts +0 -145
- package/src/quantize/index.ts +0 -84
- package/src/quantize/job.ts +0 -162
- package/src/quantize/quantizer.ts +0 -666
- package/src/quantize/rotate.ts +0 -603
- package/src/quantize/safetensors-writer.ts +0 -249
- package/src/quantize/sensitivity.ts +0 -216
- package/src/quantize/trellis.ts +0 -586
- package/src/quantize/weight-transform.ts +0 -292
- package/src/registry.ts +0 -602
- package/src/runtime-config.ts +0 -77
- package/src/safetensors.ts +0 -165
- package/src/sampler.ts +0 -1012
- package/src/serve/admin-routes.ts +0 -184
- package/src/serve/assets/curve-terrain.html +0 -105
- package/src/serve/assets/training-inference-map.html +0 -575
- package/src/serve/audio-routes.ts +0 -323
- package/src/serve/aux-routes.ts +0 -132
- package/src/serve/batch-scheduler.ts +0 -8
- package/src/serve/checkpoint-identity.ts +0 -64
- package/src/serve/generation-gateway.ts +0 -537
- package/src/serve/isolate.ts +0 -609
- package/src/serve/kv-budget.ts +0 -14
- package/src/serve/lab-routes.ts +0 -341
- package/src/serve/lane-registry.ts +0 -45
- package/src/serve/model-admin-routes.ts +0 -155
- package/src/serve/model-binding.ts +0 -49
- package/src/serve/model-host.ts +0 -511
- package/src/serve/parent-application.ts +0 -56
- package/src/serve/prompt-response-trace.ts +0 -161
- package/src/serve/response-lease.ts +0 -24
- package/src/serve/session-search.ts +0 -326
- package/src/serve/static-routes.ts +0 -101
- package/src/serve/transcription-server.ts +0 -64
- package/src/serve/transcription-service.ts +0 -502
- package/src/server.ts +0 -1282
- package/src/spec/assistant-rows.ts +0 -93
- package/src/spec/assistant-source.ts +0 -126
- package/src/spec/cache-identity.ts +0 -22
- package/src/spec/deepspec-rows.ts +0 -29
- package/src/spec/deepspec-source.ts +0 -145
- package/src/spec/dflash-source.ts +0 -149
- package/src/spec/drafter.ts +0 -304
- package/src/spec/dspark/ab-stats.ts +0 -136
- package/src/spec/dspark/calibration.ts +0 -112
- package/src/spec/dspark/data-dflash.ts +0 -156
- package/src/spec/dspark/data.ts +0 -201
- package/src/spec/dspark/deepspec-module.ts +0 -776
- package/src/spec/dspark/generate-dflash.ts +0 -245
- package/src/spec/dspark/generate.ts +0 -290
- package/src/spec/dspark/loader.ts +0 -23
- package/src/spec/dspark/loss.ts +0 -213
- package/src/spec/dspark/module-dflash.ts +0 -560
- package/src/spec/dspark/module.ts +0 -540
- package/src/spec/dspark/quantize-drafter.ts +0 -91
- package/src/spec/dspark/sample.ts +0 -112
- package/src/spec/generate.ts +0 -276
- package/src/spec/glm52-mtp-graph.ts +0 -29
- package/src/spec/glm52-mtp-rows.ts +0 -132
- package/src/spec/glm52-mtp-source.ts +0 -318
- package/src/spec/glm52-mtp-state.ts +0 -33
- package/src/spec/ngram-source.ts +0 -226
- package/src/spec/projected-draft-rows.ts +0 -73
- package/src/spec/qwen-mtp-module.ts +0 -171
- package/src/spec/qwen-mtp-rows.ts +0 -230
- package/src/spec/qwen-mtp-source.ts +0 -386
- package/src/spec/qwen-mtp-state.ts +0 -30
- package/src/spec/serve-loop.ts +0 -587
- package/src/spec/source.ts +0 -228
- package/src/spec/two-model-rows.ts +0 -62
- package/src/spec/two-model.ts +0 -113
- package/src/ssd-cache.ts +0 -431
- package/src/ssd-durability.ts +0 -209
- package/src/storage/host-buffer.ts +0 -24
- package/src/storage/kv-writer.ts +0 -83
- package/src/storage/kv-writer.worker.js +0 -264
- package/src/storage/retention-policy.ts +0 -37
- package/src/tiered-prompt-cache.ts +0 -45
- package/src/tokenizer.ts +0 -335
- package/src/tool-approvals.ts +0 -131
- package/src/tool-call.ts +0 -695
- package/src/train/dataset.ts +0 -484
- package/src/train/diffusion-lora.ts +0 -216
- package/src/train/export.ts +0 -32
- package/src/train/flash-cce.ts +0 -1234
- package/src/train/forward.ts +0 -266
- package/src/train/fuse.ts +0 -243
- package/src/train/index.ts +0 -46
- package/src/train/job.ts +0 -205
- package/src/train/lora-params.ts +0 -254
- package/src/train/loss.ts +0 -1591
- package/src/train/merge.ts +0 -195
- package/src/train/optimizer.ts +0 -183
- package/src/train/prefix-shared.ts +0 -490
- package/src/train/rank.ts +0 -137
- package/src/train/segmented.ts +0 -1828
- package/src/train/steel-qmm-header.ts +0 -1461
- package/src/train/trainer.ts +0 -1221
- package/src/train/watch.ts +0 -542
- package/src/vision/diffusion-vision.ts +0 -620
- package/src/vision/embedder.ts +0 -176
- package/src/vision/preprocess.ts +0 -247
- package/src/vision/prompt.ts +0 -409
- package/src/vision/qwen3vl-preprocess.ts +0 -409
- package/src/vision/qwen3vl-prompt.ts +0 -233
- package/src/vision/qwen3vl-tower.ts +0 -566
- package/src/vision/siglip.ts +0 -607
- package/src/vision/video-frames.ts +0 -148
- package/src/web/app.html +0 -2155
- package/src/web/app.js +0 -5863
- package/src/web/skills/memory/SKILL.md +0 -120
- package/src/web/skills/web-research/SKILL.md +0 -46
- package/src/web/skills.ts +0 -97
- package/src/web/src/adapters-panel.ts +0 -288
- package/src/web/src/assistant.ts +0 -451
- package/src/web/src/chat.ts +0 -1060
- package/src/web/src/composer.ts +0 -962
- package/src/web/src/dataset.ts +0 -132
- package/src/web/src/finetune.ts +0 -267
- package/src/web/src/hub.ts +0 -428
- package/src/web/src/main.ts +0 -98
- package/src/web/src/markdown.ts +0 -0
- package/src/web/src/memory-panel.ts +0 -572
- package/src/web/src/model-picker.ts +0 -159
- package/src/web/src/palette.ts +0 -346
- package/src/web/src/protocol.ts +0 -26
- package/src/web/src/quantize.ts +0 -167
- package/src/web/src/rag.ts +0 -274
- package/src/web/src/sessions.ts +0 -469
- package/src/web/src/shell.ts +0 -804
- package/src/web/src/ui-catalog.ts +0 -78
- package/src/web/src/voice.ts +0 -141
- package/src/web/vendor/README.md +0 -61
- package/src/web-assets.ts +0 -47
- package/src/web-tools.ts +0 -478
- package/src/weights.ts +0 -255
- /package/src/{serve → server}/completion-sink.ts +0 -0
- /package/src/{serve → server}/generated-token-history.ts +0 -0
- /package/src/web/{icon.svg → public/icon.svg} +0 -0
- /package/src/web/{manifest.webmanifest → public/manifest.webmanifest} +0 -0
- /package/src/web/{vendor → public/vendor}/hljs-LICENSE +0 -0
package/src/web/app.html
DELETED
|
@@ -1,2155 +0,0 @@
|
|
|
1
|
-
<!doctype html>
|
|
2
|
-
<html lang="en">
|
|
3
|
-
<head>
|
|
4
|
-
<meta charset="utf-8">
|
|
5
|
-
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
6
|
-
<title>mlx-bun</title>
|
|
7
|
-
<style>
|
|
8
|
-
/* ════════════════════════════════════════════════════════════════════
|
|
9
|
-
mlx-bun · unified app
|
|
10
|
-
One product, five sections (Chat · Quantize · Fine-tune · Dataset · Status).
|
|
11
|
-
Apple keynote grammar: black stage, SF Pro, gradient hero type,
|
|
12
|
-
hairline cards, ambient color blooms, numbers that land.
|
|
13
|
-
Fully self-contained — no external fonts, CDNs, or scripts.
|
|
14
|
-
════════════════════════════════════════════════════════════════════ */
|
|
15
|
-
|
|
16
|
-
/* ─── design tokens (shared across every section) ─── */
|
|
17
|
-
:root{
|
|
18
|
-
--bg:#000;
|
|
19
|
-
--ink:#f5f5f7;
|
|
20
|
-
--dim:#86868b;
|
|
21
|
-
--dimmer:#515154;
|
|
22
|
-
--hairline:rgba(255,255,255,.14);
|
|
23
|
-
--hairline-soft:rgba(255,255,255,.07);
|
|
24
|
-
--hairline-strong:rgba(255,255,255,.28);
|
|
25
|
-
--card:rgba(255,255,255,.045);
|
|
26
|
-
--card-hover:rgba(255,255,255,.08);
|
|
27
|
-
--card-2:rgba(255,255,255,.025);
|
|
28
|
-
--blue:#0a84ff; --cyan:#64d2ff; --indigo:#5e5ce6; --purple:#bf5af2;
|
|
29
|
-
--pink:#ff375f; --orange:#ff9f0a; --yellow:#ffd60a; --green:#30d158;
|
|
30
|
-
--red:#ff453a;
|
|
31
|
-
--ease:cubic-bezier(.22,.61,.21,1);
|
|
32
|
-
--nav-h:60px;
|
|
33
|
-
--mono:"SF Mono",ui-monospace,Menlo,Monaco,monospace;
|
|
34
|
-
/* bloom radial-gradient stop opacities — separate tokens so the light
|
|
35
|
-
theme can dial them down (the gradient identity/positions/colors stay
|
|
36
|
-
identical, only intensity changes — plan §7 "keep the gradient identity") */
|
|
37
|
-
--bloom-a:.13; --bloom-b:.16; --bloom-c:.10;
|
|
38
|
-
--overlay-scrim:rgba(0,0,0,.55);
|
|
39
|
-
--overlay-panel:rgba(22,22,24,.96);
|
|
40
|
-
--popover-panel:rgba(30,30,34,.98);
|
|
41
|
-
/* Code/log/tool-output surfaces (pre blocks, .logbox, edit textarea)
|
|
42
|
-
deliberately stay dark-chrome in BOTH themes — the conventional
|
|
43
|
-
"code block keeps its own dark surface" pattern (GitHub, VS Code
|
|
44
|
-
light themes, etc.), so monospace content keeps consistent contrast
|
|
45
|
-
regardless of the surrounding chat theme.
|
|
46
|
-
These MUST be opaque: the original rgba(0,0,0,.45) composited to
|
|
47
|
-
mid-GRAY over the light theme's white page, washing out the
|
|
48
|
-
near-white code ink + #86868b hljs comments (2026-07-06 visual QA).
|
|
49
|
-
Opaque values render identically over any page background. */
|
|
50
|
-
--code-bg:#121214; --code-bg-soft:#1a1a1d; --code-ink:#f5f5f7;
|
|
51
|
-
--nav-bg:rgba(0,0,0,.62);
|
|
52
|
-
--scrollbar-thumb:rgba(255,255,255,.12); --scrollbar-thumb-hover:rgba(255,255,255,.22);
|
|
53
|
-
}
|
|
54
|
-
/* ─── light theme: tasteful equivalents, same gradient identity ───
|
|
55
|
-
Applied via [data-theme="light"] on <html> (explicit user choice,
|
|
56
|
-
persisted to localStorage) OR by prefers-color-scheme:light when the
|
|
57
|
-
user's choice is "auto" (default — see initTheme()). */
|
|
58
|
-
[data-theme="light"]{
|
|
59
|
-
--bg:#f4f4f6;
|
|
60
|
-
--ink:#1d1d1f;
|
|
61
|
-
--dim:#6e6e73;
|
|
62
|
-
--dimmer:#a1a1a6;
|
|
63
|
-
--hairline:rgba(0,0,0,.12);
|
|
64
|
-
--hairline-soft:rgba(0,0,0,.06);
|
|
65
|
-
--hairline-strong:rgba(0,0,0,.28);
|
|
66
|
-
--card:rgba(0,0,0,.035);
|
|
67
|
-
--card-hover:rgba(0,0,0,.065);
|
|
68
|
-
--card-2:rgba(0,0,0,.02);
|
|
69
|
-
--bloom-a:.09; --bloom-b:.10; --bloom-c:.07;
|
|
70
|
-
--overlay-scrim:rgba(0,0,0,.28);
|
|
71
|
-
--overlay-panel:rgba(255,255,255,.97);
|
|
72
|
-
--popover-panel:rgba(255,255,255,.98);
|
|
73
|
-
--nav-bg:rgba(255,255,255,.72);
|
|
74
|
-
--scrollbar-thumb:rgba(0,0,0,.14); --scrollbar-thumb-hover:rgba(0,0,0,.26);
|
|
75
|
-
}
|
|
76
|
-
*{box-sizing:border-box;margin:0;padding:0}
|
|
77
|
-
html,body{height:100%}
|
|
78
|
-
body{
|
|
79
|
-
background:var(--bg);color:var(--ink);
|
|
80
|
-
font-family:"SF Pro Display","SF Pro Text",-apple-system,BlinkMacSystemFont,"Helvetica Neue",Helvetica,Arial,sans-serif;
|
|
81
|
-
-webkit-font-smoothing:antialiased;
|
|
82
|
-
overflow:hidden; /* app shell owns its own scroll regions */
|
|
83
|
-
}
|
|
84
|
-
::selection{background:rgba(10,132,255,.4)}
|
|
85
|
-
.num{font-variant-numeric:tabular-nums}
|
|
86
|
-
code,.mono{font-family:var(--mono)}
|
|
87
|
-
code{font-size:.86em;background:rgba(127,127,127,.16);padding:2px 8px;border-radius:6px}
|
|
88
|
-
a{color:inherit}
|
|
89
|
-
::-webkit-scrollbar{width:10px;height:10px}
|
|
90
|
-
::-webkit-scrollbar-thumb{background:var(--scrollbar-thumb);border-radius:6px;border:2px solid transparent;background-clip:content-box}
|
|
91
|
-
::-webkit-scrollbar-thumb:hover{background:var(--scrollbar-thumb-hover);background-clip:content-box}
|
|
92
|
-
::-webkit-scrollbar-track{background:transparent}
|
|
93
|
-
|
|
94
|
-
/* gradient text helpers (lifted from status/chat pages) */
|
|
95
|
-
.grad{background-clip:text;-webkit-background-clip:text;color:transparent}
|
|
96
|
-
.g-hero{background-image:linear-gradient(96deg,var(--orange) 0%,var(--pink) 30%,var(--purple) 62%,var(--blue) 100%)}
|
|
97
|
-
.g-blue{background-image:linear-gradient(94deg,var(--cyan),var(--blue) 55%,var(--indigo))}
|
|
98
|
-
.g-green{background-image:linear-gradient(94deg,#a8ff9e,var(--green) 45%,var(--cyan))}
|
|
99
|
-
.g-pink{background-image:linear-gradient(94deg,var(--orange),var(--pink) 55%,var(--purple))}
|
|
100
|
-
.g-purple{background-image:linear-gradient(94deg,var(--purple),var(--indigo) 55%,var(--blue))}
|
|
101
|
-
|
|
102
|
-
/* ─── ambient blooms behind everything ─── */
|
|
103
|
-
.bloom{position:fixed;inset:-25%;pointer-events:none;z-index:0;
|
|
104
|
-
background:
|
|
105
|
-
radial-gradient(55vw 42vh at 12% 6%, rgba(255,159,10,var(--bloom-a)), transparent 68%),
|
|
106
|
-
radial-gradient(60vw 48vh at 88% 24%, rgba(94,92,230,var(--bloom-b)), transparent 68%),
|
|
107
|
-
radial-gradient(50vw 40vh at 30% 96%, rgba(10,132,255,var(--bloom-c)), transparent 68%);
|
|
108
|
-
filter:blur(74px);
|
|
109
|
-
transition:opacity 1.2s var(--ease);
|
|
110
|
-
}
|
|
111
|
-
/* Ambient bloom is a slow continuous drift, gated behind
|
|
112
|
-
prefers-reduced-motion (plan §7): with no explicit opacity set inline
|
|
113
|
-
by the route dimmer, it just sits static — no keyframe loop to begin
|
|
114
|
-
with, so the only "animation" to guard is the opacity transition
|
|
115
|
-
above, which is a one-shot per-route dim, not a decorative loop. The
|
|
116
|
-
shimmer/pulse/blink loops below are the actual ambient animations. */
|
|
117
|
-
@media (prefers-reduced-motion:reduce){
|
|
118
|
-
.bloom{transition:none}
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
/* ═══════════════════ top navigation ═══════════════════ */
|
|
122
|
-
nav{
|
|
123
|
-
position:fixed;top:0;left:0;right:0;height:var(--nav-h);z-index:50;
|
|
124
|
-
display:flex;align-items:center;gap:18px;padding:0 22px;
|
|
125
|
-
border-bottom:1px solid var(--hairline);
|
|
126
|
-
background:var(--nav-bg);backdrop-filter:saturate(180%) blur(22px);
|
|
127
|
-
-webkit-backdrop-filter:saturate(180%) blur(22px);
|
|
128
|
-
}
|
|
129
|
-
nav .brand{font-size:18px;font-weight:700;letter-spacing:-.02em;text-decoration:none;
|
|
130
|
-
white-space:nowrap;flex:0 0 auto}
|
|
131
|
-
nav .tabs{display:flex;gap:2px;flex:1 1 auto;overflow-x:auto;scrollbar-width:none}
|
|
132
|
-
nav .tabs::-webkit-scrollbar{display:none}
|
|
133
|
-
/* Scroll-edge fades (2026-07-06 jank fix): the tab row scrolls with its
|
|
134
|
-
scrollbar hidden, so a clipped "Build Datase" read as broken/overlapped.
|
|
135
|
-
JS (updateTabFades) toggles .fade-r/.fade-l per scroll position so a
|
|
136
|
-
soft fade signals "more tabs this way" only when actually clipped. */
|
|
137
|
-
nav .tabs.fade-r{-webkit-mask-image:linear-gradient(90deg,#000 0,#000 calc(100% - 34px),transparent);
|
|
138
|
-
mask-image:linear-gradient(90deg,#000 0,#000 calc(100% - 34px),transparent)}
|
|
139
|
-
nav .tabs.fade-l{-webkit-mask-image:linear-gradient(90deg,transparent,#000 34px,#000);
|
|
140
|
-
mask-image:linear-gradient(90deg,transparent,#000 34px,#000)}
|
|
141
|
-
nav .tabs.fade-l.fade-r{-webkit-mask-image:linear-gradient(90deg,transparent,#000 34px,#000 calc(100% - 34px),transparent);
|
|
142
|
-
mask-image:linear-gradient(90deg,transparent,#000 34px,#000 calc(100% - 34px),transparent)}
|
|
143
|
-
nav .tab{
|
|
144
|
-
appearance:none;background:none;border:none;cursor:pointer;
|
|
145
|
-
color:var(--dim);font:600 14px/1 inherit;letter-spacing:-.01em;
|
|
146
|
-
padding:8px 14px;border-radius:10px;text-decoration:none;white-space:nowrap;
|
|
147
|
-
transition:color .22s var(--ease),background .22s var(--ease)}
|
|
148
|
-
nav .tab:hover{color:var(--ink)}
|
|
149
|
-
nav .tab.active{color:var(--ink);background:rgba(255,255,255,.08)}
|
|
150
|
-
nav .right{display:flex;align-items:center;gap:12px;flex:0 0 auto}
|
|
151
|
-
nav .model{font:12px var(--mono);color:var(--dim);
|
|
152
|
-
background:var(--card);border:1px solid var(--hairline);border-radius:8px;padding:5px 10px;
|
|
153
|
-
max-width:34vw;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;
|
|
154
|
-
appearance:none;-webkit-appearance:none;cursor:pointer;transition:color .18s var(--ease),border-color .18s var(--ease)}
|
|
155
|
-
nav .model:hover{color:var(--ink);border-color:var(--hairline-strong)}
|
|
156
|
-
/* Model picker anchor + popover (plan §5.6) — same positioning/transition
|
|
157
|
-
language as #chat-sampling-wrap/.samplepop, anchored below-left instead
|
|
158
|
-
of above-right since the nav sits at the top of the page. */
|
|
159
|
-
#nav-model-wrap{position:relative;display:inline-flex}
|
|
160
|
-
#nav-model-wrap #model-pop{top:calc(100% + 9px);bottom:auto;left:0;right:auto;width:340px}
|
|
161
|
-
/* Model picker rows — same list-row language as the adapter table, one
|
|
162
|
-
row per /library entry with its own fit verdict + restart command
|
|
163
|
-
(model-picker.ts; no in-process swap exists, see docs/reference/cli.md). */
|
|
164
|
-
.mp-row{display:flex;align-items:flex-start;gap:9px;padding:9px 10px;border-radius:10px;
|
|
165
|
-
border:1px solid transparent;margin-bottom:4px;transition:background .12s}
|
|
166
|
-
.mp-row:last-child{margin-bottom:0}
|
|
167
|
-
.mp-row.serving{border-color:rgba(48,209,88,.35);background:rgba(48,209,88,.06)}
|
|
168
|
-
.mp-fit-dot{width:9px;height:9px;border-radius:50%;flex:0 0 auto;margin-top:4px}
|
|
169
|
-
.mp-fit-dot.green{background:var(--green);box-shadow:0 0 7px var(--green)}
|
|
170
|
-
.mp-fit-dot.yellow{background:var(--yellow);box-shadow:0 0 7px var(--yellow)}
|
|
171
|
-
.mp-fit-dot.red{background:var(--red)}
|
|
172
|
-
.mp-main{flex:1 1 auto;min-width:0}
|
|
173
|
-
.mp-name{font-size:12.5px;color:var(--ink);font-weight:600;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
|
174
|
-
.mp-meta{font-size:10.5px;color:var(--dimmer);margin-top:2px}
|
|
175
|
-
.mp-meta b{color:var(--dim);font-variant-numeric:tabular-nums}
|
|
176
|
-
.mp-serving-tag{font:10px var(--mono);color:var(--green);letter-spacing:.03em;margin-top:2px}
|
|
177
|
-
.mp-cmd{display:flex;align-items:center;gap:6px;margin-top:6px;background:var(--code-bg-soft);border-radius:7px;
|
|
178
|
-
padding:5px 8px;overflow-x:auto}
|
|
179
|
-
.mp-cmd code{font:11px var(--mono);color:var(--ink);white-space:nowrap}
|
|
180
|
-
.mp-cmd button{appearance:none;cursor:pointer;background:var(--card);border:1px solid var(--hairline);
|
|
181
|
-
color:var(--dim);border-radius:6px;padding:2px 8px;font-size:10.5px;font-weight:600;flex:0 0 auto;white-space:nowrap}
|
|
182
|
-
.mp-cmd button:hover{color:var(--ink);border-color:var(--hairline-strong)}
|
|
183
|
-
.mp-empty{color:var(--dim);font-size:12px;padding:10px 4px;line-height:1.5}
|
|
184
|
-
.mp-foot-note{font-size:10.5px;color:var(--dimmer);line-height:1.4;margin-top:10px;padding-top:10px;border-top:1px solid var(--hairline-soft)}
|
|
185
|
-
/* "Browse models…" entry -> Model Hub panel (plan §9 Phase 3) */
|
|
186
|
-
.mp-browse-hub{display:block;width:100%;appearance:none;cursor:pointer;background:var(--card);
|
|
187
|
-
border:1px solid var(--hairline);color:var(--ink);border-radius:8px;padding:8px 10px;
|
|
188
|
-
font-size:12px;font-weight:600;margin-top:8px;text-align:center;transition:border-color .18s var(--ease),background .18s var(--ease)}
|
|
189
|
-
.mp-browse-hub:hover{border-color:var(--hairline-strong);background:var(--card-hover)}
|
|
190
|
-
.pill{display:inline-flex;align-items:center;gap:8px;
|
|
191
|
-
border:1px solid var(--hairline);border-radius:999px;padding:6px 13px;
|
|
192
|
-
font-size:12.5px;font-weight:600;color:var(--dim);letter-spacing:.01em;white-space:nowrap}
|
|
193
|
-
.pill .dot{width:8px;height:8px;border-radius:50%;background:var(--dimmer);
|
|
194
|
-
box-shadow:0 0 0 transparent;transition:all .4s}
|
|
195
|
-
.pill.ok .dot{background:var(--green);box-shadow:0 0 11px var(--green)}
|
|
196
|
-
.pill.ok{color:var(--ink)}
|
|
197
|
-
#chat-think{display:none;cursor:pointer;gap:7px;padding:5px 11px;font-size:12px;user-select:none;-webkit-user-select:none;
|
|
198
|
-
transition:color .22s var(--ease),border-color .22s var(--ease)}
|
|
199
|
-
#chat-think .dot{width:7px;height:7px}
|
|
200
|
-
#chat-think:hover{border-color:var(--hairline-strong)}
|
|
201
|
-
#chat-think.think-on{color:var(--ink)}
|
|
202
|
-
#chat-think.think-on .dot{background:var(--blue);box-shadow:0 0 11px var(--blue)}
|
|
203
|
-
.pill.bad .dot{background:var(--red);box-shadow:0 0 11px var(--red)}
|
|
204
|
-
.pill.warn .dot{background:var(--orange);box-shadow:0 0 11px var(--orange)}
|
|
205
|
-
/* sampling disclosure: a small pill that opens a popover above the composer */
|
|
206
|
-
#chat-sampling-wrap{position:relative;display:inline-flex}
|
|
207
|
-
#chat-sampling{cursor:pointer;gap:6px;padding:5px 11px;font-size:12px;user-select:none;-webkit-user-select:none;
|
|
208
|
-
transition:color .22s var(--ease),border-color .22s var(--ease)}
|
|
209
|
-
#chat-sampling:hover{border-color:var(--hairline-strong);color:var(--ink)}
|
|
210
|
-
#chat-sampling.on{color:var(--ink);border-color:var(--hairline-strong)}
|
|
211
|
-
#chat-sampling.dirty .dot{background:var(--blue);box-shadow:0 0 11px var(--blue)}
|
|
212
|
-
/* Distinct one-shot-armed dot state (task brief item 2) — orange/amber
|
|
213
|
-
reads as "temporary/pending" against the plain blue "overridden"
|
|
214
|
-
state, matching the same semantic the orange dot already carries
|
|
215
|
-
elsewhere (.pill.warn) rather than inventing a new color meaning. */
|
|
216
|
-
#chat-sampling.armed .dot{background:var(--orange);box-shadow:0 0 11px var(--orange);animation:pulse-armed 1.6s var(--ease) infinite}
|
|
217
|
-
@media (prefers-reduced-motion:reduce){#chat-sampling.armed .dot{animation:none}}
|
|
218
|
-
@keyframes pulse-armed{0%,100%{opacity:1}50%{opacity:.45}}
|
|
219
|
-
#chat-sampling .dot{width:7px;height:7px}
|
|
220
|
-
.samplepop{position:absolute;bottom:calc(100% + 9px);right:0;z-index:40;width:300px;
|
|
221
|
-
max-height:min(70vh,560px);overflow-y:auto;
|
|
222
|
-
background:var(--popover-panel);border:1px solid var(--hairline);border-radius:13px;padding:13px 14px;
|
|
223
|
-
backdrop-filter:blur(22px) saturate(1.3);-webkit-backdrop-filter:blur(22px) saturate(1.3);
|
|
224
|
-
box-shadow:0 18px 50px rgba(0,0,0,.35);display:none;
|
|
225
|
-
transform:translateY(4px);opacity:0;transition:opacity .16s var(--ease),transform .16s var(--ease)}
|
|
226
|
-
.samplepop.open{display:block;transform:translateY(0);opacity:1}
|
|
227
|
-
.samplepop h4{font-size:12px;font-weight:600;color:var(--dim);letter-spacing:.02em;
|
|
228
|
-
text-transform:uppercase;margin:0}
|
|
229
|
-
.samp-head-row{display:flex;align-items:center;justify-content:space-between;gap:8px;margin:0 0 10px}
|
|
230
|
-
.samp-oneshot-chip{font:10.5px/1 var(--mono);color:var(--orange);background:rgba(255,159,10,.12);
|
|
231
|
-
border:1px solid rgba(255,159,10,.3);border-radius:999px;padding:3px 8px;letter-spacing:.01em;white-space:nowrap}
|
|
232
|
-
.samp-scope-row{display:flex;align-items:center;gap:9px;margin:0 0 12px;padding-bottom:11px;border-bottom:1px solid var(--hairline-soft)}
|
|
233
|
-
.samp-scope-row label{font-size:12.5px;color:var(--ink);width:74px;flex:0 0 auto}
|
|
234
|
-
.samp-scope-row select{flex:1 1 auto;background:var(--card);border:1px solid var(--hairline);border-radius:7px;
|
|
235
|
-
color:var(--ink);font-size:12px;padding:5px 8px;cursor:pointer;appearance:none;-webkit-appearance:none;
|
|
236
|
-
transition:border-color .18s var(--ease)}
|
|
237
|
-
.samp-scope-row select:hover,.samp-scope-row select:focus{border-color:var(--hairline-strong)}
|
|
238
|
-
.samprow{display:flex;align-items:center;gap:9px;margin:0 0 11px}
|
|
239
|
-
.samprow label{font-size:12.5px;color:var(--ink);width:74px;flex:0 0 auto}
|
|
240
|
-
.samprow input[type=range]{flex:1 1 auto;-webkit-appearance:none;appearance:none;height:4px;margin:0;
|
|
241
|
-
border-radius:3px;background:rgba(255,255,255,.15);outline:none;cursor:pointer}
|
|
242
|
-
.samprow input[type=range]::-webkit-slider-thumb{-webkit-appearance:none;appearance:none;width:15px;height:15px;
|
|
243
|
-
border-radius:50%;background:var(--ink);border:0;box-shadow:0 1px 4px rgba(0,0,0,.5);cursor:pointer;
|
|
244
|
-
transition:background .15s var(--ease)}
|
|
245
|
-
.samprow input[type=range]:hover::-webkit-slider-thumb,
|
|
246
|
-
.samprow input[type=range]:focus::-webkit-slider-thumb{background:var(--blue)}
|
|
247
|
-
.samp-val{width:40px;flex:0 0 auto;text-align:right;font-family:var(--mono);font-size:12px;color:var(--ink)}
|
|
248
|
-
.samp-val.auto{color:var(--dimmer)}
|
|
249
|
-
.samp-num{width:74px;flex:0 0 auto;text-align:right;background:var(--card);border:1px solid var(--hairline);
|
|
250
|
-
border-radius:6px;color:var(--ink);font-family:var(--mono);font-size:11.5px;padding:3px 6px}
|
|
251
|
-
.samp-num.auto{color:var(--dimmer)}
|
|
252
|
-
.samppop-foot{display:flex;justify-content:space-between;align-items:center;margin-top:3px}
|
|
253
|
-
.samppop-foot .note{font-size:10.5px;color:var(--dimmer);line-height:1.3;flex:1 1 auto;padding-right:8px}
|
|
254
|
-
.samppop-foot button{appearance:none;cursor:pointer;background:var(--card);border:1px solid var(--hairline);
|
|
255
|
-
color:var(--dim);border-radius:8px;padding:5px 11px;font-size:11.5px;font-weight:600;flex:0 0 auto;
|
|
256
|
-
transition:color .18s var(--ease),border-color .18s var(--ease)}
|
|
257
|
-
.samppop-foot button:hover{color:var(--ink);border-color:var(--hairline-strong)}
|
|
258
|
-
/* Advanced sampling disclosure — collapsed by default, same visual language
|
|
259
|
-
as the primary rows so it doesn't read as a separate control system. */
|
|
260
|
-
.samp-adv{margin:2px 0 12px}
|
|
261
|
-
.samp-adv > summary{cursor:pointer;font-size:11.5px;font-weight:600;color:var(--dim);letter-spacing:.02em;
|
|
262
|
-
text-transform:uppercase;list-style:none;padding:2px 0 8px;user-select:none;-webkit-user-select:none}
|
|
263
|
-
.samp-adv > summary::-webkit-details-marker{display:none}
|
|
264
|
-
.samp-adv > summary::before{content:"▸ ";display:inline-block;transition:transform .15s var(--ease)}
|
|
265
|
-
.samp-adv[open] > summary::before{transform:rotate(90deg)}
|
|
266
|
-
.samp-adv > summary:hover{color:var(--ink)}
|
|
267
|
-
.samp-adv .seed-row{display:flex;align-items:center;gap:9px;margin:0 0 4px}
|
|
268
|
-
.samp-adv .seed-row label{font-size:12.5px;color:var(--ink);width:74px;flex:0 0 auto}
|
|
269
|
-
.samp-adv .seed-row input[type=text]{flex:1 1 auto;background:var(--card);border:1px solid var(--hairline);
|
|
270
|
-
border-radius:6px;color:var(--ink);font-family:var(--mono);font-size:12px;padding:5px 8px}
|
|
271
|
-
.samp-adv .seed-row input[type=text]::placeholder{color:var(--dimmer)}
|
|
272
|
-
/* System prompt pill + popover (plan §9 Phase 2) — same chrome as
|
|
273
|
-
#chat-sampling-wrap/.samplepop, just to its left in the hint row. */
|
|
274
|
-
#chat-sysprompt-wrap{position:relative;display:inline-flex}
|
|
275
|
-
#chat-sysprompt{cursor:pointer;gap:6px;padding:5px 11px;font-size:12px;user-select:none;-webkit-user-select:none;
|
|
276
|
-
transition:color .22s var(--ease),border-color .22s var(--ease)}
|
|
277
|
-
#chat-sysprompt:hover{border-color:var(--hairline-strong);color:var(--ink)}
|
|
278
|
-
#chat-sysprompt.on{color:var(--ink);border-color:var(--hairline-strong)}
|
|
279
|
-
#chat-sysprompt.dirty .dot{background:var(--purple);box-shadow:0 0 11px var(--purple)}
|
|
280
|
-
#chat-sysprompt .dot{width:7px;height:7px}
|
|
281
|
-
.sysprompt-pop{width:320px}
|
|
282
|
-
#sysprompt-text{width:100%;resize:vertical;min-height:80px;background:var(--card);
|
|
283
|
-
border:1px solid var(--hairline);border-radius:9px;color:var(--ink);
|
|
284
|
-
font:12.5px/1.5 inherit;padding:9px 10px;margin:0 0 6px}
|
|
285
|
-
#sysprompt-text::placeholder{color:var(--dimmer)}
|
|
286
|
-
#sysprompt-text:focus{outline:none;border-color:var(--hairline-strong)}
|
|
287
|
-
.sysprompt-foot-row{display:flex;justify-content:space-between;align-items:center;margin:0 0 10px}
|
|
288
|
-
.sysprompt-foot-row .note{font-size:10.5px;color:var(--dimmer)}
|
|
289
|
-
.sysprompt-foot-row button,.sysprompt-preset-row button{appearance:none;cursor:pointer;background:var(--card);
|
|
290
|
-
border:1px solid var(--hairline);color:var(--dim);border-radius:8px;padding:5px 11px;font-size:11.5px;
|
|
291
|
-
font-weight:600;flex:0 0 auto;transition:color .18s var(--ease),border-color .18s var(--ease)}
|
|
292
|
-
.sysprompt-foot-row button:hover,.sysprompt-preset-row button:hover{color:var(--ink);border-color:var(--hairline-strong)}
|
|
293
|
-
.sysprompt-presets{margin-top:12px;padding-top:12px;border-top:1px solid var(--hairline-soft)}
|
|
294
|
-
.sysprompt-preset-row{display:flex;align-items:center;gap:6px;margin:0 0 6px}
|
|
295
|
-
#sysprompt-preset-select{flex:1 1 auto;min-width:0;max-width:none;appearance:none;-webkit-appearance:none;
|
|
296
|
-
cursor:pointer;background:var(--card);text-overflow:ellipsis}
|
|
297
|
-
#nav-download{color:var(--ink);font-family:var(--mono);font-size:11.5px;font-weight:500}
|
|
298
|
-
#nav-download .dot{background:var(--orange);box-shadow:0 0 11px var(--orange);animation:dlpulse 1.4s ease-in-out infinite}
|
|
299
|
-
@keyframes dlpulse{0%,100%{opacity:.4}50%{opacity:1}}
|
|
300
|
-
.hf-gear,.icon-btn{appearance:none;cursor:pointer;display:inline-flex;align-items:center;justify-content:center;
|
|
301
|
-
width:34px;height:34px;border-radius:10px;background:var(--card);border:1px solid var(--hairline);
|
|
302
|
-
color:var(--dim);flex:0 0 auto;transition:color .22s var(--ease),background .22s var(--ease),border-color .22s var(--ease)}
|
|
303
|
-
.hf-gear:hover,.icon-btn:hover{color:var(--ink);background:var(--card-hover);border-color:var(--hairline-strong)}
|
|
304
|
-
.hf-gear.saved{color:var(--green);border-color:rgba(48,209,88,.4)}
|
|
305
|
-
|
|
306
|
-
/* mobile drawer hamburger — chat route only, hidden ≥760px via the
|
|
307
|
-
media query beside #chat-sidebar above */
|
|
308
|
-
#chat-hamburger{align-items:center;justify-content:center;gap:3px}
|
|
309
|
-
|
|
310
|
-
/* theme toggle: 3-way auto/dark/light segmented control, nav-pill sized */
|
|
311
|
-
#theme-toggle{display:inline-flex;border:1px solid var(--hairline);border-radius:10px;overflow:hidden;flex:0 0 auto}
|
|
312
|
-
#theme-toggle button{appearance:none;cursor:pointer;background:transparent;border:none;color:var(--dim);
|
|
313
|
-
padding:0 9px;height:34px;font-size:13px;line-height:1;transition:color .18s var(--ease),background .18s var(--ease)}
|
|
314
|
-
#theme-toggle button:not(:last-child){border-right:1px solid var(--hairline)}
|
|
315
|
-
#theme-toggle button:hover{color:var(--ink);background:var(--card-hover)}
|
|
316
|
-
#theme-toggle button.active{color:var(--ink);background:var(--card-hover)}
|
|
317
|
-
#theme-toggle button:focus-visible{outline:2px solid var(--blue);outline-offset:-2px}
|
|
318
|
-
/* Auto's ◐ icon only appears when the text label hides at narrow widths
|
|
319
|
-
(see the 900px media block) — never both, never neither. */
|
|
320
|
-
#theme-toggle .tt-icon{display:none}
|
|
321
|
-
|
|
322
|
-
/* Developer toggle (plan §8/§9 Phase 2): a small switch, same nav-pill
|
|
323
|
-
sizing language as #theme-toggle, showing/hiding the Quantize/Fine-tune/
|
|
324
|
-
Build Dataset/Status/Curves/Routes tabs. aria-pressed reflects state
|
|
325
|
-
(kept in sync by shell.ts's applyDeveloperMode). */
|
|
326
|
-
#nav-developer{appearance:none;cursor:pointer;display:inline-flex;align-items:center;gap:7px;
|
|
327
|
-
border:1px solid var(--hairline);border-radius:10px;background:transparent;color:var(--dim);
|
|
328
|
-
padding:0 10px;height:34px;font-size:12.5px;font-weight:600;letter-spacing:.01em;flex:0 0 auto;
|
|
329
|
-
transition:color .18s var(--ease),background .18s var(--ease),border-color .18s var(--ease)}
|
|
330
|
-
#nav-developer:hover{color:var(--ink);background:var(--card-hover);border-color:var(--hairline-strong)}
|
|
331
|
-
#nav-developer:focus-visible{outline:2px solid var(--blue);outline-offset:-2px}
|
|
332
|
-
#nav-developer .nd-dot{width:8px;height:8px;border-radius:50%;background:var(--dimmer);flex:0 0 auto;
|
|
333
|
-
transition:background .18s var(--ease)}
|
|
334
|
-
#nav-developer .nd-dot.on{background:var(--green)}
|
|
335
|
-
#nav-developer[aria-checked="true"]{color:var(--ink)}
|
|
336
|
-
|
|
337
|
-
/* ─── keyboard shortcut sheet (Cmd/Ctrl+/) ─── */
|
|
338
|
-
#shortcut-overlay{position:fixed;inset:0;z-index:210;display:none;align-items:flex-start;justify-content:center;
|
|
339
|
-
background:var(--overlay-scrim);backdrop-filter:blur(8px);-webkit-backdrop-filter:blur(8px);padding:calc(var(--nav-h) + 24px) 20px 20px}
|
|
340
|
-
#shortcut-overlay.open{display:flex;animation:fade .2s var(--ease)}
|
|
341
|
-
#shortcut-modal{width:min(480px,100%);max-height:min(76vh,620px);overflow-y:auto;background:var(--overlay-panel);
|
|
342
|
-
border:1px solid var(--hairline);border-radius:20px;padding:22px 26px;box-shadow:0 30px 80px rgba(0,0,0,.35)}
|
|
343
|
-
.sk-head{display:flex;align-items:center;justify-content:space-between;margin-bottom:14px}
|
|
344
|
-
.sk-title{font-size:18px;font-weight:700;letter-spacing:-.02em}
|
|
345
|
-
.sk-close{appearance:none;background:none;border:none;cursor:pointer;color:var(--dim);font-size:16px;
|
|
346
|
-
width:30px;height:30px;border-radius:8px;transition:color .2s,background .2s}
|
|
347
|
-
.sk-close:hover{color:var(--ink);background:var(--card-hover)}
|
|
348
|
-
.sk-row{display:flex;align-items:center;justify-content:space-between;gap:14px;padding:8px 0;
|
|
349
|
-
border-bottom:1px solid var(--hairline-soft)}
|
|
350
|
-
.sk-row:last-child{border-bottom:none}
|
|
351
|
-
.sk-desc{font-size:13.5px;color:var(--ink)}
|
|
352
|
-
.sk-keys{display:flex;gap:5px;flex:0 0 auto}
|
|
353
|
-
.sk-key{font:600 11.5px var(--mono);color:var(--ink);background:var(--card);border:1px solid var(--hairline);
|
|
354
|
-
border-radius:6px;padding:3px 8px;min-width:20px;text-align:center}
|
|
355
|
-
|
|
356
|
-
/* ─── HF settings overlay ─── */
|
|
357
|
-
#hf-overlay{position:fixed;inset:0;z-index:200;display:none;align-items:flex-start;justify-content:center;
|
|
358
|
-
background:var(--overlay-scrim);backdrop-filter:blur(8px);-webkit-backdrop-filter:blur(8px);padding:calc(var(--nav-h) + 24px) 20px 20px}
|
|
359
|
-
#hf-overlay.open{display:flex;animation:fade .3s var(--ease)}
|
|
360
|
-
#hf-modal{width:min(460px,100%);background:var(--overlay-panel);border:1px solid var(--hairline);border-radius:20px;
|
|
361
|
-
padding:24px 26px;box-shadow:0 30px 80px rgba(0,0,0,.35)}
|
|
362
|
-
.hf-modal-head{display:flex;align-items:center;justify-content:space-between;margin-bottom:6px}
|
|
363
|
-
.hf-modal-title{font-size:20px;font-weight:700;letter-spacing:-.02em}
|
|
364
|
-
.hf-modal-sub{font-size:13.5px;color:var(--dim);margin-bottom:20px;line-height:1.5}
|
|
365
|
-
.hf-close{appearance:none;background:none;border:none;cursor:pointer;color:var(--dim);font-size:16px;
|
|
366
|
-
width:30px;height:30px;border-radius:8px;transition:color .2s,background .2s}
|
|
367
|
-
.hf-close:hover{color:var(--ink);background:var(--card-hover)}
|
|
368
|
-
|
|
369
|
-
/* ─── Settings modal sections (plan §5.4/§6.5) ─── */
|
|
370
|
-
.settings-section{margin:0 0 20px;padding:0 0 20px;border-bottom:1px solid var(--hairline-soft)}
|
|
371
|
-
.settings-section:last-child{border-bottom:none;padding-bottom:0;margin-bottom:0}
|
|
372
|
-
.settings-h{font-size:12px;font-weight:700;letter-spacing:.04em;text-transform:uppercase;color:var(--dim);margin:0 0 12px}
|
|
373
|
-
.settings-toggle-row{display:flex;align-items:center;gap:10px;font-size:13.5px;color:var(--ink);
|
|
374
|
-
cursor:pointer;font-weight:500;margin-bottom:8px}
|
|
375
|
-
.settings-toggle-row input{width:auto;margin:0;accent-color:var(--blue)}
|
|
376
|
-
.settings-note{font-size:12px;color:var(--orange);margin:8px 0 0;line-height:1.5}
|
|
377
|
-
#settings-approvals-list{margin-top:12px}
|
|
378
|
-
.settings-approvals-empty{font-size:12.5px;color:var(--dimmer)}
|
|
379
|
-
.settings-approval-row{display:flex;align-items:center;gap:10px;padding:7px 0;font-size:13px;
|
|
380
|
-
border-top:1px solid var(--hairline-soft)}
|
|
381
|
-
.settings-approval-row:first-child{border-top:none}
|
|
382
|
-
.settings-approval-row .satool{font:600 12.5px var(--mono);color:var(--ink);flex:1 1 auto}
|
|
383
|
-
.settings-approval-row .saforget{appearance:none;cursor:pointer;background:none;border:1px solid var(--hairline);
|
|
384
|
-
color:var(--dim);border-radius:7px;padding:3px 9px;font-size:11.5px;font-weight:600;flex:0 0 auto;
|
|
385
|
-
transition:color .18s var(--ease),border-color .18s var(--ease)}
|
|
386
|
-
.settings-approval-row .saforget:hover{color:var(--red);border-color:rgba(255,69,58,.4)}
|
|
387
|
-
|
|
388
|
-
/* ─── inline push-to-hub panel (in done-step cards) ─── */
|
|
389
|
-
.pushpanel{margin-top:14px;border-top:1px solid var(--hairline-soft);padding-top:14px}
|
|
390
|
-
.pushpanel .field{margin-bottom:12px}
|
|
391
|
-
.pushpanel label.chk{display:flex;align-items:center;gap:9px;font-size:13.5px;color:var(--ink);cursor:pointer;font-weight:500}
|
|
392
|
-
.pushpanel label.chk input{width:auto;margin:0;accent-color:var(--blue)}
|
|
393
|
-
|
|
394
|
-
/* ─── Memory panel (plan §5.5/§9 Phase 2) — a full-height overlay beside
|
|
395
|
-
chat, same drawer/overlay language as the mobile sidebar and the HF
|
|
396
|
-
modal: fixed, scrimmed, focus-trapped, Escape closes. Sidebar entry
|
|
397
|
-
is a quiet row (not a nav tab) — see #chat-memory-entry markup. ─── */
|
|
398
|
-
#mem-overlay{position:fixed;inset:var(--nav-h) 0 0 0;z-index:150;display:none;justify-content:flex-end;
|
|
399
|
-
background:var(--overlay-scrim);backdrop-filter:blur(4px);-webkit-backdrop-filter:blur(4px)}
|
|
400
|
-
#mem-overlay.open{display:flex;animation:fade .22s var(--ease)}
|
|
401
|
-
#mem-panel{width:min(480px,100%);height:100%;background:var(--overlay-panel);border-left:1px solid var(--hairline);
|
|
402
|
-
display:flex;flex-direction:column;box-shadow:-24px 0 60px rgba(0,0,0,.4)}
|
|
403
|
-
.mem-head{display:flex;align-items:center;gap:10px;padding:16px 18px;border-bottom:1px solid var(--hairline-soft);flex:0 0 auto}
|
|
404
|
-
.mem-head h2{font-size:16px;font-weight:700;letter-spacing:-.01em;flex:1 1 auto}
|
|
405
|
-
.mem-close{appearance:none;background:none;border:none;cursor:pointer;color:var(--dim);font-size:15px;
|
|
406
|
-
width:30px;height:30px;border-radius:8px;transition:color .2s,background .2s;flex:0 0 auto}
|
|
407
|
-
.mem-close:hover{color:var(--ink);background:var(--card-hover)}
|
|
408
|
-
.mem-back{appearance:none;background:none;border:none;cursor:pointer;color:var(--dim);font-size:13px;
|
|
409
|
-
display:none;align-items:center;gap:4px;padding:4px 2px;flex:0 0 auto}
|
|
410
|
-
.mem-back:hover{color:var(--ink)}
|
|
411
|
-
.mem-back.show{display:inline-flex}
|
|
412
|
-
/* status strip: article count · last synthesis · git state */
|
|
413
|
-
.mem-status{display:flex;gap:16px;padding:12px 18px;border-bottom:1px solid var(--hairline-soft);flex:0 0 auto;flex-wrap:wrap}
|
|
414
|
-
.mem-stat{font-size:11.5px;color:var(--dim)}
|
|
415
|
-
.mem-stat b{color:var(--ink);font-weight:600;font-variant-numeric:tabular-nums}
|
|
416
|
-
.mem-body{flex:1 1 auto;overflow-y:auto;padding:14px 18px 24px}
|
|
417
|
-
/* no-vault state (consent already declined earlier, or panel opened directly) */
|
|
418
|
-
.mem-empty{text-align:center;color:var(--dim);padding:50px 18px;font-size:13.5px;line-height:1.6}
|
|
419
|
-
.mem-empty .mcta{margin-top:16px}
|
|
420
|
-
/* search + list */
|
|
421
|
-
#mem-search{width:100%;background:var(--card);border:1px solid var(--hairline);border-radius:10px;
|
|
422
|
-
color:var(--ink);font-size:12.5px;padding:9px 12px;outline:none;transition:border-color .2s;margin-bottom:14px}
|
|
423
|
-
#mem-search:focus{border-color:rgba(10,132,255,.5)}
|
|
424
|
-
#mem-search::placeholder{color:var(--dimmer)}
|
|
425
|
-
.mem-sec{margin-bottom:16px}
|
|
426
|
-
.mem-sec:last-child{margin-bottom:0}
|
|
427
|
-
.mem-sec-title{font-size:10.5px;font-weight:600;letter-spacing:.1em;text-transform:uppercase;color:var(--dimmer);
|
|
428
|
-
margin-bottom:7px;display:flex;align-items:center;gap:6px}
|
|
429
|
-
.mem-art{display:flex;align-items:center;gap:9px;padding:9px 10px;border-radius:10px;cursor:pointer;
|
|
430
|
-
border:1px solid transparent;transition:background .12s,border-color .12s}
|
|
431
|
-
.mem-art:hover{background:var(--card)}
|
|
432
|
-
.mem-art-name{font-size:13px;color:var(--ink);flex:1 1 auto;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
|
433
|
-
.mem-art-excerpt{font-size:11px;color:var(--dim);margin-top:1px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
|
434
|
-
.mem-art-ico{font-size:12px;color:var(--dimmer);flex:0 0 auto}
|
|
435
|
-
.mem-list-empty{color:var(--dim);font-size:12.5px;padding:8px 10px;line-height:1.5}
|
|
436
|
-
/* article view */
|
|
437
|
-
.mem-art-head{margin-bottom:12px}
|
|
438
|
-
.mem-art-title{font-size:19px;font-weight:700;letter-spacing:-.02em;margin-bottom:4px}
|
|
439
|
-
.mem-art-tabs{display:flex;gap:6px;margin:10px 0 14px}
|
|
440
|
-
.mem-art-tab{font:600 11.5px/1 inherit;color:var(--dim);background:var(--card);border:1px solid var(--hairline);
|
|
441
|
-
border-radius:999px;padding:6px 13px;cursor:pointer;transition:all .18s var(--ease)}
|
|
442
|
-
.mem-art-tab:hover{color:var(--ink)}
|
|
443
|
-
.mem-art-tab.active{color:var(--ink);border-color:var(--hairline-strong);background:var(--card-hover)}
|
|
444
|
-
.mem-article-render{font-size:13.5px;line-height:1.65;color:var(--ink)}
|
|
445
|
-
.mem-article-render h1,.mem-article-render h2,.mem-article-render h3{font-weight:700;margin:16px 0 7px;letter-spacing:-.01em}
|
|
446
|
-
.mem-article-render h1:first-child,.mem-article-render h2:first-child{margin-top:0}
|
|
447
|
-
.mem-article-render p{margin:0 0 10px}
|
|
448
|
-
.mem-article-render ul,.mem-article-render ol{margin:0 0 10px;padding-left:22px}
|
|
449
|
-
.mem-article-render code{font-family:var(--mono);font-size:.86em;background:rgba(127,127,127,.18);padding:2px 7px;border-radius:6px}
|
|
450
|
-
.mem-article-render pre{background:var(--code-bg);color:var(--code-ink);border:1px solid var(--hairline-soft);border-radius:12px;
|
|
451
|
-
padding:12px 14px;overflow-x:auto;margin:10px 0;font:12px/1.6 var(--mono)}
|
|
452
|
-
.mem-article-render a{color:var(--cyan);text-decoration:none;border-bottom:1px solid rgba(100,210,255,.34)}
|
|
453
|
-
/* links line (inbound/outbound) */
|
|
454
|
-
.mem-links{display:flex;flex-direction:column;gap:8px;margin-top:16px;padding-top:14px;border-top:1px solid var(--hairline-soft)}
|
|
455
|
-
.mem-links-row{display:flex;flex-wrap:wrap;gap:6px;align-items:center;font-size:11.5px}
|
|
456
|
-
.mem-links-row .lbl{color:var(--dimmer);flex:0 0 auto;font-weight:600;letter-spacing:.03em}
|
|
457
|
-
.mem-link-chip{font-size:12px;color:var(--dim);background:var(--card);border:1px solid var(--hairline);
|
|
458
|
-
border-radius:999px;padding:4px 11px;cursor:pointer;transition:all .15s var(--ease)}
|
|
459
|
-
.mem-link-chip:hover{color:var(--ink);border-color:var(--hairline-strong)}
|
|
460
|
-
/* history / diff (code-panel visual language, matching .tool pre / .approval pre) */
|
|
461
|
-
.mem-hist-entry{display:flex;align-items:baseline;gap:10px;padding:9px 10px;border-radius:10px;cursor:pointer;
|
|
462
|
-
border:1px solid transparent;transition:background .12s}
|
|
463
|
-
.mem-hist-entry:hover{background:var(--card)}
|
|
464
|
-
.mem-hist-entry.active{background:var(--card);border-color:var(--hairline)}
|
|
465
|
-
.mem-hist-subject{font-size:13px;color:var(--ink);flex:1 1 auto;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
|
466
|
-
.mem-hist-date{font-size:11px;color:var(--dimmer);font-variant-numeric:tabular-nums;flex:0 0 auto}
|
|
467
|
-
.mem-hist-hash{font:11px var(--mono);color:var(--dimmer);flex:0 0 auto}
|
|
468
|
-
.mem-diff{background:var(--code-bg-soft);border-radius:9px;padding:11px 13px;overflow-x:auto;margin-top:10px;
|
|
469
|
-
font:11.5px/1.55 var(--mono);white-space:pre-wrap;word-break:break-word;max-height:420px;overflow-y:auto}
|
|
470
|
-
.mem-diff .diffadd{color:var(--green)}
|
|
471
|
-
.mem-diff .diffdel{color:var(--red)}
|
|
472
|
-
.mem-diff .diffctx{color:var(--dim)}
|
|
473
|
-
.mem-diff .diffhunk{color:var(--cyan)}
|
|
474
|
-
|
|
475
|
-
/* sidebar entry to the panel (quiet, count-bearing — not a nav tab) */
|
|
476
|
-
#chat-memory-entry{display:flex;align-items:center;gap:9px;padding:9px 11px;border-radius:10px;cursor:pointer;
|
|
477
|
-
border:1px solid transparent;color:var(--dim);font-size:12.5px;transition:background .12s,color .12s;margin-top:auto}
|
|
478
|
-
#chat-memory-entry:hover{background:var(--card);color:var(--ink)}
|
|
479
|
-
#chat-memory-entry .mi{font-size:13px;flex:0 0 auto;color:var(--purple)}
|
|
480
|
-
#chat-memory-entry .mt{flex:1 1 auto}
|
|
481
|
-
#chat-memory-entry .mcount{font:11px var(--mono);color:var(--dimmer);background:var(--card);border-radius:999px;padding:2px 8px;flex:0 0 auto}
|
|
482
|
-
|
|
483
|
-
/* ─── Adapter routing table (plan §5.6/§9 Phase 2) — same right-side
|
|
484
|
-
overlay/panel/head/body chrome as the Memory panel (mem-overlay/
|
|
485
|
-
mem-panel/mem-head/mem-body), new top-level ids since it's a distinct
|
|
486
|
-
panel opened from the composer instead of the sidebar. ─── */
|
|
487
|
-
#adapters-overlay{position:fixed;inset:var(--nav-h) 0 0 0;z-index:150;display:none;justify-content:flex-end;
|
|
488
|
-
background:var(--overlay-scrim);backdrop-filter:blur(4px);-webkit-backdrop-filter:blur(4px)}
|
|
489
|
-
#adapters-overlay.open{display:flex;animation:fade .22s var(--ease)}
|
|
490
|
-
#adapters-panel{width:min(520px,100%);height:100%;background:var(--overlay-panel);border-left:1px solid var(--hairline);
|
|
491
|
-
display:flex;flex-direction:column;box-shadow:-24px 0 60px rgba(0,0,0,.4)}
|
|
492
|
-
#adapters-body{flex:1 1 auto;overflow-y:auto;padding:14px 18px 24px}
|
|
493
|
-
.ad-empty{text-align:center;color:var(--dim);padding:50px 18px;font-size:13.5px;line-height:1.6}
|
|
494
|
-
.ad-sec-title{font-size:10.5px;font-weight:600;letter-spacing:.1em;text-transform:uppercase;color:var(--dimmer);margin:0 0 9px}
|
|
495
|
-
.ad-note{font-size:11.5px;color:var(--dim);line-height:1.5;margin:0 0 16px}
|
|
496
|
-
.ad-row{display:flex;align-items:flex-start;gap:11px;padding:11px 12px;border-radius:12px;
|
|
497
|
-
border:1px solid var(--hairline-soft);margin-bottom:9px;transition:border-color .15s var(--ease)}
|
|
498
|
-
.ad-row.incompatible{opacity:.55}
|
|
499
|
-
.ad-row.selected{border-color:rgba(10,132,255,.5);background:rgba(10,132,255,.06)}
|
|
500
|
-
.ad-row-main{flex:1 1 auto;min-width:0}
|
|
501
|
-
.ad-row-head{display:flex;align-items:center;gap:8px;flex-wrap:wrap}
|
|
502
|
-
.ad-row-id{font-size:13.5px;font-weight:600;color:var(--ink);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
|
503
|
-
.ad-badge{font:10.5px var(--mono);letter-spacing:.02em;padding:2px 8px;border-radius:999px;border:1px solid var(--hairline);color:var(--dim);white-space:nowrap}
|
|
504
|
-
.ad-badge.mounted{color:var(--green);border-color:rgba(48,209,88,.4)}
|
|
505
|
-
.ad-badge.selected{color:var(--blue);border-color:rgba(10,132,255,.4)}
|
|
506
|
-
.ad-meta{font-size:11.5px;color:var(--dimmer);margin-top:4px;display:flex;gap:10px;flex-wrap:wrap}
|
|
507
|
-
.ad-meta b{color:var(--dim);font-weight:600;font-variant-numeric:tabular-nums}
|
|
508
|
-
.ad-why{font-size:11px;color:var(--dimmer);margin-top:4px;font-style:italic}
|
|
509
|
-
.ad-actions{display:flex;gap:6px;flex:0 0 auto;flex-wrap:wrap;align-items:center}
|
|
510
|
-
.ad-actions button{appearance:none;cursor:pointer;background:var(--card);border:1px solid var(--hairline);
|
|
511
|
-
color:var(--dim);border-radius:8px;padding:5px 11px;font-size:11.5px;font-weight:600;white-space:nowrap;
|
|
512
|
-
transition:color .18s var(--ease),border-color .18s var(--ease),background .18s var(--ease)}
|
|
513
|
-
.ad-actions button:hover:not(:disabled){color:var(--ink);border-color:var(--hairline-strong)}
|
|
514
|
-
.ad-actions button:disabled{opacity:.4;cursor:default}
|
|
515
|
-
.ad-actions button.primary{color:var(--ink);border-color:rgba(10,132,255,.5);background:rgba(10,132,255,.1)}
|
|
516
|
-
.ad-actions label.ad-stack-chk{display:flex;align-items:center;gap:5px;font-size:11px;color:var(--dimmer);cursor:pointer;white-space:nowrap}
|
|
517
|
-
.ad-actions label.ad-stack-chk input{width:auto;margin:0;accent-color:var(--blue)}
|
|
518
|
-
.ad-stack-bar{display:flex;align-items:center;gap:8px;flex-wrap:wrap;padding:10px 12px;border-radius:12px;
|
|
519
|
-
background:var(--card);border:1px solid var(--hairline);margin-bottom:14px}
|
|
520
|
-
.ad-stack-bar .lbl{font-size:11.5px;color:var(--dim);flex:0 0 auto}
|
|
521
|
-
.ad-stack-bar .expr{font:12px var(--mono);color:var(--ink);flex:1 1 auto;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
|
522
|
-
|
|
523
|
-
/* ─── Model Hub (plan §9 Phase 3, beat-matrix Axis 3) — same right-side
|
|
524
|
-
overlay/panel/head/body chrome as Memory + Adapters (mem-overlay/
|
|
525
|
-
mem-panel/mem-head/mem-body), new top-level ids since it's a distinct
|
|
526
|
-
panel opened from the model picker instead of the sidebar/composer. ─── */
|
|
527
|
-
#hub-overlay{position:fixed;inset:var(--nav-h) 0 0 0;z-index:150;display:none;justify-content:flex-end;
|
|
528
|
-
background:var(--overlay-scrim);backdrop-filter:blur(4px);-webkit-backdrop-filter:blur(4px)}
|
|
529
|
-
#hub-overlay.open{display:flex;animation:fade .22s var(--ease)}
|
|
530
|
-
#hub-panel{width:min(480px,100%);height:100%;background:var(--overlay-panel);border-left:1px solid var(--hairline);
|
|
531
|
-
display:flex;flex-direction:column;box-shadow:-24px 0 60px rgba(0,0,0,.4)}
|
|
532
|
-
#hub-body{flex:1 1 auto;overflow-y:auto;padding:14px 18px 24px}
|
|
533
|
-
.hub-sec{margin-bottom:20px}
|
|
534
|
-
.hub-sec:last-child{margin-bottom:0}
|
|
535
|
-
#hub-search-input{width:100%;background:var(--card);border:1px solid var(--hairline);border-radius:10px;
|
|
536
|
-
color:var(--ink);font-size:12.5px;padding:9px 12px;outline:none;transition:border-color .2s;margin-bottom:10px}
|
|
537
|
-
#hub-search-input:focus{border-color:rgba(10,132,255,.5)}
|
|
538
|
-
#hub-search-input::placeholder{color:var(--dimmer)}
|
|
539
|
-
.hub-search-body{min-height:0}
|
|
540
|
-
.hub-empty{color:var(--dim);font-size:12.5px;padding:10px 4px;line-height:1.5}
|
|
541
|
-
/* row language shared by Downloaded + Search results */
|
|
542
|
-
.hub-row{display:flex;align-items:flex-start;gap:10px;padding:10px 11px;border-radius:12px;
|
|
543
|
-
border:1px solid var(--hairline-soft);margin-bottom:8px;transition:border-color .15s var(--ease)}
|
|
544
|
-
.hub-row:last-child{margin-bottom:0}
|
|
545
|
-
.hub-fit-dot{width:9px;height:9px;border-radius:50%;flex:0 0 auto;margin-top:5px}
|
|
546
|
-
.hub-fit-dot.green{background:var(--green);box-shadow:0 0 7px var(--green)}
|
|
547
|
-
.hub-fit-dot.yellow{background:var(--yellow);box-shadow:0 0 7px var(--yellow)}
|
|
548
|
-
.hub-fit-dot.red{background:var(--red)}
|
|
549
|
-
.hub-row-main{flex:1 1 auto;min-width:0}
|
|
550
|
-
.hub-row-name{font-size:13px;font-weight:600;color:var(--ink);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
|
551
|
-
.hub-row-meta{font-size:11px;color:var(--dimmer);margin-top:2px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
|
552
|
-
.hub-row-actions{flex:0 0 auto;display:flex;align-items:center;gap:6px}
|
|
553
|
-
.hub-row-actions button{appearance:none;cursor:pointer;background:var(--card);border:1px solid var(--hairline);
|
|
554
|
-
color:var(--dim);border-radius:8px;padding:5px 11px;font-size:11.5px;font-weight:600;white-space:nowrap;
|
|
555
|
-
transition:color .18s var(--ease),border-color .18s var(--ease),background .18s var(--ease)}
|
|
556
|
-
.hub-row-actions button:hover:not(:disabled){color:var(--ink);border-color:rgba(10,132,255,.5);background:rgba(10,132,255,.08)}
|
|
557
|
-
.hub-row-actions button:disabled{opacity:.5;cursor:default}
|
|
558
|
-
.hub-dl-tag{font:11px var(--mono);color:var(--blue);white-space:nowrap}
|
|
559
|
-
.hub-dl-tag.done{color:var(--green)}
|
|
560
|
-
.hub-dl-tag.error{color:var(--red)}
|
|
561
|
-
/* restart-command strip (POST /api/hub/serve's honest restart_required
|
|
562
|
-
outcome) — same code-panel visual language as .mp-cmd, but persistent
|
|
563
|
-
(not a toast) since it needs to stay put long enough to copy. */
|
|
564
|
-
.hub-restart-strip{display:none}
|
|
565
|
-
.hub-restart-strip.show{display:block;margin-bottom:16px;padding:12px 14px;border-radius:12px;
|
|
566
|
-
background:rgba(10,132,255,.08);border:1px solid rgba(10,132,255,.3)}
|
|
567
|
-
.hub-restart-note{font-size:12px;color:var(--ink);line-height:1.5;margin-bottom:8px}
|
|
568
|
-
.hub-cmd{display:flex;align-items:center;gap:8px;background:var(--code-bg-soft);border-radius:8px;
|
|
569
|
-
padding:7px 10px;overflow-x:auto}
|
|
570
|
-
.hub-cmd code{font:12px var(--mono);color:var(--ink);white-space:nowrap;flex:1 1 auto}
|
|
571
|
-
.hub-copy-btn{appearance:none;cursor:pointer;background:var(--card);border:1px solid var(--hairline);
|
|
572
|
-
color:var(--dim);border-radius:6px;padding:3px 9px;font-size:11px;font-weight:600;flex:0 0 auto;white-space:nowrap}
|
|
573
|
-
.hub-copy-btn:hover{color:var(--ink);border-color:var(--hairline-strong)}
|
|
574
|
-
.hub-offline-note{font-size:11.5px;color:var(--dim);line-height:1.5;margin-top:4px;padding-top:14px;border-top:1px solid var(--hairline-soft)}
|
|
575
|
-
|
|
576
|
-
/* ═══════════════════ app shell / routing ═══════════════════ */
|
|
577
|
-
#app{position:fixed;inset:var(--nav-h) 0 0 0;z-index:1}
|
|
578
|
-
section[data-route]{display:none;position:absolute;inset:0}
|
|
579
|
-
section[data-route].active{display:block;animation:fade .5s var(--ease)}
|
|
580
|
-
@keyframes fade{from{opacity:0;transform:translateY(8px)}to{opacity:1;transform:none}}
|
|
581
|
-
|
|
582
|
-
/* a scrolling content column used by the doc-style sections */
|
|
583
|
-
.scroller{height:100%;overflow-y:auto}
|
|
584
|
-
.wrap{max-width:1180px;margin:0 auto;padding:46px 28px 120px}
|
|
585
|
-
.wrap.narrow{max-width:860px}
|
|
586
|
-
|
|
587
|
-
/* ─── shared typographic bits ─── */
|
|
588
|
-
.eyebrow{font-size:12.5px;font-weight:600;letter-spacing:.22em;text-transform:uppercase;color:var(--dim);margin-bottom:14px}
|
|
589
|
-
h1.title{font-size:clamp(38px,5.4vw,68px);font-weight:700;letter-spacing:-.032em;line-height:1.02}
|
|
590
|
-
.lead{font-size:clamp(15px,1.4vw,19px);line-height:1.55;color:var(--dim);max-width:66ch;margin-top:18px}
|
|
591
|
-
.lead strong{color:var(--ink);font-weight:600}
|
|
592
|
-
h2{font-size:clamp(26px,3.6vw,46px);font-weight:700;letter-spacing:-.026em;line-height:1.06}
|
|
593
|
-
h2 .soft{color:var(--dimmer)}
|
|
594
|
-
h3.kicker{font-size:12.5px;font-weight:600;letter-spacing:.14em;text-transform:uppercase;color:var(--dim);margin-bottom:14px}
|
|
595
|
-
|
|
596
|
-
/* ─── reveals ─── */
|
|
597
|
-
[data-reveal]{opacity:0;transform:translateY(22px);
|
|
598
|
-
transition:opacity .8s var(--ease),transform .8s var(--ease);transition-delay:calc(var(--d,0)*1ms)}
|
|
599
|
-
.lit [data-reveal]{opacity:1;transform:none}
|
|
600
|
-
@media (prefers-reduced-motion:reduce){
|
|
601
|
-
[data-reveal]{transition:none;opacity:1;transform:none}
|
|
602
|
-
*{scroll-behavior:auto}
|
|
603
|
-
/* Ambient/decorative infinite-loop animations (plan §7): shimmer
|
|
604
|
-
skeleton sweep, download-pulse dot, streaming cursor blink, running-
|
|
605
|
-
tool status dot. These loop forever by design and carry no state —
|
|
606
|
-
freezing them (not hiding) keeps the element visible+legible without
|
|
607
|
-
motion. One-shot transitions (fade-in on route change, toast slide-in,
|
|
608
|
-
popover open) are left alone; they're brief and convey state change,
|
|
609
|
-
not ambient decoration. */
|
|
610
|
-
#nav-download .dot,.shimmer,.cursor,.tool.running .sdot{animation:none!important}
|
|
611
|
-
.shimmer{color:inherit!important;background:var(--card)}
|
|
612
|
-
}
|
|
613
|
-
|
|
614
|
-
/* ─── cards ─── */
|
|
615
|
-
.grid{display:grid;gap:14px}
|
|
616
|
-
.g4{grid-template-columns:repeat(auto-fit,minmax(240px,1fr))}
|
|
617
|
-
.card{background:var(--card);border:1px solid var(--hairline);border-radius:20px;
|
|
618
|
-
padding:24px 26px;transition:background .3s var(--ease),border-color .3s var(--ease)}
|
|
619
|
-
.card:hover{background:var(--card-hover)}
|
|
620
|
-
.card h3{font-size:12.5px;font-weight:600;letter-spacing:.14em;text-transform:uppercase;color:var(--dim);margin-bottom:14px}
|
|
621
|
-
.stat{font-size:clamp(28px,3.2vw,42px);font-weight:700;letter-spacing:-.025em;line-height:1.05}
|
|
622
|
-
.stat small{font-size:.42em;font-weight:600;color:var(--dim);letter-spacing:0;margin-left:6px}
|
|
623
|
-
.card .cap{margin-top:8px;font-size:13.5px;color:var(--dim);line-height:1.5}
|
|
624
|
-
.kv{display:flex;justify-content:space-between;gap:10px;padding:5px 0;font-size:14.5px;border-bottom:1px solid var(--hairline-soft)}
|
|
625
|
-
.kv:last-child{border-bottom:none}
|
|
626
|
-
.kv b{font-weight:500;color:var(--dim)}
|
|
627
|
-
.kv span{font-weight:600}
|
|
628
|
-
.meter{height:5px;border-radius:3px;background:rgba(255,255,255,.09);margin-top:16px;overflow:hidden}
|
|
629
|
-
.meter i{display:block;height:100%;border-radius:3px;width:0%;
|
|
630
|
-
background:linear-gradient(90deg,var(--cyan),var(--blue));transition:width .8s var(--ease)}
|
|
631
|
-
.meter.warm i{background:linear-gradient(90deg,var(--orange),var(--pink))}
|
|
632
|
-
.meter.green i{background:linear-gradient(90deg,#a8ff9e,var(--green))}
|
|
633
|
-
|
|
634
|
-
/* ─── tables ─── */
|
|
635
|
-
table{width:100%;border-collapse:collapse;font-size:14.5px}
|
|
636
|
-
th,td{text-align:right;padding:13px 16px;border-bottom:1px solid var(--hairline-soft)}
|
|
637
|
-
th:first-child,td:first-child{text-align:left}
|
|
638
|
-
td:not(:first-child),th:not(:first-child){white-space:nowrap}
|
|
639
|
-
th{font-size:11.5px;font-weight:600;letter-spacing:.13em;text-transform:uppercase;color:var(--dim)}
|
|
640
|
-
tr:last-child td{border-bottom:none}
|
|
641
|
-
tbody tr{transition:background .2s}
|
|
642
|
-
tbody tr:hover{background:rgba(255,255,255,.04)}
|
|
643
|
-
td.fit-yes{color:var(--green);font-weight:600}
|
|
644
|
-
td.fit-no{color:var(--dimmer)}
|
|
645
|
-
tr.you td{background:rgba(10,132,255,.10)}
|
|
646
|
-
tr.you td:first-child{border-left:3px solid var(--blue);border-radius:2px}
|
|
647
|
-
.tablecard{padding:8px 6px}
|
|
648
|
-
|
|
649
|
-
/* ─── forms (used by all three wizards) ─── */
|
|
650
|
-
.field{margin-bottom:18px}
|
|
651
|
-
.field>label{display:block;font-size:13px;font-weight:600;color:var(--ink);margin-bottom:8px;letter-spacing:-.01em}
|
|
652
|
-
.field .hint{font-size:12.5px;color:var(--dim);margin-top:7px;line-height:1.5}
|
|
653
|
-
input[type=text],input[type=number],input[type=password],textarea,select{
|
|
654
|
-
width:100%;background:var(--card);border:1px solid var(--hairline);border-radius:12px;
|
|
655
|
-
color:var(--ink);font:14.5px/1.5 inherit;padding:11px 14px;outline:none;
|
|
656
|
-
transition:border-color .22s var(--ease),background .22s var(--ease)}
|
|
657
|
-
input::placeholder,textarea::placeholder{color:var(--dimmer)}
|
|
658
|
-
input:focus,textarea:focus,select:focus{border-color:rgba(10,132,255,.55);background:var(--card-hover)}
|
|
659
|
-
textarea{resize:vertical;min-height:96px;font-family:var(--mono);font-size:13px}
|
|
660
|
-
select{appearance:none;-webkit-appearance:none;cursor:pointer;
|
|
661
|
-
background-image:url("data:image/svg+xml;charset=US-ASCII,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8'%3E%3Cpath fill='%2386868b' d='M1 1l5 5 5-5'/%3E%3C/svg%3E");
|
|
662
|
-
background-repeat:no-repeat;background-position:right 14px center;padding-right:34px}
|
|
663
|
-
.hpgrid{display:grid;grid-template-columns:1fr 1fr;gap:14px}
|
|
664
|
-
.hpgrid .field{margin-bottom:0}
|
|
665
|
-
input[type=range]{width:100%;accent-color:var(--blue);background:transparent;cursor:pointer;height:24px}
|
|
666
|
-
|
|
667
|
-
/* segmented control */
|
|
668
|
-
.seg{display:inline-flex;background:var(--card);border:1px solid var(--hairline);border-radius:12px;padding:3px;gap:3px}
|
|
669
|
-
.seg button{appearance:none;background:none;border:none;cursor:pointer;color:var(--dim);
|
|
670
|
-
font:600 13.5px/1 inherit;padding:9px 18px;border-radius:9px;transition:all .22s var(--ease)}
|
|
671
|
-
.seg button.on{background:rgba(255,255,255,.12);color:var(--ink)}
|
|
672
|
-
.seg button:hover:not(.on){color:var(--ink)}
|
|
673
|
-
|
|
674
|
-
/* buttons */
|
|
675
|
-
.btn{appearance:none;border:none;cursor:pointer;font:600 14px/1 inherit;border-radius:13px;
|
|
676
|
-
padding:0 22px;height:46px;display:inline-flex;align-items:center;justify-content:center;gap:8px;
|
|
677
|
-
transition:filter .2s,opacity .2s,background .22s var(--ease)}
|
|
678
|
-
.btn.primary{background:linear-gradient(120deg,var(--blue),var(--purple));color:#fff}
|
|
679
|
-
.btn.primary:hover{filter:brightness(1.13)}
|
|
680
|
-
.btn.ghost{background:var(--card);border:1px solid var(--hairline);color:var(--ink)}
|
|
681
|
-
.btn.ghost:hover{background:var(--card-hover)}
|
|
682
|
-
.btn:disabled{filter:grayscale(.7) brightness(.55);cursor:default;opacity:.7}
|
|
683
|
-
.btn.sm{height:38px;padding:0 16px;font-size:13px;border-radius:11px}
|
|
684
|
-
.btnrow{display:flex;gap:10px;margin-top:24px;flex-wrap:wrap;align-items:center}
|
|
685
|
-
|
|
686
|
-
/* flash / status banners */
|
|
687
|
-
.flash{border-radius:14px;padding:13px 16px;font-size:14px;line-height:1.5;border:1px solid;margin-top:14px}
|
|
688
|
-
.flash.ok{background:rgba(48,209,88,.10);border-color:rgba(48,209,88,.35);color:#b8f5c4}
|
|
689
|
-
.flash.warn{background:rgba(255,159,10,.10);border-color:rgba(255,159,10,.35);color:#ffe0a8}
|
|
690
|
-
.flash.err{background:rgba(255,69,58,.10);border-color:rgba(255,69,58,.35);color:#ffb8b3}
|
|
691
|
-
.flash code{background:rgba(0,0,0,.3)}
|
|
692
|
-
.flash strong{color:var(--ink)}
|
|
693
|
-
|
|
694
|
-
.soon{display:inline-flex;align-items:center;gap:6px;font-size:11px;font-weight:600;letter-spacing:.06em;
|
|
695
|
-
text-transform:uppercase;color:var(--dimmer);border:1px solid var(--hairline-soft);border-radius:999px;padding:3px 10px}
|
|
696
|
-
|
|
697
|
-
/* step indicator */
|
|
698
|
-
.steps{display:flex;flex-wrap:wrap;gap:6px;margin:6px 0 30px;font:600 12px var(--mono);
|
|
699
|
-
text-transform:uppercase;letter-spacing:.05em;color:var(--dimmer)}
|
|
700
|
-
.steps .s{display:inline-flex;align-items:center;gap:8px}
|
|
701
|
-
.steps .s .n{display:inline-flex;align-items:center;justify-content:center;width:22px;height:22px;border-radius:50%;
|
|
702
|
-
border:1px solid var(--hairline);font-size:11px;color:var(--dim);transition:all .3s var(--ease)}
|
|
703
|
-
.steps .s.done .n{background:rgba(255,255,255,.10);color:var(--ink);border-color:transparent}
|
|
704
|
-
.steps .s.cur .n{background:linear-gradient(120deg,var(--blue),var(--purple));color:#fff;border-color:transparent}
|
|
705
|
-
.steps .s.cur{color:var(--ink)}
|
|
706
|
-
.steps .s.done{color:var(--dim)}
|
|
707
|
-
.steps .arrow{color:var(--dimmer);margin:0 2px}
|
|
708
|
-
|
|
709
|
-
/* progress + log shared by every job stream */
|
|
710
|
-
.progressbar{height:8px;background:rgba(255,255,255,.09);border-radius:5px;overflow:hidden}
|
|
711
|
-
.progressbar i{display:block;height:100%;width:0%;border-radius:5px;
|
|
712
|
-
background:linear-gradient(90deg,var(--cyan),var(--blue));transition:width .5s var(--ease)}
|
|
713
|
-
.progressbar.green i{background:linear-gradient(90deg,#a8ff9e,var(--green))}
|
|
714
|
-
.progmeta{display:flex;justify-content:space-between;font-size:12.5px;color:var(--dim);margin-top:8px}
|
|
715
|
-
.logbox{margin-top:18px;background:var(--code-bg-soft);border:1px solid var(--hairline-soft);border-radius:14px;
|
|
716
|
-
padding:14px 16px;max-height:340px;overflow-y:auto;font:11.5px/1.55 var(--mono);color:rgba(245,245,247,.7);white-space:pre-wrap;word-break:break-word}
|
|
717
|
-
.logbox:empty::before{content:"waiting for output…";color:var(--dimmer)}
|
|
718
|
-
|
|
719
|
-
/* shimmer skeletons */
|
|
720
|
-
.shimmer{background:linear-gradient(100deg,rgba(255,255,255,.04) 30%,rgba(255,255,255,.10) 50%,rgba(255,255,255,.04) 70%);
|
|
721
|
-
background-size:200% 100%;animation:sh 1.4s ease-in-out infinite;border-radius:8px;color:transparent!important}
|
|
722
|
-
@keyframes sh{0%{background-position:200% 0}100%{background-position:-200% 0}}
|
|
723
|
-
|
|
724
|
-
/* toasts */
|
|
725
|
-
#toasts{position:fixed;bottom:24px;left:50%;transform:translateX(-50%);z-index:100;
|
|
726
|
-
display:flex;flex-direction:column;gap:10px;align-items:center;pointer-events:none}
|
|
727
|
-
.toast{background:var(--overlay-panel);backdrop-filter:blur(20px);border:1px solid var(--hairline);
|
|
728
|
-
border-radius:13px;padding:12px 18px;font-size:14px;font-weight:500;box-shadow:0 16px 50px rgba(0,0,0,.35);
|
|
729
|
-
animation:toastIn .4s var(--ease);max-width:min(560px,90vw)}
|
|
730
|
-
.toast.err{border-color:rgba(255,69,58,.5)}
|
|
731
|
-
.toast.ok{border-color:rgba(48,209,88,.5)}
|
|
732
|
-
@keyframes toastIn{from{opacity:0;transform:translateY(14px)}to{opacity:1;transform:none}}
|
|
733
|
-
|
|
734
|
-
/* App-aware assistant spotlight (plan §6.6, §9 Phase 3, beat matrix Axis
|
|
735
|
-
12) — NEVER-HIJACK overlay: pointer-events:none on the whole thing (the
|
|
736
|
-
highlighted control stays clickable straight through the ring/popover),
|
|
737
|
-
no focus trap, transform/opacity-only animation, auto-dismiss handled
|
|
738
|
-
in assistant.ts. Positioned in fixed viewport coordinates by JS
|
|
739
|
-
(getBoundingClientRect of the target), not CSS layout. */
|
|
740
|
-
#assistant-spotlight{position:fixed;inset:0;z-index:95;pointer-events:none;
|
|
741
|
-
opacity:0;transform:scale(.98);transition:opacity .22s var(--ease),transform .22s var(--ease)}
|
|
742
|
-
#assistant-spotlight.show{opacity:1;transform:none}
|
|
743
|
-
#assistant-spotlight .asr-ring{position:fixed;border-radius:12px;
|
|
744
|
-
box-shadow:0 0 0 3px var(--blue),0 0 0 9999px rgba(0,0,0,.32),0 0 24px rgba(10,132,255,.5);
|
|
745
|
-
transition:left .22s var(--ease),top .22s var(--ease),width .22s var(--ease),height .22s var(--ease)}
|
|
746
|
-
#assistant-spotlight .asr-pop{position:fixed;max-width:min(320px,80vw);background:var(--overlay-panel);
|
|
747
|
-
backdrop-filter:blur(20px);border:1px solid var(--hairline);border-radius:12px;padding:10px 14px;
|
|
748
|
-
font-size:13px;line-height:1.4;box-shadow:0 16px 50px rgba(0,0,0,.35)}
|
|
749
|
-
@media (prefers-reduced-motion:reduce){
|
|
750
|
-
#assistant-spotlight{transition:opacity .01ms;transform:none}
|
|
751
|
-
#assistant-spotlight .asr-ring{transition:none}
|
|
752
|
-
}
|
|
753
|
-
|
|
754
|
-
/* empty state */
|
|
755
|
-
.empty{text-align:center;color:var(--dim);padding:60px 20px}
|
|
756
|
-
.empty h3{font-size:18px;font-weight:600;color:var(--ink);margin-bottom:8px;letter-spacing:-.01em}
|
|
757
|
-
.empty p{font-size:14px;max-width:46ch;margin:0 auto}
|
|
758
|
-
|
|
759
|
-
/* ═══════════════════ CHAT ═══════════════════ */
|
|
760
|
-
#s-chat{display:none}
|
|
761
|
-
#s-chat.active{display:flex!important;flex-direction:row}
|
|
762
|
-
#chat-main{flex:1;min-width:0;display:flex;flex-direction:column}
|
|
763
|
-
/* recent-chats sidebar */
|
|
764
|
-
#chat-sidebar{flex:0 0 252px;display:flex;flex-direction:column;gap:8px;padding:14px 12px;
|
|
765
|
-
border-right:1px solid var(--hairline);overflow-y:auto;background:var(--card-2)}
|
|
766
|
-
#chat-new{width:100%;text-align:left;padding:10px 13px;border-radius:11px;cursor:pointer;
|
|
767
|
-
background:var(--card);border:1px solid var(--hairline);color:var(--ink);font-size:13.5px;font-weight:600;
|
|
768
|
-
transition:background .15s,border-color .15s}
|
|
769
|
-
#chat-new:hover{background:var(--card-hover);border-color:rgba(10,132,255,.5)}
|
|
770
|
-
/* Session search — trivial client-side substring filter over titles
|
|
771
|
-
(plan §5.7 / §9 Phase 1 item 4). No server change: SessionListItem
|
|
772
|
-
carries no preview text today, so title-only is the honest scope. */
|
|
773
|
-
#chat-sess-search{width:100%;background:var(--card);border:1px solid var(--hairline);border-radius:10px;
|
|
774
|
-
color:var(--ink);font-size:12.5px;padding:8px 11px;outline:none;transition:border-color .2s}
|
|
775
|
-
#chat-sess-search:focus{border-color:rgba(10,132,255,.5)}
|
|
776
|
-
#chat-sess-search::placeholder{color:var(--dimmer)}
|
|
777
|
-
.sesslist{display:flex;flex-direction:column;gap:2px;margin-top:4px}
|
|
778
|
-
.sess{position:relative;padding:9px 50px 9px 11px;border-radius:10px;cursor:pointer;border:1px solid transparent}
|
|
779
|
-
.sess:hover{background:var(--card)}
|
|
780
|
-
.sess.active{background:var(--card);border-color:var(--hairline)}
|
|
781
|
-
.sess.sess-hidden{display:none}
|
|
782
|
-
.sess .stitle{font-size:13px;color:var(--ink);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
|
|
783
|
-
.sess .smeta{font-size:11px;color:var(--dim);margin-top:2px;display:flex;gap:5px;align-items:center;white-space:nowrap}
|
|
784
|
-
.sess .sbtn{position:absolute;top:7px;width:20px;height:20px;border:none;background:transparent;color:var(--dim);
|
|
785
|
-
cursor:pointer;border-radius:6px;opacity:0;font-size:12px;line-height:1;transition:opacity .12s,background .12s,color .12s}
|
|
786
|
-
.sess:hover .sbtn{opacity:.85}
|
|
787
|
-
.sess .sfork{right:28px}
|
|
788
|
-
.sess .sdel{right:6px}
|
|
789
|
-
.sess .sfork:hover{background:var(--card-hover);color:var(--ink);opacity:1}
|
|
790
|
-
.sess .sdel:hover{background:rgba(255,69,58,.18);color:var(--red);opacity:1}
|
|
791
|
-
.sessempty{color:var(--dim);font-size:12px;padding:8px 11px;line-height:1.5}
|
|
792
|
-
|
|
793
|
-
/* ─── mobile drawer (plan §5.7/§7/§9 Phase 1): the sidebar becomes a
|
|
794
|
-
slide-over panel on narrow viewports instead of vanishing outright. ─── */
|
|
795
|
-
#chat-hamburger{display:none}
|
|
796
|
-
@media (max-width:760px){
|
|
797
|
-
#chat-hamburger{display:inline-flex}
|
|
798
|
-
#chat-sidebar{
|
|
799
|
-
position:fixed;top:var(--nav-h);bottom:0;left:0;width:min(84vw,320px);z-index:120;
|
|
800
|
-
border-right:1px solid var(--hairline);
|
|
801
|
-
transform:translateX(-100%);transition:transform .28s var(--ease);
|
|
802
|
-
box-shadow:0 0 0 rgba(0,0,0,0);
|
|
803
|
-
}
|
|
804
|
-
#chat-sidebar.drawer-open{transform:translateX(0);box-shadow:24px 0 60px rgba(0,0,0,.45)}
|
|
805
|
-
#chat-drawer-backdrop{display:none;position:fixed;inset:var(--nav-h) 0 0 0;z-index:110;
|
|
806
|
-
background:var(--overlay-scrim);backdrop-filter:blur(4px);-webkit-backdrop-filter:blur(4px)}
|
|
807
|
-
#chat-drawer-backdrop.open{display:block;animation:fade .2s var(--ease)}
|
|
808
|
-
}
|
|
809
|
-
@media (max-width:760px) and (prefers-reduced-motion:reduce){
|
|
810
|
-
#chat-sidebar{transition:none}
|
|
811
|
-
}
|
|
812
|
-
#chat-scroll{flex:1;overflow-y:auto}
|
|
813
|
-
#chat-thread{max-width:840px;margin:0 auto;padding:30px 22px 16px;display:flex;flex-direction:column;gap:18px}
|
|
814
|
-
.msg{display:flex;flex-direction:column;gap:6px;max-width:90%}
|
|
815
|
-
.msg .who{font-size:11px;font-weight:600;letter-spacing:.12em;text-transform:uppercase;color:var(--dimmer)}
|
|
816
|
-
.msg .bubble{border-radius:16px;padding:13px 17px;line-height:1.6;word-break:break-word;overflow-wrap:anywhere}
|
|
817
|
-
.msg.user{align-self:flex-end;align-items:flex-end}
|
|
818
|
-
.msg.user .bubble{background:linear-gradient(120deg,rgba(10,132,255,.26),rgba(94,92,230,.26));border:1px solid rgba(10,132,255,.35)}
|
|
819
|
-
.msg.assistant{align-self:flex-start;width:100%;max-width:840px}
|
|
820
|
-
.msg.assistant .bubble{background:var(--card);border:1px solid var(--hairline)}
|
|
821
|
-
.msg .meta{font-size:11.5px;color:var(--dimmer);font-variant-numeric:tabular-nums}
|
|
822
|
-
.msg .meta b{color:var(--green);font-weight:600}
|
|
823
|
-
/* Message actions (plan §5.2): a quiet hover/focus row under each message,
|
|
824
|
-
same visual language as the code-block copy button (.cbcopy) — dim by
|
|
825
|
-
default, brightens on hover, never shouts. Kept visible via :focus-within
|
|
826
|
-
too so keyboard users can tab to the buttons without hovering. */
|
|
827
|
-
.msg-actions{display:flex;gap:6px;opacity:0;transition:opacity .15s;margin-top:2px}
|
|
828
|
-
.msg:hover .msg-actions,.msg:focus-within .msg-actions,.msg-actions.pinned{opacity:1}
|
|
829
|
-
.msg.user .msg-actions{align-self:flex-end}
|
|
830
|
-
.maction{font:11px/1 var(--mono);color:var(--dim);background:transparent;border:1px solid var(--hairline);
|
|
831
|
-
border-radius:6px;padding:4px 9px;cursor:pointer;transition:color .15s,border-color .15s}
|
|
832
|
-
.maction:hover{color:var(--ink);border-color:var(--dim)}
|
|
833
|
-
.maction:disabled{opacity:.4;cursor:default}
|
|
834
|
-
.maction:disabled:hover{color:var(--dim);border-color:var(--hairline)}
|
|
835
|
-
/* Edit-and-resend as sibling branch: ChatGPT/Claude's `< i/n >` linear
|
|
836
|
-
toggle (plan §5.2 explicitly rules out a tree view this phase). Only
|
|
837
|
-
rendered on the LAST user message, and only when count > 1. */
|
|
838
|
-
.sib-toggle{display:flex;align-items:center;gap:5px;font:11px/1 var(--mono);color:var(--dimmer)}
|
|
839
|
-
.sib-toggle button{background:transparent;border:1px solid var(--hairline);border-radius:6px;color:var(--dim);
|
|
840
|
-
cursor:pointer;padding:2px 7px;line-height:1.4}
|
|
841
|
-
.sib-toggle button:hover:not(:disabled){color:var(--ink);border-color:var(--dim)}
|
|
842
|
-
.sib-toggle button:disabled{opacity:.35;cursor:default}
|
|
843
|
-
.msg-edit-box{width:100%;box-sizing:border-box;background:var(--card-2);border:1px solid var(--hairline);
|
|
844
|
-
border-radius:10px;padding:9px 11px;color:var(--ink);font:inherit;line-height:1.5;resize:vertical;min-height:44px}
|
|
845
|
-
.msg-edit-actions{display:flex;gap:8px;margin-top:6px;justify-content:flex-end}
|
|
846
|
-
.thinkbox{margin:0 0 12px;border:1px solid rgba(10,132,255,.18);border-radius:12px;background:rgba(10,132,255,.06);overflow:hidden}
|
|
847
|
-
.thinkbox summary{cursor:pointer;padding:7px 10px;font-size:12px;font-weight:600;color:var(--dim);user-select:none}
|
|
848
|
-
.thinkbody{padding:0 12px 10px;color:var(--dim);font-size:.92em;white-space:pre-wrap}
|
|
849
|
-
/* markdown-ish rendering — shared by chat bubbles AND the memory panel's
|
|
850
|
-
article view (.mem-article-render renders vault/Reference docs through
|
|
851
|
-
the same mdToHtml; without this scope the panel's code fences lost
|
|
852
|
-
their header chrome — "shCopy" mash, 2026-07-07 visual QA). */
|
|
853
|
-
:is(.bubble,.mem-article-render) p{margin:0 0 10px}:is(.bubble,.mem-article-render) p:last-child{margin-bottom:0}
|
|
854
|
-
:is(.bubble,.mem-article-render) ul,:is(.bubble,.mem-article-render) ol{margin:0 0 10px;padding-left:22px}
|
|
855
|
-
:is(.bubble,.mem-article-render) li{margin:3px 0}
|
|
856
|
-
:is(.bubble,.mem-article-render) strong{font-weight:700;color:var(--ink)}
|
|
857
|
-
:is(.bubble,.mem-article-render) em{font-style:italic}
|
|
858
|
-
:is(.bubble,.mem-article-render) code{font-family:var(--mono);font-size:.86em;background:rgba(127,127,127,.18);padding:2px 7px;border-radius:6px}
|
|
859
|
-
:is(.bubble,.mem-article-render) pre{background:var(--code-bg);color:var(--code-ink);border:1px solid var(--hairline-soft);border-radius:12px;
|
|
860
|
-
padding:13px 15px;overflow-x:auto;margin:10px 0;font:12.5px/1.6 var(--mono)}
|
|
861
|
-
:is(.bubble,.mem-article-render) pre code{background:none;padding:0;font-size:inherit;color:inherit}
|
|
862
|
-
:is(.bubble,.mem-article-render) h1,:is(.bubble,.mem-article-render) h2,:is(.bubble,.mem-article-render) h3,:is(.bubble,.mem-article-render) h4,:is(.bubble,.mem-article-render) h5,:is(.bubble,.mem-article-render) h6{font-weight:700;margin:14px 0 7px;letter-spacing:-.01em;line-height:1.3}
|
|
863
|
-
:is(.bubble,.mem-article-render) h1{font-size:1.28em}:is(.bubble,.mem-article-render) h2{font-size:1.16em}:is(.bubble,.mem-article-render) h3{font-size:1.06em}
|
|
864
|
-
:is(.bubble,.mem-article-render) h4,:is(.bubble,.mem-article-render) h5,:is(.bubble,.mem-article-render) h6{font-size:1em;color:var(--dim)}
|
|
865
|
-
:is(.bubble,.mem-article-render) h1:first-child,:is(.bubble,.mem-article-render) h2:first-child,:is(.bubble,.mem-article-render) h3:first-child{margin-top:0}
|
|
866
|
-
:is(.bubble,.mem-article-render) a{color:var(--cyan);text-decoration:none;border-bottom:1px solid rgba(100,210,255,.34)}
|
|
867
|
-
:is(.bubble,.mem-article-render) a:hover{border-bottom-color:var(--cyan)}
|
|
868
|
-
:is(.bubble,.mem-article-render) del{opacity:.55}
|
|
869
|
-
:is(.bubble,.mem-article-render) blockquote{margin:9px 0;padding:3px 14px;border-left:3px solid var(--hairline);color:var(--dim)}
|
|
870
|
-
:is(.bubble,.mem-article-render) blockquote p:last-child{margin-bottom:0}
|
|
871
|
-
:is(.bubble,.mem-article-render) hr{border:none;border-top:1px solid var(--hairline);margin:15px 0}
|
|
872
|
-
:is(.bubble,.mem-article-render) li.task{list-style:none;margin-left:-18px}
|
|
873
|
-
:is(.bubble,.mem-article-render) li.task input{margin-right:7px;accent-color:var(--blue);vertical-align:-1px}
|
|
874
|
-
:is(.bubble,.mem-article-render) .md-tablewrap{overflow-x:auto;margin:11px 0}
|
|
875
|
-
:is(.bubble,.mem-article-render) table.md-table{width:100%;border-collapse:collapse;font-size:.92em}
|
|
876
|
-
:is(.bubble,.mem-article-render) table.md-table th,:is(.bubble,.mem-article-render) table.md-table td{border:1px solid var(--hairline-soft);padding:6px 11px;text-align:left}
|
|
877
|
-
:is(.bubble,.mem-article-render) table.md-table th{background:rgba(255,255,255,.05);font-weight:600;color:var(--ink)}
|
|
878
|
-
:is(.bubble,.mem-article-render) .codeblock{margin:11px 0;border:1px solid var(--hairline-soft);border-radius:12px;overflow:hidden;background:var(--code-bg);color:var(--code-ink)}
|
|
879
|
-
:is(.bubble,.mem-article-render) .cbhead{display:flex;align-items:center;justify-content:space-between;gap:10px;padding:5px 9px 5px 13px;background:rgba(255,255,255,.04);border-bottom:1px solid var(--hairline-soft)}
|
|
880
|
-
:is(.bubble,.mem-article-render) .cblang{font:11px/1 var(--mono);color:rgba(245,245,247,.65);letter-spacing:.02em}
|
|
881
|
-
:is(.bubble,.mem-article-render) .cbcopy{font:11px/1 var(--mono);color:rgba(245,245,247,.65);background:transparent;border:1px solid rgba(255,255,255,.2);border-radius:6px;padding:4px 10px;cursor:pointer;transition:color .15s,border-color .15s}
|
|
882
|
-
:is(.bubble,.mem-article-render) .cbcopy:hover{color:var(--code-ink);border-color:rgba(255,255,255,.4)}
|
|
883
|
-
:is(.bubble,.mem-article-render) .codeblock pre{margin:0;border:none;border-radius:0;background:transparent;color:inherit}
|
|
884
|
-
/* Canvas v1 (plan §9 Phase 3, beat matrix Axis 2): Preview|Source toggle
|
|
885
|
-
for html/svg fences, sharing .cbhead with the existing lang tag + Copy
|
|
886
|
-
button. .cbhead keeps lang pinned left; the toggle+copy group sits
|
|
887
|
-
right via margin-left:auto on the first right-side element instead of
|
|
888
|
-
space-between, since space-between with 3 children would push the
|
|
889
|
-
toggle to the dead center of the bar. */
|
|
890
|
-
:is(.bubble,.mem-article-render) .cbhead{justify-content:flex-start}
|
|
891
|
-
:is(.bubble,.mem-article-render) .cbtoggle{display:inline-flex;gap:2px;margin-left:auto;padding:2px;border-radius:7px;background:rgba(255,255,255,.05)}
|
|
892
|
-
:is(.bubble,.mem-article-render) .cbview{font:11px/1 var(--mono);color:rgba(245,245,247,.55);background:transparent;border:none;
|
|
893
|
-
border-radius:5px;padding:4px 9px;cursor:pointer;transition:color .15s,background .15s}
|
|
894
|
-
:is(.bubble,.mem-article-render) .cbview:hover{color:var(--code-ink)}
|
|
895
|
-
:is(.bubble,.mem-article-render) .cbview.active{color:var(--code-ink);background:rgba(255,255,255,.14)}
|
|
896
|
-
/* has-canvas keeps Copy visually last, right after the toggle group. */
|
|
897
|
-
:is(.bubble,.mem-article-render) .codeblock.has-canvas .cbcopy{margin-left:2px}
|
|
898
|
-
:is(.bubble,.mem-article-render) .cbcanvas{position:relative;background:#fff}
|
|
899
|
-
:is(.bubble,.mem-article-render) .cbframe{display:block;width:100%;height:420px;min-height:160px;max-height:80vh;
|
|
900
|
-
border:none;resize:vertical;overflow:auto;background:#fff}
|
|
901
|
-
/* No layout shift on toggle: pre and cbcanvas are simply swapped via
|
|
902
|
-
[hidden] — the codeblock's own border/rounding stays put, and the
|
|
903
|
-
iframe's own default height (420px) plus resize:vertical means the
|
|
904
|
-
block doesn't jump when Preview first opens vs. later re-opens. */
|
|
905
|
-
.cursor{display:inline-block;width:8px;height:17px;vertical-align:-3px;border-radius:2px;
|
|
906
|
-
background:linear-gradient(180deg,var(--pink),var(--purple));animation:blink 1s steps(2) infinite}
|
|
907
|
-
@keyframes blink{50%{opacity:0}}
|
|
908
|
-
|
|
909
|
-
/* tool cards */
|
|
910
|
-
.tool{margin:10px 0;border:1px solid var(--hairline);border-radius:14px;overflow:hidden;background:var(--card-2)}
|
|
911
|
-
.tool .thead{display:flex;align-items:center;gap:10px;padding:11px 15px;cursor:pointer;user-select:none;transition:background .2s}
|
|
912
|
-
.tool .thead:hover{background:rgba(255,255,255,.04)}
|
|
913
|
-
.tool .ticon{width:26px;height:26px;border-radius:8px;display:flex;align-items:center;justify-content:center;
|
|
914
|
-
font-size:13px;background:rgba(94,92,230,.18);color:var(--cyan);flex:0 0 auto}
|
|
915
|
-
.tool .tname{font:600 13.5px var(--mono);color:var(--ink)}
|
|
916
|
-
.tool .targs{font:12px var(--mono);color:var(--dim);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;flex:1 1 auto}
|
|
917
|
-
.tool .tstat{font-size:11px;font-weight:600;letter-spacing:.06em;text-transform:uppercase;flex:0 0 auto;display:flex;align-items:center;gap:6px}
|
|
918
|
-
.tool .tstat .sdot{width:7px;height:7px;border-radius:50%}
|
|
919
|
-
.tool.running .tstat{color:var(--cyan)}.tool.running .sdot{background:var(--cyan);box-shadow:0 0 8px var(--cyan);animation:blink 1.1s infinite}
|
|
920
|
-
.tool.ok .tstat{color:var(--green)}.tool.ok .sdot{background:var(--green)}
|
|
921
|
-
.tool.fail .tstat{color:var(--red)}.tool.fail .sdot{background:var(--red)}
|
|
922
|
-
.tool .caret{color:var(--dimmer);transition:transform .25s var(--ease);flex:0 0 auto}
|
|
923
|
-
.tool.open .caret{transform:rotate(90deg)}
|
|
924
|
-
.tool .tbody{display:none;border-top:1px solid var(--hairline-soft);padding:12px 15px}
|
|
925
|
-
.tool.open .tbody{display:block}
|
|
926
|
-
.tool .blk{margin-bottom:10px}.tool .blk:last-child{margin-bottom:0}
|
|
927
|
-
.tool .blbl{font-size:10.5px;font-weight:600;letter-spacing:.1em;text-transform:uppercase;color:var(--dimmer);margin-bottom:5px}
|
|
928
|
-
.tool pre{background:var(--code-bg-soft);border-radius:9px;padding:10px 12px;overflow-x:auto;
|
|
929
|
-
font:12px/1.55 var(--mono);color:rgba(245,245,247,.7);white-space:pre-wrap;word-break:break-word;max-height:260px;overflow-y:auto}
|
|
930
|
-
|
|
931
|
-
/* approval dialog */
|
|
932
|
-
.approval{margin:12px 0;border:1px solid rgba(255,159,10,.45);border-radius:16px;overflow:hidden;
|
|
933
|
-
background:rgba(255,159,10,.06);animation:fade .4s var(--ease)}
|
|
934
|
-
.approval .ahead{display:flex;align-items:center;gap:10px;padding:13px 16px;border-bottom:1px solid rgba(255,159,10,.25)}
|
|
935
|
-
.approval .ahead .ai{width:28px;height:28px;border-radius:8px;display:flex;align-items:center;justify-content:center;
|
|
936
|
-
background:rgba(255,159,10,.2);color:var(--orange);font-size:15px}
|
|
937
|
-
.approval .ahead .at{font-weight:700;font-size:14px}
|
|
938
|
-
.approval .ahead .as{font:12px var(--mono);color:var(--orange);margin-left:auto}
|
|
939
|
-
.approval .abody{padding:13px 16px}
|
|
940
|
-
.approval pre{background:var(--code-bg);color:var(--code-ink);border-radius:10px;padding:12px 14px;overflow-x:auto;
|
|
941
|
-
font:12.5px/1.6 var(--mono);white-space:pre-wrap;word-break:break-word;max-height:280px;overflow-y:auto}
|
|
942
|
-
.approval .diffadd{color:var(--green)}
|
|
943
|
-
.approval .diffdel{color:var(--red)}
|
|
944
|
-
.approval .actions{display:flex;gap:10px;padding:0 16px 15px}
|
|
945
|
-
.approval .resolved{padding:11px 16px;font-size:13px;font-weight:600;display:flex;align-items:center;gap:8px}
|
|
946
|
-
.approval .resolved.allow{color:var(--green)}
|
|
947
|
-
.approval .resolved.deny{color:var(--red)}
|
|
948
|
-
/* Editable arguments (plan §5.4/§6.5, LM Studio's pattern): pre-filled
|
|
949
|
-
with the proposed args JSON; the diff (edit/write only) stays above as
|
|
950
|
-
a read-only visual aid so large content edits don't only exist as raw
|
|
951
|
-
JSON. Approve re-parses the textarea and ships the edited object. */
|
|
952
|
-
.approval .a-diff{margin-bottom:10px}
|
|
953
|
-
.approval textarea.a-args{width:100%;background:var(--code-bg);color:var(--code-ink);border:1px solid var(--hairline);
|
|
954
|
-
border-radius:10px;padding:12px 14px;font:12.5px/1.6 var(--mono);resize:vertical;min-height:64px;max-height:280px}
|
|
955
|
-
.approval textarea.a-args:focus{outline:none;border-color:var(--orange)}
|
|
956
|
-
.approval .a-argerr{margin-top:8px;font-size:12px;color:var(--red)}
|
|
957
|
-
.approval .a-always{display:flex;align-items:center;gap:9px;padding:0 16px 13px;font-size:12.5px;
|
|
958
|
-
color:var(--dim);cursor:pointer;font-weight:500}
|
|
959
|
-
.approval .a-always input{width:auto;margin:0;accent-color:var(--orange)}
|
|
960
|
-
|
|
961
|
-
/* ─── memory provenance chip (plan §5.4.2) — deliberately NOT the wrench
|
|
962
|
-
tool-card language: a citation, not a shell command. Quiet by default
|
|
963
|
-
(small pill, one line), expands to a lightweight card on click. ─── */
|
|
964
|
-
.memchip{margin:10px 0;border:1px solid rgba(191,90,242,.3);border-radius:999px;overflow:hidden;
|
|
965
|
-
background:rgba(191,90,242,.07);max-width:fit-content;transition:border-radius .18s var(--ease)}
|
|
966
|
-
.memchip.open{border-radius:14px;max-width:none}
|
|
967
|
-
.memchip.fail{border-color:rgba(255,69,58,.45);background:rgba(255,69,58,.06)}
|
|
968
|
-
.memchip.fail .mcicon{color:var(--red)}
|
|
969
|
-
.memchip .mchead{display:flex;align-items:center;gap:8px;padding:7px 14px;cursor:pointer;user-select:none}
|
|
970
|
-
.memchip .mcicon{font-size:12.5px;flex:0 0 auto;color:var(--purple)}
|
|
971
|
-
.memchip .mclabel{font-size:12.5px;color:var(--dim);flex:1 1 auto;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
|
|
972
|
-
.memchip .mclabel b{color:var(--ink);font-weight:600}
|
|
973
|
-
.memchip .mccaret{color:var(--dimmer);font-size:11px;transition:transform .2s var(--ease);flex:0 0 auto}
|
|
974
|
-
.memchip.open .mccaret{transform:rotate(90deg)}
|
|
975
|
-
.memchip .mcbody{display:none;border-top:1px solid rgba(191,90,242,.2);padding:11px 14px 12px}
|
|
976
|
-
.memchip.open .mcbody{display:block}
|
|
977
|
-
.memchip .mcbody pre{background:var(--code-bg-soft);border-radius:9px;padding:9px 11px;overflow-x:auto;
|
|
978
|
-
font:11.5px/1.5 var(--mono);color:rgba(245,245,247,.7);white-space:pre-wrap;word-break:break-word;max-height:200px;overflow-y:auto;margin-bottom:8px}
|
|
979
|
-
.memchip .mcopen{font:11px/1 var(--mono);color:var(--purple);background:transparent;border:1px solid rgba(191,90,242,.4);
|
|
980
|
-
border-radius:6px;padding:5px 10px;cursor:pointer;transition:background .15s}
|
|
981
|
-
.memchip .mcopen:hover{background:rgba(191,90,242,.14)}
|
|
982
|
-
|
|
983
|
-
/* ─── Chat-with-files RAG v1 Sources panel (plan §9 Phase 3, beat matrix
|
|
984
|
-
Axis 5) — deliberately close to the memchip's citation language above:
|
|
985
|
-
a collapsed "Sources · K" line under the reply that expands to
|
|
986
|
-
filename+snippet per retrieved chunk, plus [n] markers in the reply
|
|
987
|
-
text rendered as small round citation buttons that jump to their row. */
|
|
988
|
-
.sources{margin-top:11px;border:1px solid var(--hairline-soft);border-radius:12px;overflow:hidden;
|
|
989
|
-
background:rgba(255,255,255,.03)}
|
|
990
|
-
.sources summary{list-style:none;display:flex;align-items:center;gap:7px;padding:8px 13px;
|
|
991
|
-
cursor:pointer;user-select:none;font-size:12.5px;font-weight:600;color:var(--dim)}
|
|
992
|
-
.sources summary::-webkit-details-marker{display:none}
|
|
993
|
-
.sources summary::before{content:"›";display:inline-block;color:var(--dimmer);
|
|
994
|
-
transition:transform .2s var(--ease)}
|
|
995
|
-
.sources[open] summary::before{transform:rotate(90deg)}
|
|
996
|
-
.src-list{border-top:1px solid var(--hairline-soft);padding:9px 13px 11px;display:flex;flex-direction:column;gap:10px}
|
|
997
|
-
.src-row{border-radius:9px;padding:7px 9px;transition:background .5s var(--ease)}
|
|
998
|
-
.src-row.pulse{background:rgba(10,132,255,.16)}
|
|
999
|
-
.src-n{font:700 11px var(--mono);color:var(--blue);margin-right:7px}
|
|
1000
|
-
.src-meta{display:inline-flex;align-items:baseline;gap:8px;flex-wrap:wrap}
|
|
1001
|
-
.src-file{font-size:12.5px;font-weight:600;color:var(--ink)}
|
|
1002
|
-
.src-range{font:11px var(--mono);color:var(--dimmer)}
|
|
1003
|
-
.src-snippet{margin-top:4px;font-size:12px;line-height:1.5;color:var(--dim);white-space:pre-wrap;word-break:break-word}
|
|
1004
|
-
/* [n] citation markers rendered inline in the assistant's reply text
|
|
1005
|
-
(markdown.ts's linkifyCitations) — small round buttons, never full-size
|
|
1006
|
-
links, so they read as footnote markers rather than ordinary hyperlinks. */
|
|
1007
|
-
:is(.bubble) .cite-mark{display:inline-flex;align-items:center;justify-content:center;
|
|
1008
|
-
min-width:16px;height:16px;padding:0 4px;margin:0 1px;border-radius:999px;border:none;
|
|
1009
|
-
background:rgba(10,132,255,.18);color:var(--blue);font:700 10px var(--mono);
|
|
1010
|
-
cursor:pointer;vertical-align:2px;line-height:1;transition:background .15s}
|
|
1011
|
-
:is(.bubble) .cite-mark:hover{background:rgba(10,132,255,.3)}
|
|
1012
|
-
@media (prefers-reduced-motion:reduce){.src-row{transition:none}.sources summary::before{transition:none}}
|
|
1013
|
-
|
|
1014
|
-
/* chat hello / starters */
|
|
1015
|
-
#chat-hello{max-width:840px;margin:6vh auto 0;padding:0 22px;text-align:center}
|
|
1016
|
-
#chat-hello h1{font-size:clamp(34px,5vw,54px);font-weight:700;letter-spacing:-.03em;line-height:1.08}
|
|
1017
|
-
#chat-hello p{margin-top:13px;color:var(--dim);font-size:15.5px}
|
|
1018
|
-
#chat-hello .chips{display:flex;flex-wrap:wrap;gap:10px;justify-content:center;margin-top:28px}
|
|
1019
|
-
.chip{border:1px solid var(--hairline);background:var(--card);color:var(--dim);border-radius:999px;
|
|
1020
|
-
padding:9px 16px;font-size:13.5px;cursor:pointer;transition:all .25s var(--ease)}
|
|
1021
|
-
.chip:hover{color:var(--ink);border-color:var(--hairline-strong);background:var(--card-hover)}
|
|
1022
|
-
|
|
1023
|
-
/* memory consent card (plan §5.1/§9 Phase 2) — one-line, skippable, in the
|
|
1024
|
-
hero. Never auto-creates the vault; a plain hairline card, not a modal,
|
|
1025
|
-
so it reads as an offer, not an interruption. */
|
|
1026
|
-
#chat-consent{display:none;align-items:center;gap:12px;text-align:left;margin:22px auto 0;max-width:640px;
|
|
1027
|
-
border:1px solid var(--hairline);background:var(--card);border-radius:14px;padding:13px 16px}
|
|
1028
|
-
#chat-consent.show{display:flex}
|
|
1029
|
-
#chat-consent .cc-icon{font-size:18px;flex:0 0 auto}
|
|
1030
|
-
#chat-consent .cc-text{flex:1 1 auto;font-size:13px;color:var(--dim);line-height:1.5}
|
|
1031
|
-
#chat-consent .cc-text b{color:var(--ink);font-weight:600}
|
|
1032
|
-
#chat-consent .cc-actions{display:flex;gap:8px;flex:0 0 auto}
|
|
1033
|
-
|
|
1034
|
-
/* chat composer */
|
|
1035
|
-
#chat-foot{flex:0 0 auto;border-top:1px solid var(--hairline);padding:14px 22px 16px;
|
|
1036
|
-
background:var(--card-2);backdrop-filter:blur(16px)}
|
|
1037
|
-
.composer{max-width:840px;margin:0 auto}
|
|
1038
|
-
.queuebar{display:none;max-width:840px;margin:0 auto 10px;gap:8px;flex-wrap:wrap;font-size:12px;color:var(--dim)}
|
|
1039
|
-
.queuebar .qtag{display:inline-flex;align-items:center;gap:6px;border:1px solid var(--hairline);
|
|
1040
|
-
border-radius:999px;padding:4px 11px;background:var(--card)}
|
|
1041
|
-
.queuebar .qtag b{color:var(--ink);font-weight:600}
|
|
1042
|
-
.inputrow{display:flex;gap:10px;align-items:flex-end}
|
|
1043
|
-
#chat-box-wrap{position:relative;flex:1;min-width:0}
|
|
1044
|
-
#chat-box{width:100%;background:var(--card);border:1px solid var(--hairline);border-radius:16px;
|
|
1045
|
-
color:var(--ink);font:15px/1.5 inherit;padding:13px 16px;resize:none;outline:none;
|
|
1046
|
-
max-height:200px;min-height:50px;transition:border-color .25s;box-sizing:border-box}
|
|
1047
|
-
#chat-box:focus{border-color:rgba(10,132,255,.5)}
|
|
1048
|
-
/* Unified "#" retrieval mention picker (plan §5.2/§9 Phase 2) — same
|
|
1049
|
-
floating-panel chrome as .samplepop, anchored above the textarea it
|
|
1050
|
-
types into instead of a pill. Zero layout shift: absolutely positioned,
|
|
1051
|
-
hidden by default, never reflows the composer underneath it. */
|
|
1052
|
-
.mentionpop{position:absolute;bottom:calc(100% + 8px);left:0;right:0;z-index:40;
|
|
1053
|
-
max-height:min(46vh,320px);overflow-y:auto;
|
|
1054
|
-
background:var(--popover-panel);border:1px solid var(--hairline);border-radius:13px;padding:6px;
|
|
1055
|
-
backdrop-filter:blur(22px) saturate(1.3);-webkit-backdrop-filter:blur(22px) saturate(1.3);
|
|
1056
|
-
box-shadow:0 18px 50px rgba(0,0,0,.35);display:none;
|
|
1057
|
-
transform:translateY(4px);opacity:0;transition:opacity .14s var(--ease),transform .14s var(--ease)}
|
|
1058
|
-
.mentionpop.open{display:block;transform:translateY(0);opacity:1}
|
|
1059
|
-
.mention-sec-title{font-size:10.5px;font-weight:600;color:var(--dimmer);letter-spacing:.04em;
|
|
1060
|
-
text-transform:uppercase;padding:7px 9px 4px}
|
|
1061
|
-
.mention-row{display:flex;align-items:center;gap:8px;padding:7px 9px;border-radius:9px;cursor:pointer}
|
|
1062
|
-
.mention-row:hover,.mention-row.active{background:var(--card-hover)}
|
|
1063
|
-
.mention-ico{font-size:12px;color:var(--dimmer);flex:0 0 auto}
|
|
1064
|
-
.mention-label{flex:1 1 auto;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;
|
|
1065
|
-
font-size:13px;color:var(--ink)}
|
|
1066
|
-
.mention-tag{flex:0 0 auto;font-size:10px;color:var(--dimmer);text-transform:uppercase;letter-spacing:.03em}
|
|
1067
|
-
.mention-empty{padding:10px 9px;font-size:12px;color:var(--dim)}
|
|
1068
|
-
/* Pulse a file's attach-chip when it's selected from the mention picker
|
|
1069
|
-
(the file is already attached — this is the only feedback needed). */
|
|
1070
|
-
.attach-chip.pulse{animation:chippulse .5s var(--ease) 2}
|
|
1071
|
-
@keyframes chippulse{0%,100%{box-shadow:0 0 0 rgba(10,132,255,0)}50%{box-shadow:0 0 0 3px rgba(10,132,255,.45)}}
|
|
1072
|
-
@media (prefers-reduced-motion:reduce){.attach-chip.pulse{animation:none}}
|
|
1073
|
-
#chat-send{background:linear-gradient(120deg,var(--blue),var(--purple));color:#fff;
|
|
1074
|
-
border:none;border-radius:14px;height:50px;padding:0 22px;font:600 14px inherit;cursor:pointer;transition:filter .2s}
|
|
1075
|
-
#chat-send:hover{filter:brightness(1.14)}
|
|
1076
|
-
#chat-send:disabled{filter:grayscale(.8) brightness(.6);cursor:default}
|
|
1077
|
-
#chat-stop{display:none;background:var(--card);border:1px solid var(--hairline);color:var(--ink);
|
|
1078
|
-
border-radius:14px;height:50px;padding:0 20px;font:600 14px inherit;cursor:pointer}
|
|
1079
|
-
#chat-stop:hover{background:var(--card-hover)}
|
|
1080
|
-
/* Wrap + shrink rules (2026-07-06 jank fix): without flex-wrap/min-width:0
|
|
1081
|
-
the right pill cluster overflowed past the viewport edge at tablet
|
|
1082
|
-
widths ("no adapter" clipped). The status line ellipsizes first; the
|
|
1083
|
-
pills wrap to a second row before anything leaves the screen. */
|
|
1084
|
-
.chat-hint{max-width:840px;margin:8px auto 0;font-size:11.5px;color:var(--dimmer);display:flex;justify-content:space-between;gap:10px;flex-wrap:wrap}
|
|
1085
|
-
.chat-hint #chat-status-line{flex:1 1 auto;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
|
1086
|
-
.chat-hint .hint-right{display:flex;gap:12px;align-items:center;flex-wrap:wrap;justify-content:flex-end;min-width:0}
|
|
1087
|
-
/* Native <select> in .pill clothes: reset the UA appearance and draw our
|
|
1088
|
-
own chevron — the stock arrow collided with the label text ("no adaptɘr"
|
|
1089
|
-
glyph pile-up), and long adapter ids now ellipsize instead of overflow. */
|
|
1090
|
-
#chat-adapter{appearance:none;-webkit-appearance:none;max-width:200px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;
|
|
1091
|
-
padding-right:27px;cursor:pointer;
|
|
1092
|
-
background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='8' height='5' viewBox='0 0 8 5'%3E%3Cpath d='M1 1l3 3 3-3' fill='none' stroke='%23909098' stroke-width='1.4' stroke-linecap='round'/%3E%3C/svg%3E");
|
|
1093
|
-
background-repeat:no-repeat;background-position:right 11px center}
|
|
1094
|
-
/* perf strip — always-visible, quiet: replaces the old bare #chat-tps text
|
|
1095
|
-
with tok/s + TTFT + context-fill + (server-driven only) a lane badge. */
|
|
1096
|
-
#chat-perf{max-width:840px;margin:8px auto 0;display:flex;align-items:center;gap:14px;
|
|
1097
|
-
font:11px/1 var(--mono);letter-spacing:.01em;color:var(--dimmer);flex-wrap:wrap}
|
|
1098
|
-
#chat-perf .pf-item{white-space:nowrap}
|
|
1099
|
-
#chat-perf .pf-item b{color:var(--ink);font-weight:600}
|
|
1100
|
-
#chat-perf .pf-lane{display:none;align-items:center;gap:5px;border:1px solid var(--hairline);
|
|
1101
|
-
border-radius:999px;padding:2px 9px;font-size:10.5px;text-transform:uppercase;letter-spacing:.04em}
|
|
1102
|
-
#chat-perf .pf-lane .dot{width:6px;height:6px;border-radius:50%;background:var(--dimmer)}
|
|
1103
|
-
#chat-perf .pf-lane.serial .dot{background:var(--dim)}
|
|
1104
|
-
#chat-perf .pf-lane.spec .dot{background:var(--blue);box-shadow:0 0 8px var(--blue)}
|
|
1105
|
-
#chat-perf .pf-lane.batched .dot{background:var(--green);box-shadow:0 0 8px var(--green)}
|
|
1106
|
-
/* adapter chip — upgraded presentation over the bare <select>: current
|
|
1107
|
-
selection + specialty line, options grayed by /v1/adapters/available's
|
|
1108
|
-
compatible flag rather than hidden outright. */
|
|
1109
|
-
#chat-adapter option:disabled{color:var(--dimmer);font-style:italic}
|
|
1110
|
-
/* "manage adapters" gear beside the quick-switch <select> — opens the
|
|
1111
|
-
routing table (#adapters-overlay), same pill sizing as #chat-think. */
|
|
1112
|
-
#adapters-manage{appearance:none;-webkit-appearance:none;cursor:pointer;display:inline-flex;align-items:center;
|
|
1113
|
-
justify-content:center;width:27px;height:27px;border-radius:999px;background:var(--card);
|
|
1114
|
-
border:1px solid var(--hairline);color:var(--dim);font-size:12.5px;line-height:1;flex:0 0 auto;
|
|
1115
|
-
transition:color .18s var(--ease),border-color .18s var(--ease),background .18s var(--ease)}
|
|
1116
|
-
#adapters-manage:hover{color:var(--ink);background:var(--card-hover);border-color:var(--hairline-strong)}
|
|
1117
|
-
/* attach (+) button + attachment chips */
|
|
1118
|
-
#chat-attach-btn{flex:0 0 auto;width:50px;height:50px;border-radius:14px;background:var(--card);
|
|
1119
|
-
border:1px solid var(--hairline);color:var(--ink);font-size:23px;line-height:1;cursor:pointer;transition:background .15s,border-color .15s}
|
|
1120
|
-
#chat-attach-btn:hover{background:var(--card-hover);border-color:rgba(10,132,255,.5)}
|
|
1121
|
-
/* hold-to-talk mic (voice.ts) */
|
|
1122
|
-
#chat-mic-btn{flex:0 0 auto;width:50px;height:50px;border-radius:14px;background:var(--card);
|
|
1123
|
-
border:1px solid var(--hairline);color:var(--ink);font-size:20px;line-height:1;cursor:pointer;user-select:none;-webkit-user-select:none;transition:background .15s,border-color .15s}
|
|
1124
|
-
#chat-mic-btn:hover{background:var(--card-hover);border-color:rgba(10,132,255,.5)}
|
|
1125
|
-
#chat-mic-btn.recording{background:rgba(255,69,58,.18);border-color:rgba(255,69,58,.7);animation:micpulse 1s ease-in-out infinite}
|
|
1126
|
-
#chat-mic-btn.busy{opacity:.6;cursor:progress}
|
|
1127
|
-
@keyframes micpulse{0%,100%{box-shadow:0 0 0 0 rgba(255,69,58,.35)}50%{box-shadow:0 0 0 8px rgba(255,69,58,0)}}
|
|
1128
|
-
.attach-row{display:none;flex-wrap:wrap;gap:8px;max-width:840px;width:100%;margin:0 auto 8px}
|
|
1129
|
-
.attach-chip{display:flex;align-items:center;gap:7px;background:var(--card);border:1px solid var(--hairline);
|
|
1130
|
-
border-radius:10px;padding:5px 9px;font-size:12px;max-width:240px}
|
|
1131
|
-
.attach-chip img{width:34px;height:34px;object-fit:cover;border-radius:6px;display:block}
|
|
1132
|
-
.attach-chip .att-ico{font-size:16px}
|
|
1133
|
-
.attach-chip .att-name{white-space:nowrap;overflow:hidden;text-overflow:ellipsis;color:var(--ink)}
|
|
1134
|
-
.attach-chip .att-x{border:none;background:transparent;color:var(--dim);cursor:pointer;font-size:12px;line-height:1;padding:0 2px}
|
|
1135
|
-
.attach-chip .att-x:hover{color:var(--red)}
|
|
1136
|
-
/* Chat-with-files RAG v1 (plan §9 Phase 3, beat matrix Axis 5): shown on
|
|
1137
|
-
text-file chips once the attached set is large enough that only the
|
|
1138
|
-
most relevant chunks get sent per turn instead of the whole file
|
|
1139
|
-
(composer.ts's shouldRetrieve threshold) — transparency principle, the
|
|
1140
|
-
mode switch must be visible on the chip, not just in prompt content
|
|
1141
|
-
the user never sees. */
|
|
1142
|
-
.attach-chip .att-tag{flex:0 0 auto;font-size:9.5px;font-weight:600;letter-spacing:.04em;text-transform:uppercase;
|
|
1143
|
-
color:var(--purple);background:rgba(191,90,242,.14);border-radius:999px;padding:2px 7px}
|
|
1144
|
-
.composer.dragover{outline:2px dashed var(--blue);outline-offset:6px;border-radius:14px}
|
|
1145
|
-
.msg-atts{display:flex;flex-wrap:wrap;gap:8px;margin-bottom:8px}
|
|
1146
|
-
.msg-att-img{max-width:220px;max-height:220px;border-radius:10px;display:block}
|
|
1147
|
-
.msg-att-file{display:inline-flex;align-items:center;gap:5px;font-size:12px;background:rgba(255,255,255,.08);border-radius:8px;padding:5px 10px}
|
|
1148
|
-
.msg.user .msg-text{white-space:pre-wrap;word-break:break-word}
|
|
1149
|
-
|
|
1150
|
-
/* loss chart */
|
|
1151
|
-
.chartcard{margin-top:20px}
|
|
1152
|
-
/* the chart's SVG gridlines are hardcoded white-on-dark (drawChart()), so
|
|
1153
|
-
the wrap keeps the always-dark code-panel surface for contrast rather
|
|
1154
|
-
than following the chat theme — same rationale as .bubble pre above. */
|
|
1155
|
-
.chart-wrap{position:relative;background:var(--code-bg-soft);border:1px solid var(--hairline-soft);border-radius:14px;padding:14px;margin-top:6px}
|
|
1156
|
-
.chart-legend{display:flex;gap:18px;font-size:12px;color:var(--dim);margin-bottom:8px}
|
|
1157
|
-
.chart-legend .lg{display:inline-flex;align-items:center;gap:7px}
|
|
1158
|
-
.chart-legend .sw{width:14px;height:3px;border-radius:2px}
|
|
1159
|
-
.chart-stats{display:flex;gap:22px;flex-wrap:wrap;margin-top:12px;font-size:13px}
|
|
1160
|
-
.chart-stats .cs b{display:block;font-size:11px;font-weight:600;letter-spacing:.1em;text-transform:uppercase;color:var(--dim);margin-bottom:3px}
|
|
1161
|
-
.chart-stats .cs span{font-size:20px;font-weight:700;letter-spacing:-.02em}
|
|
1162
|
-
|
|
1163
|
-
/* Tablet band (2026-07-06 jank fix): with up to 7 tabs (fewer once the
|
|
1164
|
-
plan §8 Developer toggle is off — see nav .tab[data-dev] above) +
|
|
1165
|
-
hamburger + shortcuts + theme toggle + gear + conn pill, the tab row
|
|
1166
|
-
can overflow well above the 900px cutoff — tighten tab padding first
|
|
1167
|
-
so fewer widths need the scroll-fade at all. */
|
|
1168
|
-
@media (max-width:1160px){
|
|
1169
|
-
nav{gap:12px}
|
|
1170
|
-
nav .tab{padding:8px 9px;font-size:13px}
|
|
1171
|
-
}
|
|
1172
|
-
@media (max-width:900px){
|
|
1173
|
-
.hpgrid{grid-template-columns:1fr}
|
|
1174
|
-
nav .model{display:none}
|
|
1175
|
-
.wrap{padding:34px 18px 100px}
|
|
1176
|
-
nav{gap:12px;padding:0 16px}
|
|
1177
|
-
/* Icon-only theme toggle a bit earlier than 560px — the nav is already
|
|
1178
|
-
tight here (hamburger + shortcuts + toggle + hf-gear + conn pill) and
|
|
1179
|
-
"Auto" as a text label isn't worth the horizontal cost at this width.
|
|
1180
|
-
The ◐ tt-icon stands in so the Auto button never renders as an empty
|
|
1181
|
-
sliver (2026-07-06 QA finding). */
|
|
1182
|
-
#theme-toggle button{padding:0 7px;font-size:15px}
|
|
1183
|
-
#theme-toggle .tt-label{display:none}
|
|
1184
|
-
#theme-toggle .tt-icon{display:inline}
|
|
1185
|
-
/* Developer toggle: dot-only past this width, same reasoning as the
|
|
1186
|
-
theme toggle above — "Developer" as a text label costs more than
|
|
1187
|
-
the dot alone conveys once the row is already tight. */
|
|
1188
|
-
#nav-developer span:not(.nd-dot){display:none}
|
|
1189
|
-
#nav-developer{padding:0;width:34px;justify-content:center}
|
|
1190
|
-
}
|
|
1191
|
-
@media (max-width:760px){
|
|
1192
|
-
/* Touch band (2026-07-06 QA): a keyboard-shortcut sheet button is dead
|
|
1193
|
-
weight on touch viewports, and the nav needs its width back. */
|
|
1194
|
-
#nav-shortcuts{display:none}
|
|
1195
|
-
/* The conn pill collapses to its status dot (title keeps the text for
|
|
1196
|
-
long-press/hover); "live · localhost" was clipping at the viewport
|
|
1197
|
-
edge at 375px. */
|
|
1198
|
-
#nav-conn #nav-conn-text{display:none}
|
|
1199
|
-
#nav-conn{padding:6px 9px}
|
|
1200
|
-
/* Keep a scrollable strip of tabs alive — flex shrank them to 0 width
|
|
1201
|
-
at phone sizes, silently removing Quantize/Status navigation. The
|
|
1202
|
-
scroll-fade masks signal the rest. With the Developer toggle off
|
|
1203
|
-
(the fresh-browser default) this row can be just the single Chat
|
|
1204
|
-
tab — min-width still holds so the fade-mask math in
|
|
1205
|
-
updateTabFades() never divides against a zero-width strip. */
|
|
1206
|
-
nav .tabs{min-width:96px}
|
|
1207
|
-
}
|
|
1208
|
-
@media (max-width:560px){
|
|
1209
|
-
nav .brand{font-size:16px}
|
|
1210
|
-
nav .tab{padding:8px 10px;font-size:13px}
|
|
1211
|
-
nav .right{gap:6px}
|
|
1212
|
-
.icon-btn,.hf-gear{width:30px;height:30px}
|
|
1213
|
-
/* Phones follow the system theme (Auto is the default); the explicit
|
|
1214
|
-
3-way toggle returns ≥560px. Freeing its ~110px is what keeps the
|
|
1215
|
-
tab strip + status dot inside a 375px viewport (2026-07-06 QA). */
|
|
1216
|
-
#theme-toggle{display:none}
|
|
1217
|
-
}
|
|
1218
|
-
</style>
|
|
1219
|
-
<!-- Vendored syntax-highlighting theme (src/web/vendor/hljs-theme.css, hand-derived
|
|
1220
|
-
from the design tokens above — see that file's header). No CDN; served by
|
|
1221
|
-
src/server.ts alongside this page. -->
|
|
1222
|
-
<link rel="stylesheet" href="/assets/hljs.css">
|
|
1223
|
-
<!-- PWA installability (plan §9 Phase 3, beat-matrix Axis 10): manifest +
|
|
1224
|
-
inline SVG icon (src/web/manifest.webmanifest, src/web/icon.svg — no
|
|
1225
|
-
binary PNGs, per the hygiene gate; some browsers won't render an SVG
|
|
1226
|
-
app icon and that's an accepted tradeoff, see docs/reference/server-config.md).
|
|
1227
|
-
theme-color matches the app's --bg token so the OS chrome doesn't
|
|
1228
|
-
flash a mismatched color on launch. -->
|
|
1229
|
-
<link rel="manifest" href="/manifest.webmanifest">
|
|
1230
|
-
<link rel="icon" href="/assets/icon.svg" type="image/svg+xml">
|
|
1231
|
-
<link rel="apple-touch-icon" href="/assets/icon.svg">
|
|
1232
|
-
<meta name="theme-color" content="#000000">
|
|
1233
|
-
</head>
|
|
1234
|
-
<body>
|
|
1235
|
-
<div class="bloom" id="bloom"></div>
|
|
1236
|
-
|
|
1237
|
-
<!-- ═══════════════════ TOP NAV ═══════════════════ -->
|
|
1238
|
-
<nav>
|
|
1239
|
-
<!-- Mobile drawer trigger — chat route only (CSS keeps it hidden ≥760px
|
|
1240
|
-
and JS keeps it hidden off the chat route, since there is no
|
|
1241
|
-
sidebar to open elsewhere). aria-controls/expanded kept in sync by
|
|
1242
|
-
the drawer open/close functions. -->
|
|
1243
|
-
<button class="icon-btn" id="chat-hamburger" title="Open recent chats" aria-label="Open recent chats"
|
|
1244
|
-
aria-controls="chat-sidebar" aria-expanded="false">
|
|
1245
|
-
<svg viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" aria-hidden="true"><line x1="3" y1="6" x2="21" y2="6"></line><line x1="3" y1="12" x2="21" y2="12"></line><line x1="3" y1="18" x2="21" y2="18"></line></svg>
|
|
1246
|
-
</button>
|
|
1247
|
-
<a class="brand grad g-hero" href="#/chat">mlx-bun</a>
|
|
1248
|
-
<div class="tabs" id="tabs">
|
|
1249
|
-
<a class="tab" href="#/chat" data-tab="chat">Chat</a>
|
|
1250
|
-
<a class="tab" href="#/quantize" data-tab="quantize" data-dev>Quantize</a>
|
|
1251
|
-
<a class="tab" href="#/finetune" data-tab="finetune" data-dev>Fine-tune</a>
|
|
1252
|
-
<a class="tab" href="#/dataset" data-tab="dataset" data-dev>Build Dataset</a>
|
|
1253
|
-
<a class="tab" href="#/status" data-tab="status" data-dev>Status</a>
|
|
1254
|
-
<!-- Full-page link (not hash-routed), so the active-tab highlight logic
|
|
1255
|
-
can never mark it — web-ui-pass-plan.md #19, cosmetic-only per the
|
|
1256
|
-
plan. aria-label spells out that it leaves the SPA rather than
|
|
1257
|
-
leaving the tab looking silently broken. -->
|
|
1258
|
-
<a class="tab" href="/curves" data-tab="curves" data-dev aria-label="Curve designer (opens a separate page)">Curves</a>
|
|
1259
|
-
<a class="tab" href="#/routes" data-tab="routes" data-dev>Routes</a>
|
|
1260
|
-
</div>
|
|
1261
|
-
<div class="right">
|
|
1262
|
-
<!-- Developer toggle (plan §8/§9 Phase 2): collapses Quantize/Fine-tune/
|
|
1263
|
-
Build Dataset/Status/Curves/Routes behind one switch. State + the
|
|
1264
|
-
one-time existing-user default live in shell.ts's
|
|
1265
|
-
initDeveloperToggle/isDeveloperMode. -->
|
|
1266
|
-
<button id="nav-developer" data-spotlight="developer-toggle" type="button" role="switch" aria-checked="false"
|
|
1267
|
-
title="Show developer tools (Quantize, Fine-tune, Build Dataset, Status, Curves, Routes)"
|
|
1268
|
-
aria-label="Toggle developer tools">
|
|
1269
|
-
<span class="nd-dot" id="nav-developer-dot" aria-hidden="true"></span><span>Developer</span>
|
|
1270
|
-
</button>
|
|
1271
|
-
<button class="icon-btn" id="nav-shortcuts" title="Keyboard shortcuts (Cmd/Ctrl+/)" aria-label="Keyboard shortcuts"
|
|
1272
|
-
aria-haspopup="dialog" aria-controls="shortcut-overlay" aria-expanded="false">
|
|
1273
|
-
<svg viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="2" y="6" width="20" height="12" rx="2"></rect><path d="M6 10h.01M10 10h.01M14 10h.01M18 10h.01M6 14h12"></path></svg>
|
|
1274
|
-
</button>
|
|
1275
|
-
<!-- 3-way theme toggle (plan §7): auto follows prefers-color-scheme,
|
|
1276
|
-
dark/light are explicit user overrides — all persisted to
|
|
1277
|
-
localStorage by setTheme() below. -->
|
|
1278
|
-
<div id="theme-toggle" role="group" aria-label="Theme">
|
|
1279
|
-
<button type="button" data-theme-choice="auto" title="Match system theme" aria-label="Auto theme"><span class="tt-icon" aria-hidden="true">◐</span><span class="tt-label">Auto</span></button>
|
|
1280
|
-
<button type="button" data-theme-choice="dark" title="Dark theme" aria-label="Dark theme">🌙</button>
|
|
1281
|
-
<button type="button" data-theme-choice="light" title="Light theme" aria-label="Light theme">☀️</button>
|
|
1282
|
-
</div>
|
|
1283
|
-
<button class="hf-gear" id="nav-hf" title="Hugging Face token settings" aria-label="Hugging Face settings">
|
|
1284
|
-
<svg viewBox="0 0 24 24" width="17" height="17" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="12" cy="12" r="3"></circle><path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 1 1-2.83 2.83l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-4 0v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 1 1-2.83-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1 0-4h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 1 1 2.83-2.83l.06.06a1.65 1.65 0 0 0 1.82.33H9a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 4 0v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 1 1 2.83 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 0 4h-.09a1.65 1.65 0 0 0-1.51 1z"></path></svg>
|
|
1285
|
-
</button>
|
|
1286
|
-
<!-- Model picker (plan §5.6/§9 Phase 2): #nav-model was a dead <span>
|
|
1287
|
-
with no click handler; now opens a popover of downloaded models
|
|
1288
|
-
with a per-row /fit-shaped verdict (model-picker.ts). Wrapped for
|
|
1289
|
-
position:relative anchoring, same pattern as #chat-sampling-wrap. -->
|
|
1290
|
-
<span id="nav-model-wrap">
|
|
1291
|
-
<button class="model" id="nav-model" data-spotlight="model-picker" type="button" title="active model — click to browse downloaded models"
|
|
1292
|
-
aria-haspopup="dialog" aria-controls="model-pop" aria-expanded="false">connecting…</button>
|
|
1293
|
-
<div id="model-pop" class="samplepop" role="dialog" aria-label="Downloaded models">
|
|
1294
|
-
<h4>Downloaded models</h4>
|
|
1295
|
-
<div id="model-pop-body"></div>
|
|
1296
|
-
</div>
|
|
1297
|
-
</span>
|
|
1298
|
-
<span class="pill" id="nav-conn"><span class="dot"></span><span id="nav-conn-text">connecting…</span></span>
|
|
1299
|
-
<span class="pill" id="nav-download" style="display:none" title="A more capable model is downloading in the background"><span class="dot"></span><span id="nav-download-text"></span></span>
|
|
1300
|
-
</div>
|
|
1301
|
-
</nav>
|
|
1302
|
-
|
|
1303
|
-
<!-- ═══════════════════ SETTINGS OVERLAY ═══════════════════
|
|
1304
|
-
(was "HF settings" — widened in place for the Agent tools section,
|
|
1305
|
-
plan §5.4/§6.5/§9 Phase 2. Same ids/behavior for the HF piece; nothing
|
|
1306
|
-
about push-to-hub or the hf-* wiring changed.) -->
|
|
1307
|
-
<div id="hf-overlay">
|
|
1308
|
-
<div id="hf-modal" role="dialog" aria-modal="true" aria-labelledby="hf-modal-title-el">
|
|
1309
|
-
<div class="hf-modal-head">
|
|
1310
|
-
<div class="hf-modal-title" id="hf-modal-title-el">Settings</div>
|
|
1311
|
-
<button class="hf-close" id="hf-close" aria-label="Close">✕</button>
|
|
1312
|
-
</div>
|
|
1313
|
-
|
|
1314
|
-
<!-- Agent tools (plan §5.4/§6.5): the codingTools opt-in + the durable
|
|
1315
|
-
always-allow list. Lives above Hugging Face since it's the more
|
|
1316
|
-
chat-relevant setting. -->
|
|
1317
|
-
<div class="settings-section">
|
|
1318
|
-
<h3 class="settings-h">Agent tools</h3>
|
|
1319
|
-
<label class="chk settings-toggle-row" for="settings-coding-tools">
|
|
1320
|
-
<input type="checkbox" id="settings-coding-tools">
|
|
1321
|
-
<span>Let the agent touch files on this machine</span>
|
|
1322
|
-
</label>
|
|
1323
|
-
<div class="hint">
|
|
1324
|
-
Off by default. When on, the chat assistant can run shell commands and
|
|
1325
|
-
edit or create files — each one asks first, in the chat, unless you
|
|
1326
|
-
choose "always allow" for that action. Nothing runs without your
|
|
1327
|
-
review the first time.
|
|
1328
|
-
</div>
|
|
1329
|
-
<p class="settings-note" id="settings-coding-tools-note" style="display:none"></p>
|
|
1330
|
-
<div id="settings-approvals-list"></div>
|
|
1331
|
-
</div>
|
|
1332
|
-
|
|
1333
|
-
<div class="settings-section">
|
|
1334
|
-
<h3 class="settings-h">Hugging Face</h3>
|
|
1335
|
-
<p class="hf-modal-sub" id="hf-state">Checking for a saved token…</p>
|
|
1336
|
-
<div class="field" style="margin-bottom:10px">
|
|
1337
|
-
<label>Write token</label>
|
|
1338
|
-
<input type="password" id="hf-token-input" placeholder="hf_…" autocomplete="off" spellcheck="false">
|
|
1339
|
-
<div class="hint">A <strong>write</strong> token from huggingface.co/settings/tokens. Stored locally at <code>~/.mlx-bun/hf.json</code> — never sent anywhere but your own Hub pushes.</div>
|
|
1340
|
-
</div>
|
|
1341
|
-
<div id="hf-settings-msg"></div>
|
|
1342
|
-
<div class="btnrow" style="margin-top:14px">
|
|
1343
|
-
<button class="btn primary sm" id="hf-save">Save token</button>
|
|
1344
|
-
</div>
|
|
1345
|
-
</div>
|
|
1346
|
-
</div>
|
|
1347
|
-
</div>
|
|
1348
|
-
|
|
1349
|
-
<!-- ═══════════════════ KEYBOARD SHORTCUT SHEET ═══════════════════ -->
|
|
1350
|
-
<div id="shortcut-overlay">
|
|
1351
|
-
<div id="shortcut-modal" role="dialog" aria-modal="true" aria-labelledby="sk-modal-title">
|
|
1352
|
-
<div class="sk-head">
|
|
1353
|
-
<div class="sk-title" id="sk-modal-title">Keyboard shortcuts</div>
|
|
1354
|
-
<button class="sk-close" id="sk-close" aria-label="Close">✕</button>
|
|
1355
|
-
</div>
|
|
1356
|
-
<div class="sk-row"><span class="sk-desc">Show this shortcut sheet</span><span class="sk-keys"><span class="sk-key" id="sk-mod-label">⌘</span><span class="sk-key">/</span></span></div>
|
|
1357
|
-
<div class="sk-row"><span class="sk-desc">New chat</span><span class="sk-keys"><span class="sk-key sk-mod"></span><span class="sk-key">Shift</span><span class="sk-key">O</span></span></div>
|
|
1358
|
-
<div class="sk-row"><span class="sk-desc">Focus the composer</span><span class="sk-keys"><span class="sk-key">Shift</span><span class="sk-key">Esc</span></span></div>
|
|
1359
|
-
<div class="sk-row"><span class="sk-desc">Copy last response</span><span class="sk-keys"><span class="sk-key sk-mod"></span><span class="sk-key">Shift</span><span class="sk-key">C</span></span></div>
|
|
1360
|
-
<div class="sk-row"><span class="sk-desc">Send message</span><span class="sk-keys"><span class="sk-key">Enter</span></span></div>
|
|
1361
|
-
<div class="sk-row"><span class="sk-desc">New line in composer</span><span class="sk-keys"><span class="sk-key">Shift</span><span class="sk-key">Enter</span></span></div>
|
|
1362
|
-
<div class="sk-row"><span class="sk-desc">Close popover / drawer / sheet</span><span class="sk-keys"><span class="sk-key">Esc</span></span></div>
|
|
1363
|
-
</div>
|
|
1364
|
-
</div>
|
|
1365
|
-
|
|
1366
|
-
<!-- ═══════════════════ MEMORY PANEL ═══════════════════ (plan §5.5/§9 Phase 2)
|
|
1367
|
-
Full-height overlay beside chat, same drawer/overlay language as the HF
|
|
1368
|
-
modal and mobile sidebar: fixed, scrimmed, focus-trapped, Escape closes.
|
|
1369
|
-
All content rendered client-side by memory-panel.ts from the
|
|
1370
|
-
/api/memory/* REST wrappers — no new WS frames. -->
|
|
1371
|
-
<div id="mem-overlay">
|
|
1372
|
-
<div id="mem-panel" role="dialog" aria-modal="true" aria-labelledby="mem-title">
|
|
1373
|
-
<div class="mem-head">
|
|
1374
|
-
<button class="mem-back" id="mem-back" type="button" aria-label="Back to article list">‹ Back</button>
|
|
1375
|
-
<h2 id="mem-title">Memory</h2>
|
|
1376
|
-
<button class="mem-close" id="mem-close" aria-label="Close memory panel">✕</button>
|
|
1377
|
-
</div>
|
|
1378
|
-
<div class="mem-status" id="mem-status"></div>
|
|
1379
|
-
<div class="mem-body" id="mem-body"></div>
|
|
1380
|
-
</div>
|
|
1381
|
-
</div>
|
|
1382
|
-
|
|
1383
|
-
<!-- ═══════════════════ ADAPTER ROUTING TABLE ═══════════════════ (plan §5.6/§9 Phase 2)
|
|
1384
|
-
Same right-side overlay/panel chrome as the Memory panel (fixed,
|
|
1385
|
-
scrimmed, focus-trapped, Escape closes) — the deep adapter surface
|
|
1386
|
-
opened from the composer's ⚙ button beside #chat-adapter. Reads the
|
|
1387
|
-
same /v1/adapters + /v1/adapters/available list state the quick
|
|
1388
|
-
<select> uses (adapters-panel.ts), never a second fetch owner. -->
|
|
1389
|
-
<div id="adapters-overlay">
|
|
1390
|
-
<div id="adapters-panel" role="dialog" aria-modal="true" aria-labelledby="adapters-title">
|
|
1391
|
-
<div class="mem-head">
|
|
1392
|
-
<h2 id="adapters-title">Adapters</h2>
|
|
1393
|
-
<button class="mem-close" id="adapters-close" aria-label="Close adapter routing table">✕</button>
|
|
1394
|
-
</div>
|
|
1395
|
-
<div class="mem-body" id="adapters-body"></div>
|
|
1396
|
-
</div>
|
|
1397
|
-
</div>
|
|
1398
|
-
|
|
1399
|
-
<!-- ═══════════════════ MODEL HUB ═══════════════════ (plan §9 Phase 3, beat-matrix Axis 3)
|
|
1400
|
-
Same right-side overlay/panel/head/body chrome as the Memory + Adapters
|
|
1401
|
-
panels (fixed, scrimmed, focus-trapped, Escape closes). Opened from the
|
|
1402
|
-
model picker popover's "Browse models…" action (hub.ts registers the
|
|
1403
|
-
open callback via shell.ts's setOpenHubFromModelPicker). Three sections:
|
|
1404
|
-
Downloaded (from /api/hub/local), Search Hugging Face (/api/hub/search),
|
|
1405
|
-
and a quiet offline footer note. NEVER auto-downloads, NEVER
|
|
1406
|
-
auto-serves — every action here is an explicit click. -->
|
|
1407
|
-
<div id="hub-overlay">
|
|
1408
|
-
<div id="hub-panel" role="dialog" aria-modal="true" aria-labelledby="hub-title">
|
|
1409
|
-
<div class="mem-head">
|
|
1410
|
-
<h2 id="hub-title">Model Hub</h2>
|
|
1411
|
-
<button class="mem-close" id="hub-close" aria-label="Close Model Hub">✕</button>
|
|
1412
|
-
</div>
|
|
1413
|
-
<div class="mem-body" id="hub-body">
|
|
1414
|
-
<div id="hub-restart-strip" class="hub-restart-strip"></div>
|
|
1415
|
-
|
|
1416
|
-
<div class="hub-sec">
|
|
1417
|
-
<div class="mem-sec-title">Downloaded</div>
|
|
1418
|
-
<div id="hub-local-body"></div>
|
|
1419
|
-
</div>
|
|
1420
|
-
|
|
1421
|
-
<div class="hub-sec">
|
|
1422
|
-
<div class="mem-sec-title">Search Hugging Face</div>
|
|
1423
|
-
<input type="search" id="hub-search-input" placeholder="Search MLX models…" aria-label="Search Hugging Face for MLX models" autocomplete="off">
|
|
1424
|
-
<div id="hub-search-body" class="hub-search-body">
|
|
1425
|
-
<div class="hub-empty">Type to search Hugging Face for MLX models.</div>
|
|
1426
|
-
</div>
|
|
1427
|
-
</div>
|
|
1428
|
-
|
|
1429
|
-
<p class="hub-offline-note" id="hub-offline-note" style="display:none">
|
|
1430
|
-
You're offline (or Hugging Face is unreachable) — search needs a network connection,
|
|
1431
|
-
but your downloaded models still work fully offline.
|
|
1432
|
-
</p>
|
|
1433
|
-
</div>
|
|
1434
|
-
</div>
|
|
1435
|
-
</div>
|
|
1436
|
-
|
|
1437
|
-
<!-- Mobile drawer backdrop (chat sidebar only — see #chat-sidebar CSS) -->
|
|
1438
|
-
<div id="chat-drawer-backdrop"></div>
|
|
1439
|
-
|
|
1440
|
-
<div id="app">
|
|
1441
|
-
|
|
1442
|
-
<!-- ═══════════════════ CHAT ═══════════════════ -->
|
|
1443
|
-
<section data-route="chat" id="s-chat">
|
|
1444
|
-
<aside id="chat-sidebar">
|
|
1445
|
-
<button id="chat-new" data-spotlight="new-chat" title="Start a new chat (or type /new)">+ New chat</button>
|
|
1446
|
-
<input type="search" id="chat-sess-search" data-spotlight="session-search" placeholder="Search chats…" aria-label="Search chats" autocomplete="off">
|
|
1447
|
-
<div id="chat-sessions" class="sesslist"></div>
|
|
1448
|
-
<!-- Memory panel entry (plan §5.5) — quiet, chat-adjacent, NOT a nav
|
|
1449
|
-
tab: memory is the product, not a dev tool. Hidden entirely until
|
|
1450
|
-
the first /api/memory/status poll resolves (see memory-panel.ts),
|
|
1451
|
-
so it never flashes "0 articles" before a real vault is known to
|
|
1452
|
-
exist or not exist. -->
|
|
1453
|
-
<button id="chat-memory-entry" data-spotlight="memory-entry" type="button" style="display:none"
|
|
1454
|
-
aria-haspopup="dialog" aria-controls="mem-overlay" aria-expanded="false">
|
|
1455
|
-
<span class="mi" aria-hidden="true">◆</span>
|
|
1456
|
-
<span class="mt">Memory</span>
|
|
1457
|
-
<span class="mcount" id="chat-memory-count">—</span>
|
|
1458
|
-
</button>
|
|
1459
|
-
</aside>
|
|
1460
|
-
<div id="chat-main">
|
|
1461
|
-
<div id="chat-scroll">
|
|
1462
|
-
<div id="chat-hello">
|
|
1463
|
-
<h1>Local. Private. <span class="grad g-hero">Yours.</span></h1>
|
|
1464
|
-
<p id="chat-hello-sub">A full pi agent on this machine — local model, local tools, zero cloud. Every token below is generated here.</p>
|
|
1465
|
-
<div class="chips" id="chat-hello-chips">
|
|
1466
|
-
<button class="chip" data-q="What is mlx-bun and what can it do?">What is mlx-bun?</button>
|
|
1467
|
-
<button class="chip" data-q="How do I load a bigger or different model?">Load a bigger model</button>
|
|
1468
|
-
<button class="chip" data-q="Is this really private? Where does my data go?">Is this private?</button>
|
|
1469
|
-
<button class="chip" data-q="What can you help me with right now?">What can you do?</button>
|
|
1470
|
-
</div>
|
|
1471
|
-
<!-- Memory consent card (plan §5.1/§9 Phase 2): first run only (no
|
|
1472
|
-
vault + never dismissed, see memory-panel.ts's isConsentDismissed).
|
|
1473
|
-
NEVER auto-creates the vault — this is the only path that calls
|
|
1474
|
-
POST /api/memory/init. Hidden by default; shown by
|
|
1475
|
-
maybeShowConsentCard() after the first status poll. -->
|
|
1476
|
-
<div id="chat-consent" role="group" aria-label="Memory setup">
|
|
1477
|
-
<span class="cc-icon" aria-hidden="true">◆</span>
|
|
1478
|
-
<span class="cc-text">Let mlx-bun remember what matters? Creates a local, git-tracked vault you can open anytime.</span>
|
|
1479
|
-
<span class="cc-actions">
|
|
1480
|
-
<button class="btn ghost sm" id="chat-consent-skip" type="button">Skip</button>
|
|
1481
|
-
<button class="btn primary sm" id="chat-consent-yes" type="button">Set up memory</button>
|
|
1482
|
-
</span>
|
|
1483
|
-
</div>
|
|
1484
|
-
</div>
|
|
1485
|
-
<!-- aria-live is a coarse approximation: it announces at the container
|
|
1486
|
-
level (whole finished messages / completed blocks), not per-token
|
|
1487
|
-
on the live tail — announcing every streamed delta would spam a
|
|
1488
|
-
screen reader with one interruption per token. aria-atomic=false
|
|
1489
|
-
so only the newly-appended content is announced, not the whole
|
|
1490
|
-
growing thread each time. -->
|
|
1491
|
-
<!-- data-ui-chrome="content" excludes the whole rendered conversation
|
|
1492
|
-
(model/user/tool text, links, citations) from captureUiSnapshot
|
|
1493
|
-
(assistant.ts) — that snapshot is re-served to the model verbatim
|
|
1494
|
-
as trusted "current UI state" via get_current_app_context, and an
|
|
1495
|
-
`a[href]` inside a rendered message is untrusted content, not app
|
|
1496
|
-
chrome (see isAgentChrome's doc comment). -->
|
|
1497
|
-
<div id="chat-thread" role="log" aria-live="polite" aria-atomic="false" data-ui-chrome="content"></div>
|
|
1498
|
-
</div>
|
|
1499
|
-
<div id="chat-foot">
|
|
1500
|
-
<div class="queuebar" id="chat-queue"></div>
|
|
1501
|
-
<div class="composer">
|
|
1502
|
-
<div id="chat-attach" class="attach-row"></div>
|
|
1503
|
-
<div class="inputrow">
|
|
1504
|
-
<button id="chat-attach-btn" class="iconbtn" title="Attach files or images" aria-label="Attach files">+</button>
|
|
1505
|
-
<input type="file" id="chat-file-input" multiple style="display:none">
|
|
1506
|
-
<!-- Hold-to-talk dictation (voice.ts): shown only when the `ready`
|
|
1507
|
-
frame carries transcription=true (a Whisper checkpoint serves
|
|
1508
|
-
/v1/audio/sessions). Press and hold (mouse/touch, or Space/Enter
|
|
1509
|
-
while focused); release to transcribe into the box. -->
|
|
1510
|
-
<button id="chat-mic-btn" class="iconbtn" title="Hold to talk (Whisper)" aria-label="Hold to talk" style="display:none">🎙</button>
|
|
1511
|
-
<div id="chat-box-wrap">
|
|
1512
|
-
<!-- Keep the placeholder SHORT: the Enter/Shift+Enter AND "#"
|
|
1513
|
-
hints live in the chat-hint status line below (and the Cmd+/
|
|
1514
|
-
sheet). A long placeholder wraps to two lines at phone/tablet
|
|
1515
|
-
widths inside this single-row box and clips against the
|
|
1516
|
-
border (2026-07-06 jank; re-introduced + re-fixed 07-07). -->
|
|
1517
|
-
<textarea id="chat-box" data-spotlight="composer" rows="1" placeholder="Message the local agent…"></textarea>
|
|
1518
|
-
<!-- Unified "#" retrieval mention (plan §5.2/§9 Phase 2): typing
|
|
1519
|
-
"#" opens this picker over both attached files and vault
|
|
1520
|
-
articles as one gesture (composer.ts's initMentionPicker).
|
|
1521
|
-
role=listbox for the arrow-key/Enter contract; content is
|
|
1522
|
-
built entirely client-side by renderMentionListHtml(). -->
|
|
1523
|
-
<div id="chat-mention-pop" class="mentionpop" role="listbox" aria-label="Attach a file or recall a memory"></div>
|
|
1524
|
-
</div>
|
|
1525
|
-
<button id="chat-stop" title="Abort the current turn">Stop</button>
|
|
1526
|
-
<button id="chat-send" data-spotlight="send">Send</button>
|
|
1527
|
-
</div>
|
|
1528
|
-
<div class="chat-hint">
|
|
1529
|
-
<span id="chat-status-line">Enter to send · Shift+Enter for a new line · type while it streams to steer</span>
|
|
1530
|
-
<span class="hint-right">
|
|
1531
|
-
<!-- Per-chat system prompt + presets v1 (plan §9 Phase 2, beat
|
|
1532
|
-
matrix Axis 4): same pill+popover chrome as #chat-sampling
|
|
1533
|
-
(setSysPromptPopoverClose registers with shell.ts's
|
|
1534
|
-
closeTopOverlay the same way). The dot lights up whenever a
|
|
1535
|
-
custom prompt is active — the user must always SEE it's
|
|
1536
|
-
shaping replies (trust rule), never a silent setting. -->
|
|
1537
|
-
<span id="chat-sysprompt-wrap">
|
|
1538
|
-
<span id="chat-sysprompt" data-spotlight="prompt-pill" class="pill" role="button" tabindex="0" aria-haspopup="true" aria-expanded="false"
|
|
1539
|
-
title="System prompt (shapes how the assistant replies in this chat)"><span class="dot"></span>Prompt</span>
|
|
1540
|
-
<div id="chat-sysprompt-pop" class="samplepop sysprompt-pop" role="dialog" aria-label="System prompt">
|
|
1541
|
-
<h4>System prompt</h4>
|
|
1542
|
-
<textarea id="sysprompt-text" rows="6" maxlength="4000"
|
|
1543
|
-
placeholder="e.g. Answer in French. Keep replies under 3 sentences. Prefer Python over JS."
|
|
1544
|
-
aria-label="Custom system prompt"></textarea>
|
|
1545
|
-
<div class="sysprompt-foot-row">
|
|
1546
|
-
<span class="note" id="sysprompt-count">0 / 4000</span>
|
|
1547
|
-
<button id="sysprompt-clear" type="button">Clear</button>
|
|
1548
|
-
</div>
|
|
1549
|
-
<span class="note">Layered on top of the built-in assistant prompt — it doesn't replace mlx-bun's own
|
|
1550
|
-
identity/tool guidance. Takes effect on your next message (not retroactively).</span>
|
|
1551
|
-
<div class="sysprompt-presets">
|
|
1552
|
-
<h4>Presets</h4>
|
|
1553
|
-
<div class="sysprompt-preset-row">
|
|
1554
|
-
<select id="sysprompt-preset-select" class="pill" aria-label="Saved presets">
|
|
1555
|
-
<option value="">— presets —</option>
|
|
1556
|
-
</select>
|
|
1557
|
-
<button id="sysprompt-preset-save" type="button" title="Save the current prompt + sampling as a named preset">Save</button>
|
|
1558
|
-
<button id="sysprompt-preset-delete" type="button" title="Delete the selected preset">Delete</button>
|
|
1559
|
-
</div>
|
|
1560
|
-
<span class="note">A preset bundles this system prompt with the current Sampling settings — applying one sets both at once.</span>
|
|
1561
|
-
</div>
|
|
1562
|
-
</div>
|
|
1563
|
-
</span>
|
|
1564
|
-
<span id="chat-sampling-wrap">
|
|
1565
|
-
<span id="chat-sampling" data-spotlight="sampling-pill" class="pill" role="button" tabindex="0" aria-haspopup="true" aria-expanded="false"
|
|
1566
|
-
title="Sampling controls (temperature · top_p · top_k)"><span class="dot"></span>Sampling</span>
|
|
1567
|
-
<div id="chat-sampling-pop" class="samplepop" role="dialog" aria-label="Sampling controls">
|
|
1568
|
-
<div class="samp-head-row">
|
|
1569
|
-
<h4>Sampling</h4>
|
|
1570
|
-
<span id="samp-oneshot-chip" class="samp-oneshot-chip" style="display:none">next msg only</span>
|
|
1571
|
-
</div>
|
|
1572
|
-
<!-- Per-message sampling scope (plan §9 Phase 3, beat matrix Axis 4):
|
|
1573
|
-
"this session" (default) behaves exactly as today; "next message
|
|
1574
|
-
only" arms a one-shot override consumed by the next completed
|
|
1575
|
-
turn (composer.ts's pushSampling/clearOneShotArmed). -->
|
|
1576
|
-
<div class="samp-scope-row">
|
|
1577
|
-
<label for="samp-scope">Apply</label>
|
|
1578
|
-
<select id="samp-scope" aria-label="Sampling scope">
|
|
1579
|
-
<option value="session">this session</option>
|
|
1580
|
-
<option value="next_turn">next message only</option>
|
|
1581
|
-
</select>
|
|
1582
|
-
</div>
|
|
1583
|
-
<div class="samprow">
|
|
1584
|
-
<label for="samp-temp">Temperature</label>
|
|
1585
|
-
<input id="samp-temp" type="range" min="0" max="2" step="0.05" aria-label="Temperature">
|
|
1586
|
-
<output id="samp-temp-val" class="samp-val auto">0.70</output>
|
|
1587
|
-
</div>
|
|
1588
|
-
<div class="samprow">
|
|
1589
|
-
<label for="samp-topp">top_p</label>
|
|
1590
|
-
<input id="samp-topp" type="range" min="0" max="1" step="0.01" aria-label="top_p">
|
|
1591
|
-
<output id="samp-topp-val" class="samp-val auto">0.95</output>
|
|
1592
|
-
</div>
|
|
1593
|
-
<div class="samprow">
|
|
1594
|
-
<label for="samp-topk">top_k</label>
|
|
1595
|
-
<input id="samp-topk" type="range" min="0" max="100" step="1" aria-label="top_k">
|
|
1596
|
-
<output id="samp-topk-val" class="samp-val auto">off</output>
|
|
1597
|
-
</div>
|
|
1598
|
-
<details class="samp-adv" id="samp-advanced">
|
|
1599
|
-
<summary>Advanced</summary>
|
|
1600
|
-
<div class="samprow">
|
|
1601
|
-
<label for="samp-minp">min_p</label>
|
|
1602
|
-
<input id="samp-minp" type="range" min="0" max="0.5" step="0.01" aria-label="min_p">
|
|
1603
|
-
<output id="samp-minp-val" class="samp-val auto">off</output>
|
|
1604
|
-
</div>
|
|
1605
|
-
<div class="samprow">
|
|
1606
|
-
<label for="samp-xtcp">XTC prob</label>
|
|
1607
|
-
<input id="samp-xtcp" type="range" min="0" max="1" step="0.01" aria-label="XTC probability">
|
|
1608
|
-
<output id="samp-xtcp-val" class="samp-val auto">off</output>
|
|
1609
|
-
</div>
|
|
1610
|
-
<div class="samprow">
|
|
1611
|
-
<label for="samp-xtct">XTC thresh</label>
|
|
1612
|
-
<input id="samp-xtct" type="range" min="0" max="1" step="0.01" aria-label="XTC threshold">
|
|
1613
|
-
<output id="samp-xtct-val" class="samp-val auto">off</output>
|
|
1614
|
-
</div>
|
|
1615
|
-
<div class="samprow">
|
|
1616
|
-
<label for="samp-reppen">repetition</label>
|
|
1617
|
-
<input id="samp-reppen" type="range" min="1" max="2" step="0.01" aria-label="repetition penalty">
|
|
1618
|
-
<output id="samp-reppen-val" class="samp-val auto">off</output>
|
|
1619
|
-
</div>
|
|
1620
|
-
<div class="samprow">
|
|
1621
|
-
<label for="samp-repctx">rep. window</label>
|
|
1622
|
-
<input id="samp-repctx" type="range" min="0" max="1024" step="16" aria-label="repetition context size">
|
|
1623
|
-
<output id="samp-repctx-val" class="samp-val auto">off</output>
|
|
1624
|
-
</div>
|
|
1625
|
-
<div class="samprow">
|
|
1626
|
-
<label for="samp-prespen">presence</label>
|
|
1627
|
-
<input id="samp-prespen" type="range" min="-2" max="2" step="0.05" aria-label="presence penalty">
|
|
1628
|
-
<output id="samp-prespen-val" class="samp-val auto">off</output>
|
|
1629
|
-
</div>
|
|
1630
|
-
<div class="samprow">
|
|
1631
|
-
<label for="samp-freqpen">frequency</label>
|
|
1632
|
-
<input id="samp-freqpen" type="range" min="-2" max="2" step="0.05" aria-label="frequency penalty">
|
|
1633
|
-
<output id="samp-freqpen-val" class="samp-val auto">off</output>
|
|
1634
|
-
</div>
|
|
1635
|
-
<div class="seed-row">
|
|
1636
|
-
<label for="samp-seed">seed</label>
|
|
1637
|
-
<input id="samp-seed" type="text" inputmode="numeric" pattern="[0-9]*" placeholder="random" aria-label="seed">
|
|
1638
|
-
</div>
|
|
1639
|
-
</details>
|
|
1640
|
-
<div class="samppop-foot">
|
|
1641
|
-
<span class="note" id="samp-foot-note">Sliders default to this model's recommended values — temperature also follows the thinking toggle. Reset returns everything to defaults.</span>
|
|
1642
|
-
<button id="samp-reset" type="button">Reset</button>
|
|
1643
|
-
</div>
|
|
1644
|
-
</div>
|
|
1645
|
-
</span>
|
|
1646
|
-
<select id="chat-adapter" data-spotlight="adapter-select" class="pill" title="LoRA adapter for this chat (none = base model)">
|
|
1647
|
-
<option value="">no adapter</option>
|
|
1648
|
-
</select>
|
|
1649
|
-
<!-- Adapter routing table entry (plan §5.6/§9 Phase 2): the
|
|
1650
|
-
quick-switch <select> above stays the fast path; this opens
|
|
1651
|
-
the deep three-state surface (available/loaded/selected,
|
|
1652
|
-
RAM cost, stacking) — same adapter list state, not a
|
|
1653
|
-
second source of truth (see adapters-panel.ts). -->
|
|
1654
|
-
<button id="adapters-manage" type="button" title="Manage adapters — routing table, RAM cost, stacking"
|
|
1655
|
-
aria-haspopup="dialog" aria-controls="adapters-overlay" aria-expanded="false">⚙</button>
|
|
1656
|
-
<span id="chat-think" class="pill" role="button" tabindex="0" title="Toggle the model's reasoning"><span class="dot"></span>Thinking</span>
|
|
1657
|
-
</span>
|
|
1658
|
-
</div>
|
|
1659
|
-
<!-- Perf strip: replaces the old bare #chat-tps dim text (plan §5.2,
|
|
1660
|
-
§6.4) with tok/s, TTFT, context-fill, and a server-driven lane
|
|
1661
|
-
badge, always visible (not a popover) — "speed is ambient, not a
|
|
1662
|
-
stat you look up". Context-fill absorbs the old #chat-context
|
|
1663
|
-
span's job (single source of the "how full is my context"
|
|
1664
|
-
reading, no longer duplicated between the hint row and here). -->
|
|
1665
|
-
<div id="chat-perf">
|
|
1666
|
-
<span class="pf-item" id="pf-tps" title="Decode speed for the current/last turn"></span>
|
|
1667
|
-
<span class="pf-item" id="pf-ttft" title="Time to first token"></span>
|
|
1668
|
-
<span class="pf-item" id="pf-fill" title="Context window in use — auto-compacts before it fills"></span>
|
|
1669
|
-
<!-- Lane badge: server-driven only (never client-inferred, plan §2.3
|
|
1670
|
-
risk #5). Hidden until a turn_end frame actually carries `lane`
|
|
1671
|
-
(wave-1 backend work landed the field — src/serve/lane-registry.ts,
|
|
1672
|
-
pi-web.ts turn_end — so this fills in for real turns, not a stub). -->
|
|
1673
|
-
<span class="pf-lane" id="pf-lane"><span class="dot"></span><span id="pf-lane-label"></span></span>
|
|
1674
|
-
</div>
|
|
1675
|
-
</div>
|
|
1676
|
-
</div>
|
|
1677
|
-
</div>
|
|
1678
|
-
</section>
|
|
1679
|
-
|
|
1680
|
-
<!-- ═══════════════════ QUANTIZE ═══════════════════ -->
|
|
1681
|
-
<section data-route="quantize" id="s-quantize">
|
|
1682
|
-
<div class="scroller"><div class="wrap narrow lit">
|
|
1683
|
-
<div class="eyebrow">Workflow · quantize</div>
|
|
1684
|
-
<h1 class="title">Quantize <span class="grad g-blue">a model.</span></h1>
|
|
1685
|
-
<p class="lead">Shrink any supported model to 4 or 8-bit with OptiQ-grade group quantization — smaller on disk,
|
|
1686
|
-
faster to decode, and serveable the moment it's scanned. <strong>No Python in the path.</strong></p>
|
|
1687
|
-
|
|
1688
|
-
<div class="steps" id="q-steps"></div>
|
|
1689
|
-
|
|
1690
|
-
<!-- step 1: source -->
|
|
1691
|
-
<div class="card" data-qstep="0">
|
|
1692
|
-
<h2 style="font-size:24px;margin-bottom:4px">Source model</h2>
|
|
1693
|
-
<p class="lead" style="margin:6px 0 22px;font-size:14.5px">Point at a Hugging Face repo id or a local snapshot path. Inspect first to confirm support and size.</p>
|
|
1694
|
-
<div class="field">
|
|
1695
|
-
<label>Model id or path</label>
|
|
1696
|
-
<div id="q-drop">
|
|
1697
|
-
<input type="text" id="q-model" data-spotlight="quantize-source" placeholder="mlx-community/Qwen3-8B-bf16" autocomplete="off">
|
|
1698
|
-
</div>
|
|
1699
|
-
<input type="file" id="q-folder" webkitdirectory directory style="display:none">
|
|
1700
|
-
<div class="hint">Anything on huggingface.co, an absolute path to a local snapshot, or <strong>choose / drop a folder</strong> below — we read its <code>config.json</code> and find the snapshot on disk.</div>
|
|
1701
|
-
</div>
|
|
1702
|
-
<div class="btnrow">
|
|
1703
|
-
<button class="btn ghost" id="q-browse">Choose folder…</button>
|
|
1704
|
-
<button class="btn ghost" id="q-inspect">Inspect</button>
|
|
1705
|
-
</div>
|
|
1706
|
-
<div id="q-inspect-out"></div>
|
|
1707
|
-
</div>
|
|
1708
|
-
|
|
1709
|
-
<!-- step 2: configure -->
|
|
1710
|
-
<div class="card" data-qstep="1" style="display:none">
|
|
1711
|
-
<h2 style="font-size:24px;margin-bottom:4px">Configure</h2>
|
|
1712
|
-
<p class="lead" style="margin:6px 0 22px;font-size:14.5px">Lower bits = smaller and faster, with some quality cost. 4-bit is the balanced default.</p>
|
|
1713
|
-
<div class="field">
|
|
1714
|
-
<label>Precision</label>
|
|
1715
|
-
<div class="seg" id="q-bits">
|
|
1716
|
-
<button data-v="4" class="on">4-bit</button>
|
|
1717
|
-
<button data-v="8">8-bit</button>
|
|
1718
|
-
</div>
|
|
1719
|
-
<div class="hint" id="q-bits-hint">4-bit · ~4× smaller than bf16 · the recommended default for local serving.</div>
|
|
1720
|
-
</div>
|
|
1721
|
-
<div class="field">
|
|
1722
|
-
<label>Group size</label>
|
|
1723
|
-
<div class="seg" id="q-gs">
|
|
1724
|
-
<button data-v="64" class="on">64</button>
|
|
1725
|
-
<button data-v="32">32</button>
|
|
1726
|
-
</div>
|
|
1727
|
-
<div class="hint">Weights per shared scale. 64 is standard; 32 trades a little size for slightly better fidelity.</div>
|
|
1728
|
-
</div>
|
|
1729
|
-
<div class="field">
|
|
1730
|
-
<label>Bit allocation</label>
|
|
1731
|
-
<div class="seg" id="q-mode">
|
|
1732
|
-
<button data-v="uniform" class="on">Uniform bits</button>
|
|
1733
|
-
<button data-v="mixed">Mixed (target bpw)</button>
|
|
1734
|
-
</div>
|
|
1735
|
-
<div class="hint">Uniform applies the bits above to every layer. Mixed runs OptiQ's sensitivity sweep + knapsack to allocate per-layer precision toward a target average.</div>
|
|
1736
|
-
</div>
|
|
1737
|
-
<div class="field" id="q-bpw-field" style="display:none">
|
|
1738
|
-
<label>Target bits per weight · <span class="num" id="q-bpw-val">5.0</span></label>
|
|
1739
|
-
<input type="range" id="q-bpw" min="3" max="8" step="0.5" value="5" style="accent-color:var(--blue)">
|
|
1740
|
-
<div class="hint">OptiQ measures each layer's sensitivity, then solves a knapsack to pick per-layer 4/8-bit hitting this average bpw (~2–5 min). Lower = smaller; higher = closer to bf16.</div>
|
|
1741
|
-
</div>
|
|
1742
|
-
<div class="btnrow">
|
|
1743
|
-
<button class="btn ghost" data-qback>Back</button>
|
|
1744
|
-
<button class="btn primary" id="q-submit">Start quantize</button>
|
|
1745
|
-
</div>
|
|
1746
|
-
</div>
|
|
1747
|
-
|
|
1748
|
-
<!-- step 3: run -->
|
|
1749
|
-
<div class="card" data-qstep="2" style="display:none">
|
|
1750
|
-
<h2 style="font-size:24px;margin-bottom:16px">Running</h2>
|
|
1751
|
-
<div class="progressbar"><i id="q-bar"></i></div>
|
|
1752
|
-
<div class="progmeta"><span id="q-msg">Starting…</span><span id="q-pct">0%</span></div>
|
|
1753
|
-
<div class="logbox" id="q-log"></div>
|
|
1754
|
-
</div>
|
|
1755
|
-
|
|
1756
|
-
<!-- step 4: done -->
|
|
1757
|
-
<div class="card" data-qstep="3" style="display:none">
|
|
1758
|
-
<h2 style="font-size:24px;margin-bottom:6px"><span class="grad g-green">Done.</span></h2>
|
|
1759
|
-
<p class="lead" style="margin:6px 0 18px;font-size:14.5px">Your quantized model is written to disk.</p>
|
|
1760
|
-
<div class="field"><label>Output directory</label>
|
|
1761
|
-
<pre class="logbox" id="q-out" style="max-height:none;margin-top:0"></pre></div>
|
|
1762
|
-
<div class="flash ok">Written to your model cache and registered automatically — it's already serveable from the Library and the model picker.</div>
|
|
1763
|
-
<div class="grid g4" style="margin-top:14px">
|
|
1764
|
-
<div class="card"><h3>Push to Hugging Face</h3><div class="cap">Publish this quantized model to a Hugging Face repo.</div>
|
|
1765
|
-
<div class="btnrow" style="margin-top:14px"><button class="btn ghost sm" id="q-push">Push to HF</button></div>
|
|
1766
|
-
<div id="q-push-panel"></div>
|
|
1767
|
-
</div>
|
|
1768
|
-
</div>
|
|
1769
|
-
<div class="btnrow"><button class="btn ghost" id="q-again">Quantize another</button></div>
|
|
1770
|
-
</div>
|
|
1771
|
-
</div></div>
|
|
1772
|
-
</section>
|
|
1773
|
-
|
|
1774
|
-
<!-- ═══════════════════ FINE-TUNE ═══════════════════ -->
|
|
1775
|
-
<section data-route="finetune" id="s-finetune">
|
|
1776
|
-
<div class="scroller"><div class="wrap narrow lit">
|
|
1777
|
-
<div class="eyebrow">Workflow · fine-tune</div>
|
|
1778
|
-
<h1 class="title">Fine-tune <span class="grad g-green">a LoRA.</span></h1>
|
|
1779
|
-
<p class="lead">Train a low-rank adapter on top of any local model — SFT or DPO — and watch the loss curve land
|
|
1780
|
-
live. Hot-swappable onto the quantized base, <strong>no full reload.</strong></p>
|
|
1781
|
-
|
|
1782
|
-
<div class="steps" id="f-steps"></div>
|
|
1783
|
-
|
|
1784
|
-
<!-- step 1: base -->
|
|
1785
|
-
<div class="card" data-fstep="0">
|
|
1786
|
-
<h2 style="font-size:24px;margin-bottom:4px">Base model</h2>
|
|
1787
|
-
<p class="lead" style="margin:6px 0 22px;font-size:14.5px">The model you're adapting. Point at a local model directory or snapshot path.</p>
|
|
1788
|
-
<div class="field">
|
|
1789
|
-
<label>Model directory / path</label>
|
|
1790
|
-
<div id="f-drop">
|
|
1791
|
-
<input type="text" id="f-model" data-spotlight="finetune-base" placeholder="/abs/path/to/model or mlx-community/Qwen3-8B-4bit" autocomplete="off">
|
|
1792
|
-
</div>
|
|
1793
|
-
<input type="file" id="f-folder" webkitdirectory directory style="display:none">
|
|
1794
|
-
<div class="hint">An OptiQ quant or any supported local model works — type a path/repo id, or <strong>choose / drop a folder</strong> below.</div>
|
|
1795
|
-
</div>
|
|
1796
|
-
<div class="btnrow">
|
|
1797
|
-
<button class="btn ghost" id="f-browse">Choose folder…</button>
|
|
1798
|
-
<button class="btn primary" id="f-next0">Continue</button>
|
|
1799
|
-
</div>
|
|
1800
|
-
</div>
|
|
1801
|
-
|
|
1802
|
-
<!-- step 2: dataset -->
|
|
1803
|
-
<div class="card" data-fstep="1" style="display:none">
|
|
1804
|
-
<h2 style="font-size:24px;margin-bottom:4px">Dataset</h2>
|
|
1805
|
-
<p class="lead" style="margin:6px 0 22px;font-size:14.5px">A directory with <code>train.jsonl</code> (and optionally <code>valid.jsonl</code>).
|
|
1806
|
-
Standard shapes accepted: <code>{"text":…}</code>, <code>{"prompt":…,"completion":…}</code>, <code>{"messages":[…]}</code>.</p>
|
|
1807
|
-
<div class="field">
|
|
1808
|
-
<label>Dataset directory</label>
|
|
1809
|
-
<input type="text" id="f-data" placeholder="/abs/path/to/dataset" autocomplete="off">
|
|
1810
|
-
</div>
|
|
1811
|
-
<div class="btnrow"><button class="btn ghost" id="f-inspect">Inspect</button></div>
|
|
1812
|
-
<div id="f-inspect-out"></div>
|
|
1813
|
-
<div class="btnrow">
|
|
1814
|
-
<button class="btn ghost" data-fback>Back</button>
|
|
1815
|
-
<button class="btn primary" id="f-next1" disabled>Continue</button>
|
|
1816
|
-
</div>
|
|
1817
|
-
</div>
|
|
1818
|
-
|
|
1819
|
-
<!-- step 3: hyperparameters -->
|
|
1820
|
-
<div class="card" data-fstep="2" style="display:none">
|
|
1821
|
-
<h2 style="font-size:24px;margin-bottom:16px">Hyperparameters</h2>
|
|
1822
|
-
<div class="field">
|
|
1823
|
-
<label>Training objective</label>
|
|
1824
|
-
<div class="seg" id="f-method">
|
|
1825
|
-
<button data-v="sft" class="on">SFT</button>
|
|
1826
|
-
<button data-v="dpo">DPO</button>
|
|
1827
|
-
<button data-v="orpo">ORPO</button>
|
|
1828
|
-
</div>
|
|
1829
|
-
<div class="hint" id="f-method-hint">Supervised fine-tuning — data is messages or prompt+completion.</div>
|
|
1830
|
-
</div>
|
|
1831
|
-
|
|
1832
|
-
<div id="f-dpo-extra" style="display:none">
|
|
1833
|
-
<div class="flash warn" style="margin-bottom:18px">
|
|
1834
|
-
<strong>DPO needs a lower learning rate.</strong> Default 5e-5 (~4× lower than SFT) with warmup. Too high and the preference loss blows out the reward margin. Confirm <em>chosen</em>/<em>rejected</em> are both valid completions of the same prompt.
|
|
1835
|
-
</div>
|
|
1836
|
-
<div class="hpgrid" style="margin-bottom:18px">
|
|
1837
|
-
<div class="field"><label>DPO beta (KL strength)</label><input type="number" id="f-beta" value="0.1" step="0.01" min="0.01" max="1"></div>
|
|
1838
|
-
<div class="field"><label>LR schedule</label>
|
|
1839
|
-
<select id="f-sched"><option value="cosine">cosine (recommended)</option><option value="constant">constant</option></select></div>
|
|
1840
|
-
</div>
|
|
1841
|
-
</div>
|
|
1842
|
-
|
|
1843
|
-
<div id="f-orpo-extra" style="display:none">
|
|
1844
|
-
<div class="flash warn" style="margin-bottom:18px">
|
|
1845
|
-
<strong>ORPO is reference-free.</strong> One monolithic loss (SFT + odds-ratio), no reference model — half the forwards of DPO. Default LR 1e-5 (lower than DPO: the loss carries a full SFT term). λ weights only the odds-ratio term. Data is {prompt, chosen, rejected}.
|
|
1846
|
-
</div>
|
|
1847
|
-
<div class="hpgrid" style="margin-bottom:18px">
|
|
1848
|
-
<div class="field"><label>ORPO λ (odds-ratio weight)</label><input type="number" id="f-orpo-lambda" value="0.1" step="0.05" min="0.01" max="2"></div>
|
|
1849
|
-
<div class="field"><label>LR schedule</label>
|
|
1850
|
-
<select id="f-orpo-sched"><option value="cosine">cosine (recommended)</option><option value="constant">constant</option></select></div>
|
|
1851
|
-
</div>
|
|
1852
|
-
<div class="field" style="margin-bottom:18px">
|
|
1853
|
-
<label>SFT scope</label>
|
|
1854
|
-
<div class="seg" id="f-orpo-scope">
|
|
1855
|
-
<button data-v="full" class="on">full</button>
|
|
1856
|
-
<button data-v="response">response</button>
|
|
1857
|
-
</div>
|
|
1858
|
-
<div class="hint">full = paper/TRL chosen-NLL over the whole sequence; response = pre-2026-07 response-only.</div>
|
|
1859
|
-
</div>
|
|
1860
|
-
</div>
|
|
1861
|
-
|
|
1862
|
-
<div class="hpgrid">
|
|
1863
|
-
<div class="field"><label>Rank</label><input type="number" id="f-rank" value="8" min="1" max="128"></div>
|
|
1864
|
-
<div class="field"><label>Scale (alpha = rank × scale)</label><input type="number" id="f-scale" value="20" step="0.5" min="0"></div>
|
|
1865
|
-
<div class="field"><label>Dropout</label><input type="number" id="f-dropout" value="0" step="0.01" min="0" max="0.5"></div>
|
|
1866
|
-
<div class="field"><label>Target modules</label><input type="text" id="f-modules" value="q_proj,v_proj"></div>
|
|
1867
|
-
<div class="field"><label>num_layers (-1 = all)</label><input type="number" id="f-layers" value="16" min="-1"></div>
|
|
1868
|
-
<div class="field"><label>iters</label><input type="number" id="f-iters" value="500" min="10"></div>
|
|
1869
|
-
<div class="field"><label>batch_size</label><input type="number" id="f-batch" value="1" min="1" max="16"></div>
|
|
1870
|
-
<div class="field"><label>learning rate</label><input type="number" id="f-lr" value="0.0002" step="0.00001" min="0"></div>
|
|
1871
|
-
<div class="field"><label>max_seq_length</label><input type="number" id="f-seq" value="1024" min="128"></div>
|
|
1872
|
-
</div>
|
|
1873
|
-
<div class="btnrow">
|
|
1874
|
-
<button class="btn ghost" data-fback>Back</button>
|
|
1875
|
-
<button class="btn primary" id="f-submit">Start training</button>
|
|
1876
|
-
</div>
|
|
1877
|
-
</div>
|
|
1878
|
-
|
|
1879
|
-
<!-- step 4: train -->
|
|
1880
|
-
<div class="card" data-fstep="3" style="display:none">
|
|
1881
|
-
<h2 style="font-size:24px;margin-bottom:16px">Training</h2>
|
|
1882
|
-
<div class="progressbar green"><i id="f-bar"></i></div>
|
|
1883
|
-
<div class="progmeta"><span id="f-msg">Starting…</span><span id="f-pct">0%</span></div>
|
|
1884
|
-
|
|
1885
|
-
<div class="chartcard">
|
|
1886
|
-
<div class="chart-legend">
|
|
1887
|
-
<span class="lg"><span class="sw" style="background:var(--green)"></span>train loss</span>
|
|
1888
|
-
<span class="lg" id="f-leg-val" style="display:none"><span class="sw" style="background:var(--blue)"></span>val loss</span>
|
|
1889
|
-
</div>
|
|
1890
|
-
<div class="chart-wrap">
|
|
1891
|
-
<svg id="f-chart" viewBox="0 0 600 200" preserveAspectRatio="none" style="width:100%;height:200px;display:block">
|
|
1892
|
-
<text x="300" y="104" text-anchor="middle" fill="var(--dimmer)" font-size="13" id="f-chart-empty">waiting for the first metric…</text>
|
|
1893
|
-
</svg>
|
|
1894
|
-
</div>
|
|
1895
|
-
<div class="chart-stats">
|
|
1896
|
-
<div class="cs"><b>step</b><span class="num" id="f-step">—</span></div>
|
|
1897
|
-
<div class="cs"><b>train loss</b><span class="num grad g-green" id="f-loss">—</span></div>
|
|
1898
|
-
<div class="cs"><b>learning rate</b><span class="num" id="f-curlr">—</span></div>
|
|
1899
|
-
<div class="cs"><b>tok/s</b><span class="num" id="f-curtps">—</span></div>
|
|
1900
|
-
</div>
|
|
1901
|
-
</div>
|
|
1902
|
-
<div class="logbox" id="f-log"></div>
|
|
1903
|
-
</div>
|
|
1904
|
-
|
|
1905
|
-
<!-- step 5: done -->
|
|
1906
|
-
<div class="card" data-fstep="4" style="display:none">
|
|
1907
|
-
<h2 style="font-size:24px;margin-bottom:6px"><span class="grad g-green">Done.</span></h2>
|
|
1908
|
-
<p class="lead" style="margin:6px 0 18px;font-size:14.5px">Adapter trained and saved.</p>
|
|
1909
|
-
<div class="field"><label>Adapter path</label>
|
|
1910
|
-
<pre class="logbox" id="f-out" style="max-height:none;margin-top:0"></pre></div>
|
|
1911
|
-
<div class="grid g4" style="margin-top:8px">
|
|
1912
|
-
<div class="card"><h3>Merge into base</h3><div class="cap">Fold two adapters together (or an adapter into a base adapter) into the weights for a standalone model.</div>
|
|
1913
|
-
<div class="field" style="margin-top:14px;margin-bottom:10px"><label>Adapter A</label><input type="text" id="f-merge-a" placeholder="/abs/path/to/adapter" autocomplete="off"></div>
|
|
1914
|
-
<div class="field" style="margin-bottom:10px"><label>Adapter B</label><input type="text" id="f-merge-b" placeholder="/abs/path/to/other-adapter" autocomplete="off"></div>
|
|
1915
|
-
<div class="btnrow" style="margin-top:0"><button class="btn ghost sm" id="f-merge-go">Merge</button></div>
|
|
1916
|
-
<div id="f-merge-out"></div>
|
|
1917
|
-
</div>
|
|
1918
|
-
<div class="card"><h3>Export model dir</h3><div class="cap">Bundle base + adapter into one drop-in directory.</div>
|
|
1919
|
-
<div class="field" style="margin-top:14px;margin-bottom:10px"><label>Base model</label><input type="text" id="f-exp-base" placeholder="/abs/path/to/base" autocomplete="off"></div>
|
|
1920
|
-
<div class="field" style="margin-bottom:10px"><label>Adapter path</label><input type="text" id="f-exp-adapter" placeholder="/abs/path/to/adapter" autocomplete="off"></div>
|
|
1921
|
-
<div class="btnrow" style="margin-top:0"><button class="btn ghost sm" id="f-exp-go">Export</button></div>
|
|
1922
|
-
<div id="f-exp-out"></div>
|
|
1923
|
-
</div>
|
|
1924
|
-
<div class="card"><h3>Push to HF</h3><div class="cap">Publish the trained adapter to a Hugging Face repo.</div>
|
|
1925
|
-
<div class="btnrow" style="margin-top:14px"><button class="btn ghost sm" id="f-push">Push to HF</button></div>
|
|
1926
|
-
<div id="f-push-panel"></div>
|
|
1927
|
-
</div>
|
|
1928
|
-
</div>
|
|
1929
|
-
<div class="btnrow"><button class="btn ghost" id="f-again">Train another</button></div>
|
|
1930
|
-
</div>
|
|
1931
|
-
</div></div>
|
|
1932
|
-
</section>
|
|
1933
|
-
|
|
1934
|
-
<!-- ═══════════════════ DATASET ═══════════════════ -->
|
|
1935
|
-
<section data-route="dataset" id="s-dataset">
|
|
1936
|
-
<div class="scroller"><div class="wrap lit">
|
|
1937
|
-
<div class="eyebrow">Workflow · dataset</div>
|
|
1938
|
-
<h1 class="title">Build <span class="grad g-pink">a dataset.</span></h1>
|
|
1939
|
-
<p class="lead">Turn pairs, docs, code, and seeds into clean JSONL the fine-tune workflow can consume.
|
|
1940
|
-
Some templates run entirely offline; <strong>LLM-driven ones use the local model on this server.</strong></p>
|
|
1941
|
-
|
|
1942
|
-
<div class="steps" id="d-steps"></div>
|
|
1943
|
-
|
|
1944
|
-
<!-- step 1: pick template -->
|
|
1945
|
-
<div data-dstep="0">
|
|
1946
|
-
<div id="d-templates" class="grid" style="grid-template-columns:repeat(auto-fill,minmax(300px,1fr));margin-top:6px"></div>
|
|
1947
|
-
</div>
|
|
1948
|
-
|
|
1949
|
-
<!-- step 2: fill form -->
|
|
1950
|
-
<div class="card" data-dstep="1" style="display:none;max-width:860px">
|
|
1951
|
-
<h2 style="font-size:24px;margin-bottom:4px" id="d-form-title">—</h2>
|
|
1952
|
-
<p class="lead" style="margin:6px 0 22px;font-size:14.5px" id="d-form-desc"></p>
|
|
1953
|
-
<div id="d-form"></div>
|
|
1954
|
-
<div class="btnrow">
|
|
1955
|
-
<button class="btn ghost" data-dback>Back</button>
|
|
1956
|
-
<button class="btn primary" id="d-submit">Generate</button>
|
|
1957
|
-
</div>
|
|
1958
|
-
</div>
|
|
1959
|
-
|
|
1960
|
-
<!-- step 3: run -->
|
|
1961
|
-
<div class="card" data-dstep="2" style="display:none;max-width:860px">
|
|
1962
|
-
<h2 style="font-size:24px;margin-bottom:16px">Generating</h2>
|
|
1963
|
-
<div class="progressbar"><i id="d-bar"></i></div>
|
|
1964
|
-
<div class="progmeta"><span id="d-msg">Starting…</span><span id="d-pct">0%</span></div>
|
|
1965
|
-
<div class="logbox" id="d-log"></div>
|
|
1966
|
-
</div>
|
|
1967
|
-
|
|
1968
|
-
<!-- step 4: done -->
|
|
1969
|
-
<div class="card" data-dstep="3" style="display:none;max-width:860px">
|
|
1970
|
-
<h2 style="font-size:24px;margin-bottom:6px"><span class="grad g-green">Done.</span></h2>
|
|
1971
|
-
<div class="grid g4" style="margin:6px 0 18px">
|
|
1972
|
-
<div class="card"><h3>Train rows</h3><div class="stat num grad g-green" id="d-ntrain">—</div></div>
|
|
1973
|
-
<div class="card"><h3>Valid rows</h3><div class="stat num grad g-blue" id="d-nvalid">—</div></div>
|
|
1974
|
-
</div>
|
|
1975
|
-
<div class="field"><label>Output directory</label>
|
|
1976
|
-
<pre class="logbox" id="d-out" style="max-height:none;margin-top:0"></pre></div>
|
|
1977
|
-
<div class="flash ok">Point Fine-tune → Dataset at this directory to train on it.</div>
|
|
1978
|
-
<div class="grid g4" style="margin-top:14px">
|
|
1979
|
-
<div class="card"><h3>Push to HF (dataset repo)</h3><div class="cap">Publish this JSONL dataset to a Hugging Face dataset repo.</div>
|
|
1980
|
-
<div class="btnrow" style="margin-top:14px"><button class="btn ghost sm" id="d-push">Push to HF</button></div>
|
|
1981
|
-
<div id="d-push-panel"></div>
|
|
1982
|
-
</div>
|
|
1983
|
-
</div>
|
|
1984
|
-
<div class="btnrow"><button class="btn ghost" id="d-again">Build another</button></div>
|
|
1985
|
-
</div>
|
|
1986
|
-
</div></div>
|
|
1987
|
-
</section>
|
|
1988
|
-
|
|
1989
|
-
<!-- ═══════════════════ STATUS ═══════════════════ -->
|
|
1990
|
-
<section data-route="status" id="s-status">
|
|
1991
|
-
<div class="scroller"><div class="wrap" id="status-page">
|
|
1992
|
-
|
|
1993
|
-
<div style="text-align:center;margin-bottom:18px" data-reveal>
|
|
1994
|
-
<div class="eyebrow" style="margin-bottom:18px">local ai · apple silicon · one binary</div>
|
|
1995
|
-
<h1 class="title grad g-hero" style="font-size:clamp(48px,8vw,110px)">mlx-bun</h1>
|
|
1996
|
-
<p class="lead" style="margin:18px auto 0;text-align:center">The Python-free local AI engine. <strong>Bun + MLX, nothing else.</strong></p>
|
|
1997
|
-
</div>
|
|
1998
|
-
|
|
1999
|
-
<section style="margin-top:56px" data-reveal>
|
|
2000
|
-
<h2>Now serving<span class="soft">.</span></h2>
|
|
2001
|
-
<div class="grid g4" style="margin-top:24px">
|
|
2002
|
-
<div class="card" id="st-model-card" style="grid-column:1 / -1;display:flex;flex-wrap:wrap;align-items:baseline;gap:14px 26px">
|
|
2003
|
-
<div><h3>Model</h3><div id="st-model-id" style="font-size:clamp(20px,2.6vw,32px);font-weight:700;letter-spacing:-.02em;word-break:break-all">—</div></div>
|
|
2004
|
-
<div id="st-model-meta" style="font-size:14.5px;color:var(--dim);font-weight:500">—</div>
|
|
2005
|
-
</div>
|
|
2006
|
-
<div class="card">
|
|
2007
|
-
<h3>Decode · predicted</h3>
|
|
2008
|
-
<div class="stat num grad g-green"><span id="st-tps">—</span><small>tok/s</small></div>
|
|
2009
|
-
<div class="cap" id="st-tps-cap">from weights ÷ bandwidth on this machine</div>
|
|
2010
|
-
</div>
|
|
2011
|
-
<div class="card">
|
|
2012
|
-
<h3>Max safe context</h3>
|
|
2013
|
-
<div class="stat num grad g-blue"><span id="st-safe-ctx">—</span><small>tokens</small></div>
|
|
2014
|
-
<div class="cap">solved from KV growth + prefill transient, never guessed</div>
|
|
2015
|
-
</div>
|
|
2016
|
-
<div class="card">
|
|
2017
|
-
<h3>Memory</h3>
|
|
2018
|
-
<div class="kv"><b>weights</b><span class="num" id="st-weights">—</span></div>
|
|
2019
|
-
<div class="kv"><b>usable</b><span class="num" id="st-usable">—</span></div>
|
|
2020
|
-
<div class="kv"><b>budget</b><span class="num" id="st-budget">—</span></div>
|
|
2021
|
-
<div class="meter warm"><i id="st-mem-bar"></i></div>
|
|
2022
|
-
</div>
|
|
2023
|
-
<div class="card">
|
|
2024
|
-
<h3>Prompt cache</h3>
|
|
2025
|
-
<div class="kv"><b>hit rate</b><span class="num" id="st-pc-hits">—</span></div>
|
|
2026
|
-
<div class="kv"><b>entries</b><span class="num" id="st-pc-entries">—</span></div>
|
|
2027
|
-
<div class="kv"><b>bytes</b><span class="num" id="st-pc-bytes">—</span></div>
|
|
2028
|
-
<div class="meter"><i id="st-pc-bar"></i></div>
|
|
2029
|
-
</div>
|
|
2030
|
-
<div class="card">
|
|
2031
|
-
<h3>Response store</h3>
|
|
2032
|
-
<div class="kv"><b>entries</b><span class="num" id="st-rs-entries">—</span></div>
|
|
2033
|
-
<div class="kv"><b>bytes</b><span class="num" id="st-rs-bytes">—</span></div>
|
|
2034
|
-
<div class="kv"><b>ttl</b><span class="num" id="st-rs-ttl">—</span></div>
|
|
2035
|
-
<div class="meter"><i id="st-rs-bar"></i></div>
|
|
2036
|
-
</div>
|
|
2037
|
-
<div class="card">
|
|
2038
|
-
<h3>KV quantization</h3>
|
|
2039
|
-
<div class="kv"><b>mode</b><span id="st-kv-mode">—</span></div>
|
|
2040
|
-
<div id="st-kv-layers"></div>
|
|
2041
|
-
</div>
|
|
2042
|
-
<div class="card" id="st-glm-expert-card" style="display:none">
|
|
2043
|
-
<h3>GLM expert tiers · last turn</h3>
|
|
2044
|
-
<div class="kv"><b>main cache hit</b><span class="num" id="st-glm-main-hit">—</span></div>
|
|
2045
|
-
<div class="kv"><b>main SSD read</b><span class="num" id="st-glm-main-read">—</span></div>
|
|
2046
|
-
<div class="kv"><b>MTP cache hit</b><span class="num" id="st-glm-mtp-hit">—</span></div>
|
|
2047
|
-
<div class="kv"><b>MTP SSD read</b><span class="num" id="st-glm-mtp-read">—</span></div>
|
|
2048
|
-
<div class="cap" id="st-glm-residency">waiting for the first completed turn</div>
|
|
2049
|
-
</div>
|
|
2050
|
-
<div class="card" id="st-dl-card" style="grid-column:1 / -1;display:none">
|
|
2051
|
-
<h3>Downloads</h3>
|
|
2052
|
-
<div id="st-dl-list"></div>
|
|
2053
|
-
</div>
|
|
2054
|
-
</div>
|
|
2055
|
-
</section>
|
|
2056
|
-
|
|
2057
|
-
<section style="margin-top:80px" data-reveal>
|
|
2058
|
-
<h2>Library<span class="soft">.</span> <span class="grad g-green">Every model, assessed.</span></h2>
|
|
2059
|
-
<p class="lead">Everything in the local cache, each judged by the fit solver for <strong>this machine</strong>.</p>
|
|
2060
|
-
<div class="card tablecard" style="margin-top:26px">
|
|
2061
|
-
<table>
|
|
2062
|
-
<thead><tr><th>Model</th><th>Size</th><th>Status</th><th>Max context</th><th>Decode</th></tr></thead>
|
|
2063
|
-
<tbody id="st-lib-body"><tr><td colspan="5" style="text-align:center;color:var(--dimmer)">waiting for server…</td></tr></tbody>
|
|
2064
|
-
</table>
|
|
2065
|
-
</div>
|
|
2066
|
-
</section>
|
|
2067
|
-
|
|
2068
|
-
<section style="margin-top:80px" data-reveal>
|
|
2069
|
-
<h2>Will it fit<span class="soft">?</span> <span class="grad g-blue">Solved, not guessed.</span></h2>
|
|
2070
|
-
<p class="lead">Weights + KV growth + prefill transient against wired memory — the same assessment that gates every request.</p>
|
|
2071
|
-
<div class="grid g4" style="margin:26px 0 14px">
|
|
2072
|
-
<div class="card">
|
|
2073
|
-
<h3>This machine</h3>
|
|
2074
|
-
<div class="kv"><b>RAM</b><span class="num" id="st-m-ram">—</span></div>
|
|
2075
|
-
<div class="kv"><b>bandwidth</b><span class="num" id="st-m-bw">—</span></div>
|
|
2076
|
-
<div class="kv"><b>verdict</b><span id="st-m-fits">—</span></div>
|
|
2077
|
-
</div>
|
|
2078
|
-
<div class="card">
|
|
2079
|
-
<h3>At max context · <span class="num" id="st-fit-ctx">—</span> tokens</h3>
|
|
2080
|
-
<div class="kv" data-glm-fit style="display:none"><b>artifact on disk</b><span class="num" id="st-f-artifact">—</span></div>
|
|
2081
|
-
<div class="kv"><b id="st-f-weights-label">weights</b><span class="num" id="st-f-weights">—</span></div>
|
|
2082
|
-
<div class="kv" data-glm-fit style="display:none"><b>main expert slab</b><span class="num" id="st-f-main-slab">—</span></div>
|
|
2083
|
-
<div class="kv" data-glm-fit style="display:none"><b>MTP expert slab</b><span class="num" id="st-f-mtp-slab">—</span></div>
|
|
2084
|
-
<div class="kv"><b>kv cache</b><span class="num" id="st-f-kv">—</span></div>
|
|
2085
|
-
<div class="kv"><b id="st-f-transient-label">transient</b><span class="num" id="st-f-transient">—</span></div>
|
|
2086
|
-
<div class="kv"><b>total / usable</b><span class="num" id="st-f-total">—</span></div>
|
|
2087
|
-
<div class="meter"><i id="st-fit-bar"></i></div>
|
|
2088
|
-
</div>
|
|
2089
|
-
</div>
|
|
2090
|
-
<div class="card tablecard">
|
|
2091
|
-
<table>
|
|
2092
|
-
<thead><tr><th>Apple silicon · this model @ <span class="num" id="st-sku-ctx">32,768</span> ctx</th>
|
|
2093
|
-
<th>RAM</th><th>Fits</th><th>Max context</th><th>Decode</th></tr></thead>
|
|
2094
|
-
<tbody id="st-sku-body"><tr><td colspan="5" style="text-align:center;color:var(--dimmer)">waiting for server…</td></tr></tbody>
|
|
2095
|
-
</table>
|
|
2096
|
-
</div>
|
|
2097
|
-
</section>
|
|
2098
|
-
|
|
2099
|
-
<footer style="margin-top:90px;border-top:1px solid var(--hairline);padding-top:26px;display:flex;flex-wrap:wrap;justify-content:space-between;gap:12px;font-size:13px;color:var(--dimmer)">
|
|
2100
|
-
<span>mlx-bun · M-series Macs · no Python anywhere in the serving path</span>
|
|
2101
|
-
<span>server up <span id="st-uptime">—</span> · updated <span id="st-updated">never</span></span>
|
|
2102
|
-
</footer>
|
|
2103
|
-
</div></div>
|
|
2104
|
-
</section>
|
|
2105
|
-
|
|
2106
|
-
<!-- ═══════════════════ ROUTES ═══════════════════ -->
|
|
2107
|
-
<!-- web-ui-pass-plan.md #17: /dag 404s outside a repo checkout (readFileSync
|
|
2108
|
-
on a repo-relative doc, absent from compiled binaries/npm installs).
|
|
2109
|
-
shell.ts's initRoutesProbe() HEAD-checks /dag at boot: on success this
|
|
2110
|
-
iframe renders as-is; on 404 it hides the Routes tab and swaps this
|
|
2111
|
-
section's innerHTML for a graceful note instead of a raw 404 iframe.
|
|
2112
|
-
Could instead embed the artifact server-side like /curves' fallback
|
|
2113
|
-
does — that's server.ts territory, out of scope this wave. -->
|
|
2114
|
-
<section data-route="routes" id="s-routes">
|
|
2115
|
-
<iframe src="/dag" title="Training · inference route map"
|
|
2116
|
-
style="width:100%;height:100%;border:0;display:block"></iframe>
|
|
2117
|
-
</section>
|
|
2118
|
-
|
|
2119
|
-
</div>
|
|
2120
|
-
|
|
2121
|
-
<div id="toasts"></div>
|
|
2122
|
-
|
|
2123
|
-
<!-- App-aware assistant spotlight overlay (plan §6.6, §9 Phase 3, beat
|
|
2124
|
-
matrix Axis 12) — built/positioned by assistant.ts's showSpotlight().
|
|
2125
|
-
data-ui-chrome="assistant" excludes it (and only it) from the DOM
|
|
2126
|
-
snapshot the assistant itself reads, so it never spotlights or
|
|
2127
|
-
narrates its own overlay. NEVER-HIJACK (verbatim §6.6): the whole
|
|
2128
|
-
overlay is pointer-events:none in CSS below — the highlighted control
|
|
2129
|
-
stays clickable straight through the ring and popover. It traps no
|
|
2130
|
-
focus (deliberately absent from shell.ts's trapFocus/closeTopOverlay
|
|
2131
|
-
set — this is not a modal). -->
|
|
2132
|
-
<div id="assistant-spotlight" data-ui-chrome="assistant" aria-hidden="true">
|
|
2133
|
-
<div class="asr-ring"></div>
|
|
2134
|
-
<div class="asr-pop"><div class="asr-pop-text"></div></div>
|
|
2135
|
-
</div>
|
|
2136
|
-
|
|
2137
|
-
<!-- Vendored syntax highlighter (src/web/vendor/hljs.js — highlight.js core +
|
|
2138
|
-
a curated language set, BSD-3-Clause, see that file's header). `defer`
|
|
2139
|
-
means this loads/executes at DOMContentLoaded, which happens well before
|
|
2140
|
-
any real highlighting call (those only fire later, on streamed content
|
|
2141
|
-
or history replay) — app.js (below) only *calls* highlightIn() from
|
|
2142
|
-
async event handlers, so load order here is safe in practice.
|
|
2143
|
-
highlightIn() still guards with `typeof hljs !== "undefined"` for the
|
|
2144
|
-
case this tag is ever stripped or the asset 404s. No CDN; served by
|
|
2145
|
-
src/server.ts at /assets/hljs.js. -->
|
|
2146
|
-
<script defer src="/assets/hljs.js"></script>
|
|
2147
|
-
<!-- The app itself: GENERATED bundle of src/web/src/*.ts (plan §7/§9 Phase 2
|
|
2148
|
-
module split), built by `bun scripts/build-web.ts` into src/web/app.js
|
|
2149
|
-
and served by src/server.ts at /assets/app.js — see that file's header
|
|
2150
|
-
comment. `defer` (like hljs.js above) means both scripts run in document
|
|
2151
|
-
order at DOMContentLoaded, after the DOM this script queries with $()
|
|
2152
|
-
is fully parsed. Edit src/web/src/*.ts, never this bundle or this tag. -->
|
|
2153
|
-
<script defer src="/assets/app.js"></script>
|
|
2154
|
-
</body>
|
|
2155
|
-
</html>
|