llama.cpp
.devops
nix
apps.nix devshells.nix docker.nix jetson-support.nix nixpkgs-instances.nix package-gguf-py.nix package.nix python-scripts.nix scope.nix sif.nix.github
ISSUE_TEMPLATE
010-bug-compilation.yml 011-bug-results.yml 019-bug-misc.yml 020-enhancement.yml 030-research.yml 040-refactor.yml config.ymlworkflows
bench.yml.disabled build-cache.yml build-cmake-pkg.yml build-linux-cross.yml build.yml check-vendor.yml close-issue.yml copilot-setup-steps.yml docker.yml editorconfig.yml gguf-publish.yml labeler.yml pre-tokenizer-hashes.yml python-check-requirements.yml python-lint.yml python-type-check.yml release.yml server-metal.yml server-webui.yml server.yml update-ops-docs.yml winget.ymlbenches
cmake
arm64-apple-clang.cmake arm64-windows-llvm.cmake build-info.cmake common.cmake download-models.cmake git-vars.cmake license.cmake llama-config.cmake.in llama.pc.in riscv64-spacemit-linux-gnu-gcc.cmake x64-windows-llvm.cmakecommon
jinja
README.md caps.cpp caps.h lexer.cpp lexer.h parser.cpp parser.h runtime.cpp runtime.h string.cpp string.h utils.h value.cpp value.hdocs
multimodal
MobileVLM.md gemma3.md glmedge.md granitevision.md llava.md minicpmo2.6.md minicpmo4.0.md minicpmv2.5.md minicpmv2.6.md minicpmv4.0.md minicpmv4.5.mdops
BLAS.csv CANN.csv CPU.csv CUDA.csv Metal.csv OpenCL.csv SYCL.csv Vulkan.csv WebGPU.csv ZenDNN.csv zDNN.csvexamples
llama.android
app
src
lib
.gitignore build.gradle.kts consumer-rules.pro proguard-rules.promodel-conversion
scripts
causal
compare-embeddings-logits.sh compare-logits.py convert-model.sh modelcard.template run-casual-gen-embeddings-org.py run-converted-model-embeddings-logits.sh run-converted-model.sh run-org-model.pyembedding
compare-embeddings-logits.sh convert-model.sh modelcard.template run-converted-model.sh run-original-model.pyutils
__init__.py check-nmse.py common.py compare_tokens.py create-collection-add-model.sh curl-embedding-server.sh hf-add-model-to-collection.py hf-create-collection.py hf-create-model.py hf-upload-gguf-model.py inspect-converted-model.sh inspect-org-model.py perplexity-gen.sh perplexity-run-simple.sh perplexity-run.sh quantize.sh run-embedding-server.sh semantic_check.py tensor-info.pysycl
CMakeLists.txt README.md build.sh ls-sycl-device.cpp run-llama2.sh test.sh win-build-sycl.bat win-run-llama2.bat win-test.batggml
include
ggml-alloc.h ggml-backend.h ggml-blas.h ggml-cann.h ggml-cpp.h ggml-cpu.h ggml-cuda.h ggml-hexagon.h ggml-metal.h ggml-opencl.h ggml-opt.h ggml-rpc.h ggml-sycl.h ggml-virtgpu.h ggml-vulkan.h ggml-webgpu.h ggml-zdnn.h ggml-zendnn.h ggml.h gguf.hsrc
ggml-cann
CMakeLists.txt acl_tensor.cpp acl_tensor.h aclnn_ops.cpp aclnn_ops.h common.h ggml-cann.cppggml-cpu
CMakeLists.txt arch-fallback.h binary-ops.cpp binary-ops.h common.h ggml-cpu-impl.h ggml-cpu.c ggml-cpu.cpp hbm.cpp hbm.h ops.cpp ops.h quants.c quants.h repack.cpp repack.h simd-mappings.h traits.cpp traits.h unary-ops.cpp unary-ops.h vec.cpp vec.hggml-cuda
template-instances
fattn-mma-f16-instance-ncols1_1-ncols2_16.cu fattn-mma-f16-instance-ncols1_1-ncols2_32.cu fattn-mma-f16-instance-ncols1_1-ncols2_8.cu fattn-mma-f16-instance-ncols1_16-ncols2_1.cu fattn-mma-f16-instance-ncols1_16-ncols2_2.cu fattn-mma-f16-instance-ncols1_16-ncols2_4.cu fattn-mma-f16-instance-ncols1_2-ncols2_16.cu fattn-mma-f16-instance-ncols1_2-ncols2_32.cu fattn-mma-f16-instance-ncols1_2-ncols2_4.cu fattn-mma-f16-instance-ncols1_2-ncols2_8.cu fattn-mma-f16-instance-ncols1_32-ncols2_1.cu fattn-mma-f16-instance-ncols1_32-ncols2_2.cu fattn-mma-f16-instance-ncols1_4-ncols2_16.cu fattn-mma-f16-instance-ncols1_4-ncols2_2.cu fattn-mma-f16-instance-ncols1_4-ncols2_4.cu fattn-mma-f16-instance-ncols1_4-ncols2_8.cu fattn-mma-f16-instance-ncols1_64-ncols2_1.cu fattn-mma-f16-instance-ncols1_8-ncols2_1.cu fattn-mma-f16-instance-ncols1_8-ncols2_2.cu fattn-mma-f16-instance-ncols1_8-ncols2_4.cu fattn-mma-f16-instance-ncols1_8-ncols2_8.cu fattn-tile-instance-dkq112-dv112.cu fattn-tile-instance-dkq128-dv128.cu fattn-tile-instance-dkq256-dv256.cu fattn-tile-instance-dkq40-dv40.cu fattn-tile-instance-dkq576-dv512.cu fattn-tile-instance-dkq64-dv64.cu fattn-tile-instance-dkq72-dv72.cu fattn-tile-instance-dkq80-dv80.cu fattn-tile-instance-dkq96-dv96.cu fattn-vec-instance-f16-f16.cu fattn-vec-instance-f16-q4_0.cu fattn-vec-instance-f16-q4_1.cu fattn-vec-instance-f16-q5_0.cu fattn-vec-instance-f16-q5_1.cu fattn-vec-instance-f16-q8_0.cu fattn-vec-instance-q4_0-f16.cu fattn-vec-instance-q4_0-q4_0.cu fattn-vec-instance-q4_0-q4_1.cu fattn-vec-instance-q4_0-q5_0.cu fattn-vec-instance-q4_0-q5_1.cu fattn-vec-instance-q4_0-q8_0.cu fattn-vec-instance-q4_1-f16.cu fattn-vec-instance-q4_1-q4_0.cu fattn-vec-instance-q4_1-q4_1.cu fattn-vec-instance-q4_1-q5_0.cu fattn-vec-instance-q4_1-q5_1.cu fattn-vec-instance-q4_1-q8_0.cu fattn-vec-instance-q5_0-f16.cu fattn-vec-instance-q5_0-q4_0.cu fattn-vec-instance-q5_0-q4_1.cu fattn-vec-instance-q5_0-q5_0.cu fattn-vec-instance-q5_0-q5_1.cu fattn-vec-instance-q5_0-q8_0.cu fattn-vec-instance-q5_1-f16.cu fattn-vec-instance-q5_1-q4_0.cu fattn-vec-instance-q5_1-q4_1.cu fattn-vec-instance-q5_1-q5_0.cu fattn-vec-instance-q5_1-q5_1.cu fattn-vec-instance-q5_1-q8_0.cu fattn-vec-instance-q8_0-f16.cu fattn-vec-instance-q8_0-q4_0.cu fattn-vec-instance-q8_0-q4_1.cu fattn-vec-instance-q8_0-q5_0.cu fattn-vec-instance-q8_0-q5_1.cu fattn-vec-instance-q8_0-q8_0.cu generate_cu_files.py mmf-instance-ncols_1.cu mmf-instance-ncols_10.cu mmf-instance-ncols_11.cu mmf-instance-ncols_12.cu mmf-instance-ncols_13.cu mmf-instance-ncols_14.cu mmf-instance-ncols_15.cu mmf-instance-ncols_16.cu mmf-instance-ncols_2.cu mmf-instance-ncols_3.cu mmf-instance-ncols_4.cu mmf-instance-ncols_5.cu mmf-instance-ncols_6.cu mmf-instance-ncols_7.cu mmf-instance-ncols_8.cu mmf-instance-ncols_9.cu mmq-instance-iq1_s.cu mmq-instance-iq2_s.cu mmq-instance-iq2_xs.cu mmq-instance-iq2_xxs.cu mmq-instance-iq3_s.cu mmq-instance-iq3_xxs.cu mmq-instance-iq4_nl.cu mmq-instance-iq4_xs.cu mmq-instance-mxfp4.cu mmq-instance-q2_k.cu mmq-instance-q3_k.cu mmq-instance-q4_0.cu mmq-instance-q4_1.cu mmq-instance-q4_k.cu mmq-instance-q5_0.cu mmq-instance-q5_1.cu mmq-instance-q5_k.cu mmq-instance-q6_k.cu mmq-instance-q8_0.cuggml-hexagon
htp
CMakeLists.txt act-ops.c argsort-ops.c binary-ops.c cmake-toolchain.cmake cpy-ops.c flash-attn-ops.c get-rows-ops.c hex-dma.c hex-dma.h hex-dump.h hex-fastdiv.h hex-utils.h htp-ctx.h htp-msg.h htp-ops.h htp_iface.idl hvx-arith.h hvx-base.h hvx-copy.h hvx-div.h hvx-dump.h hvx-exp.h hvx-floor.h hvx-inverse.h hvx-reduce.h hvx-scale.h hvx-sigmoid.h hvx-sqrt.h hvx-types.h hvx-utils.h main.c matmul-ops.c rope-ops.c set-rows-ops.c softmax-ops.c sum-rows-ops.c unary-ops.c worker-pool.c worker-pool.hggml-metal
CMakeLists.txt ggml-metal-common.cpp ggml-metal-common.h ggml-metal-context.h ggml-metal-context.m ggml-metal-device.cpp ggml-metal-device.h ggml-metal-device.m ggml-metal-impl.h ggml-metal-ops.cpp ggml-metal-ops.h ggml-metal.cpp ggml-metal.metalggml-opencl
kernels
add.cl add_id.cl argsort.cl clamp.cl concat.cl conv2d.cl conv2d_f16_f32.cl cpy.cl cvt.cl diag_mask_inf.cl div.cl embed_kernel.py expm1.cl fill.cl flash_attn_f16.cl flash_attn_f32.cl flash_attn_f32_f16.cl gelu.cl gemm_moe_mxfp4_f32.cl gemv_moe_mxfp4_f32.cl gemv_noshuffle.cl gemv_noshuffle_general.cl gemv_noshuffle_general_q8_0_f32.cl get_rows.cl glu.cl group_norm.cl im2col_f16.cl im2col_f32.cl mean.cl mul.cl mul_mat_Ab_Bi_8x4.cl mul_mat_f16_f32.cl mul_mm_f16_f32_kq_kqv.cl mul_mm_f16_f32_l4_lm.cl mul_mm_f32_f32_l4_lm.cl mul_mm_q6_k_f32_l4_lm.cl mul_mm_q8_0_f32_8x4.cl mul_mm_q8_0_f32_l4_lm.cl mul_mv_f16_f16.cl mul_mv_f16_f32.cl mul_mv_f16_f32_1row.cl mul_mv_f16_f32_l4.cl mul_mv_f32_f32.cl mul_mv_id_mxfp4_f32.cl mul_mv_id_mxfp4_f32_flat.cl mul_mv_id_q4_0_f32_8x_flat.cl mul_mv_id_q8_0_f32.cl mul_mv_id_q8_0_f32_flat.cl mul_mv_mxfp4_f32.cl mul_mv_mxfp4_f32_flat.cl mul_mv_q4_0_f32.cl mul_mv_q4_0_f32_1d_16x_flat.cl mul_mv_q4_0_f32_1d_8x_flat.cl mul_mv_q4_0_f32_8x_flat.cl mul_mv_q4_0_f32_v.cl mul_mv_q4_k_f32.cl mul_mv_q6_k_f32.cl mul_mv_q6_k_f32_flat.cl mul_mv_q8_0_f32.cl mul_mv_q8_0_f32_flat.cl norm.cl pad.cl relu.cl repeat.cl rms_norm.cl rope.cl scale.cl set_rows.cl sigmoid.cl silu.cl softmax_4_f16.cl softmax_4_f32.cl softmax_f16.cl softmax_f32.cl softplus.cl solve_tri.cl sqr.cl sqrt.cl ssm_conv.cl sub.cl sum_rows.cl tanh.cl transpose.cl tri.cl tsembd.cl upscale.clggml-sycl
CMakeLists.txt add-id.cpp add-id.hpp backend.hpp binbcast.cpp binbcast.hpp common.cpp common.hpp concat.cpp concat.hpp conv.cpp conv.hpp convert.cpp convert.hpp count-equal.cpp count-equal.hpp cpy.cpp cpy.hpp dequantize.hpp dmmv.cpp dmmv.hpp element_wise.cpp element_wise.hpp gemm.hpp getrows.cpp getrows.hpp ggml-sycl.cpp gla.cpp gla.hpp im2col.cpp im2col.hpp mmq.cpp mmq.hpp mmvq.cpp mmvq.hpp norm.cpp norm.hpp outprod.cpp outprod.hpp pad.cpp pad.hpp pad_reflect_1d.cpp pad_reflect_1d.hpp presets.hpp quantize.hpp quants.hpp repeat_back.cpp repeat_back.hpp roll.cpp roll.hpp rope.cpp rope.hpp set.cpp set.hpp set_rows.cpp set_rows.hpp softmax.cpp softmax.hpp ssm_conv.cpp ssm_conv.hpp sycl_hw.cpp sycl_hw.hpp tsembd.cpp tsembd.hpp vecdotq.hpp wkv.cpp wkv.hppggml-virtgpu
backend
CMakeLists.txt apir_cs_ggml-rpc-back.cpp backend-convert.h backend-dispatched-backend.cpp backend-dispatched-buffer-type.cpp backend-dispatched-buffer.cpp backend-dispatched-device.cpp backend-dispatched.cpp backend-dispatched.gen.h backend-dispatched.h backend-virgl-apir.h backend.cppggml-vulkan
vulkan-shaders
CMakeLists.txt abs.comp acc.comp add.comp add1.comp add_id.comp arange.comp argmax.comp argsort.comp argsort_large.comp ceil.comp clamp.comp concat.comp contig_copy.comp conv2d_dw.comp conv2d_mm.comp conv_transpose_1d.comp copy.comp copy_from_quant.comp copy_to_quant.comp copy_transpose.comp cos.comp count_equal.comp count_experts.comp cumsum.comp cumsum_multipass1.comp cumsum_multipass2.comp dequant_f32.comp dequant_funcs.glsl dequant_funcs_cm2.glsl dequant_head.glsl dequant_iq1_m.comp dequant_iq1_s.comp dequant_iq2_s.comp dequant_iq2_xs.comp dequant_iq2_xxs.comp dequant_iq3_s.comp dequant_iq3_xxs.comp dequant_iq4_nl.comp dequant_iq4_xs.comp dequant_mxfp4.comp dequant_q2_k.comp dequant_q3_k.comp dequant_q4_0.comp dequant_q4_1.comp dequant_q4_k.comp dequant_q5_0.comp dequant_q5_1.comp dequant_q5_k.comp dequant_q6_k.comp dequant_q8_0.comp diag.comp diag_mask_inf.comp div.comp exp.comp fill.comp flash_attn.comp flash_attn_base.glsl flash_attn_cm1.comp flash_attn_cm2.comp flash_attn_mask_opt.comp flash_attn_split_k_reduce.comp floor.comp geglu.comp geglu_erf.comp geglu_quick.comp gelu.comp gelu_erf.comp gelu_quick.comp generic_binary_head.glsl generic_head.glsl generic_unary_head.glsl get_rows.comp get_rows_quant.comp glu_head.glsl glu_main.glsl group_norm.comp hardsigmoid.comp hardswish.comp im2col.comp im2col_3d.comp l2_norm.comp leaky_relu.comp log.comp mul.comp mul_mat_split_k_reduce.comp mul_mat_vec.comp mul_mat_vec_base.glsl mul_mat_vec_iface.glsl mul_mat_vec_iq1_m.comp mul_mat_vec_iq1_s.comp mul_mat_vec_iq2_s.comp mul_mat_vec_iq2_xs.comp mul_mat_vec_iq2_xxs.comp mul_mat_vec_iq3_s.comp mul_mat_vec_iq3_xxs.comp mul_mat_vec_nc.comp mul_mat_vec_p021.comp mul_mat_vec_q2_k.comp mul_mat_vec_q3_k.comp mul_mat_vec_q4_k.comp mul_mat_vec_q5_k.comp mul_mat_vec_q6_k.comp mul_mat_vecq.comp mul_mat_vecq_funcs.glsl mul_mm.comp mul_mm_cm2.comp mul_mm_funcs.glsl mul_mm_id_funcs.glsl mul_mmq.comp mul_mmq_funcs.glsl mul_mmq_shmem_types.glsl multi_add.comp neg.comp norm.comp opt_step_adamw.comp opt_step_sgd.comp pad.comp pool2d.comp quantize_q8_1.comp reglu.comp relu.comp repeat.comp repeat_back.comp rms_norm.comp rms_norm_back.comp rms_norm_partials.comp roll.comp rope_funcs.glsl rope_head.glsl rope_multi.comp rope_neox.comp rope_norm.comp rope_params.glsl rope_vision.comp round.comp rte.glsl scale.comp sigmoid.comp silu.comp silu_back.comp sin.comp soft_max.comp soft_max_back.comp soft_max_large1.comp soft_max_large2.comp soft_max_large3.comp soft_max_large_common.glsl softplus.comp solve_tri.comp sqrt.comp square.comp ssm_conv.comp ssm_scan.comp step.comp sub.comp sum_rows.comp sum_rows.glsl swiglu.comp swiglu_oai.comp tanh.comp timestep_embedding.comp topk_argsort.comp topk_moe.comp topk_nary_search.comp tri.comp trunc.comp types.glsl upscale.comp utils.glsl vulkan-shaders-gen.cpp wkv6.comp wkv7.comp xielu.compggml-webgpu
wgsl-shaders
argmax.wgsl argsort.wgsl argsort_merge.wgsl binary.wgsl common_decls.tmpl cpy.tmpl.wgsl cumsum.wgsl embed_wgsl.py flash_attn.wgsl get_rows.tmpl.wgsl glu.tmpl.wgsl memset.wgsl mul_mat.tmpl.wgsl mul_mat_decls.tmpl mul_mat_reg_tile.tmpl.wgsl mul_mat_subgroup_matrix.tmpl.wgsl mul_mat_vec.tmpl.wgsl pad.wgsl rms_norm.wgsl rope.tmpl.wgsl scale.tmpl.wgsl set_rows.wgsl soft_max.tmpl.wgsl sum_rows.wgsl unary.wgslgguf-py
gguf
scripts
gguf_convert_endian.py gguf_dump.py gguf_editor_gui.py gguf_hash.py gguf_new_metadata.py gguf_set_metadata.pygrammars
README.md arithmetic.gbnf c.gbnf chess.gbnf english.gbnf japanese.gbnf json.gbnf json_arr.gbnf list.gbnfmedia
llama0-banner.png llama0-logo.png llama1-banner.png llama1-icon-transparent.png llama1-icon-transparent.svg llama1-icon.png llama1-icon.svg llama1-logo.png llama1-logo.svg matmul.png matmul.svgmodels
templates
Apertus-8B-Instruct.jinja ByteDance-Seed-OSS.jinja CohereForAI-c4ai-command-r-plus-tool_use.jinja CohereForAI-c4ai-command-r7b-12-2024-tool_use.jinja GLM-4.6.jinja Kimi-K2-Instruct.jinja Kimi-K2-Thinking.jinja MiMo-VL.jinja MiniMax-M2.jinja Mistral-Small-3.2-24B-Instruct-2506.jinja NVIDIA-Nemotron-3-Nano-30B-A3B-BF16.jinja NVIDIA-Nemotron-Nano-v2.jinja NousResearch-Hermes-2-Pro-Llama-3-8B-tool_use.jinja NousResearch-Hermes-3-Llama-3.1-8B-tool_use.jinja Qwen-QwQ-32B.jinja Qwen-Qwen2.5-7B-Instruct.jinja Qwen-Qwen3-0.6B.jinja Qwen3-Coder.jinja README.md deepseek-ai-DeepSeek-R1-Distill-Llama-8B.jinja deepseek-ai-DeepSeek-R1-Distill-Qwen-32B.jinja deepseek-ai-DeepSeek-V3.1.jinja fireworks-ai-llama-3-firefunction-v2.jinja google-gemma-2-2b-it.jinja ibm-granite-granite-3.3-2B-Instruct.jinja llama-cpp-deepseek-r1.jinja llama-cpp-lfm2.jinja llama-cpp-rwkv-world.jinja meetkai-functionary-medium-v3.1.jinja meetkai-functionary-medium-v3.2.jinja meta-llama-Llama-3.1-8B-Instruct.jinja meta-llama-Llama-3.2-3B-Instruct.jinja meta-llama-Llama-3.3-70B-Instruct.jinja microsoft-Phi-3.5-mini-instruct.jinja mistralai-Ministral-3-14B-Reasoning-2512.jinja mistralai-Mistral-Nemo-Instruct-2407.jinja moonshotai-Kimi-K2.jinja openai-gpt-oss-120b.jinja unsloth-Apriel-1.5.jinja unsloth-mistral-Devstral-Small-2507.jinja upstage-Solar-Open-100B.jinjarequirements
requirements-all.txt requirements-compare-llama-bench.txt requirements-convert_hf_to_gguf.txt requirements-convert_hf_to_gguf_update.txt requirements-convert_legacy_llama.txt requirements-convert_llama_ggml_to_gguf.txt requirements-convert_lora_to_gguf.txt requirements-gguf_editor_gui.txt requirements-pydantic.txt requirements-server-bench.txt requirements-test-tokenizer-random.txt requirements-tool_bench.txtscripts
bench-models.sh build-info.sh check-requirements.sh compare-commits.sh compare-llama-bench.py compare-logprobs.py create_ops_docs.py debug-test.sh fetch_server_test_models.py gen-authors.sh gen-unicode-data.py get-flags.mk get-hellaswag.sh get-pg.sh get-wikitext-103.sh get-wikitext-2.sh get-winogrande.sh get_chat_template.py hf.sh install-oneapi.bat pr2wt.sh serve-static.js server-bench.py sync-ggml-am.sh sync-ggml.last sync-ggml.sh sync_vendor.py tool_bench.py tool_bench.sh verify-checksum-models.py xxd.cmakesrc
models
afmoe.cpp apertus.cpp arcee.cpp arctic.cpp arwkv7.cpp baichuan.cpp bailingmoe.cpp bailingmoe2.cpp bert.cpp bitnet.cpp bloom.cpp chameleon.cpp chatglm.cpp codeshell.cpp cogvlm.cpp cohere2-iswa.cpp command-r.cpp dbrx.cpp deci.cpp deepseek.cpp deepseek2.cpp dots1.cpp dream.cpp ernie4-5-moe.cpp ernie4-5.cpp exaone-moe.cpp exaone.cpp exaone4.cpp falcon-h1.cpp falcon.cpp gemma-embedding.cpp gemma.cpp gemma2-iswa.cpp gemma3.cpp gemma3n-iswa.cpp glm4-moe.cpp glm4.cpp gpt2.cpp gptneox.cpp granite-hybrid.cpp granite.cpp graph-context-mamba.cpp grok.cpp grovemoe.cpp hunyuan-dense.cpp hunyuan-moe.cpp internlm2.cpp jais.cpp jamba.cpp kimi-linear.cpp lfm2.cpp llada-moe.cpp llada.cpp llama-iswa.cpp llama.cpp maincoder.cpp mamba.cpp mimo2-iswa.cpp minicpm3.cpp minimax-m2.cpp mistral3.cpp models.h modern-bert.cpp mpt.cpp nemotron-h.cpp nemotron.cpp neo-bert.cpp olmo.cpp olmo2.cpp olmoe.cpp openai-moe-iswa.cpp openelm.cpp orion.cpp pangu-embedded.cpp phi2.cpp phi3.cpp plamo.cpp plamo2.cpp plamo3.cpp plm.cpp qwen.cpp qwen2.cpp qwen2moe.cpp qwen2vl.cpp qwen3.cpp qwen35.cpp qwen35moe.cpp qwen3moe.cpp qwen3next.cpp qwen3vl-moe.cpp qwen3vl.cpp refact.cpp rnd1.cpp rwkv6-base.cpp rwkv6.cpp rwkv6qwen2.cpp rwkv7-base.cpp rwkv7.cpp seed-oss.cpp smallthinker.cpp smollm3.cpp stablelm.cpp starcoder.cpp starcoder2.cpp step35-iswa.cpp t5-dec.cpp t5-enc.cpp wavtokenizer-dec.cpp xverse.cpptests
peg-parser
simple-tokenize.cpp simple-tokenize.h test-basic.cpp test-gbnf-generation.cpp test-json-parser.cpp test-json-serialization.cpp test-unicode.cpp tests.htools
cvector-generator
CMakeLists.txt README.md completions.txt cvector-generator.cpp mean.hpp negative.txt pca.hpp positive.txtmtmd
legacy-models
convert_image_encoder_to_gguf.py glmedge-convert-image-encoder-to-gguf.py glmedge-surgery.py llava_surgery.py llava_surgery_v2.py minicpmv-convert-image-encoder-to-gguf.py minicpmv-surgery.pymodels
cogvlm.cpp conformer.cpp glm4v.cpp internvl.cpp kimik25.cpp kimivl.cpp llama4.cpp llava.cpp minicpmv.cpp mobilenetv5.cpp models.h pixtral.cpp qwen2vl.cpp qwen3vl.cpp siglip.cpp whisper-enc.cpp youtuvl.cppserver
public_legacy
colorthemes.css completion.js favicon.ico index-new.html index.html index.js json-schema-to-grammar.mjs loading.html prompt-formats.js style.css system-prompts.js theme-beeninorder.css theme-ketivah.css theme-mangotango.css theme-playground.css theme-polarnight.css theme-snowstorm.csspublic_simplechat
datautils.mjs index.html readme.md simplechat.css simplechat.js simplechat_screens.webp ui.mjstests
unit
test_basic.py test_chat_completion.py test_compat_anthropic.py test_compat_oai_responses.py test_completion.py test_ctx_shift.py test_embedding.py test_infill.py test_lora.py test_rerank.py test_router.py test_security.py test_sleep.py test_slot_save.py test_speculative.py test_template.py test_tokenize.py test_tool_call.py test_vision_api.pywebui
.storybook
ModeWatcherDecorator.svelte TooltipProviderDecorator.svelte main.ts preview.ts vitest.setup.tssrc
lib
components
app
chat
ChatAttachments
ChatAttachmentPreview.svelte ChatAttachmentThumbnailFile.svelte ChatAttachmentThumbnailImage.svelte ChatAttachmentsList.svelte ChatAttachmentsViewAll.svelteChatForm
ChatFormActions
ChatFormActionFileAttachments.svelte ChatFormActionRecord.svelte ChatFormActionSubmit.svelte ChatFormActions.svelteChatMessages
ChatMessage.svelte ChatMessageActions.svelte ChatMessageAssistant.svelte ChatMessageBranchingControls.svelte ChatMessageEditForm.svelte ChatMessageStatistics.svelte ChatMessageSystem.svelte ChatMessageThinkingBlock.svelte ChatMessageUser.svelte ChatMessages.svelteChatScreen
ChatScreen.svelte ChatScreenDragOverlay.svelte ChatScreenHeader.svelte ChatScreenProcessingInfo.sveltedialogs
DialogChatAttachmentPreview.svelte DialogChatAttachmentsViewAll.svelte DialogChatError.svelte DialogChatSettings.svelte DialogConfirmation.svelte DialogConversationSelection.svelte DialogConversationTitleUpdate.svelte DialogEmptyFileAlert.svelte DialogModelInformation.svelte DialogModelNotAvailable.sveltemisc
ActionButton.svelte ActionDropdown.svelte BadgeChatStatistic.svelte BadgeInfo.svelte BadgeModality.svelte CodePreviewDialog.svelte ConversationSelection.svelte CopyToClipboardIcon.svelte KeyboardShortcutInfo.svelte MarkdownContent.svelte RemoveButton.svelte SearchInput.svelte SyntaxHighlightedCode.svelteui
alert-dialog
alert-dialog-action.svelte alert-dialog-cancel.svelte alert-dialog-content.svelte alert-dialog-description.svelte alert-dialog-footer.svelte alert-dialog-header.svelte alert-dialog-overlay.svelte alert-dialog-title.svelte alert-dialog-trigger.svelte index.tscard
card-action.svelte card-content.svelte card-description.svelte card-footer.svelte card-header.svelte card-title.svelte card.svelte index.tsdialog
dialog-close.svelte dialog-content.svelte dialog-description.svelte dialog-footer.svelte dialog-header.svelte dialog-overlay.svelte dialog-title.svelte dialog-trigger.svelte index.tsdropdown-menu
dropdown-menu-checkbox-item.svelte dropdown-menu-content.svelte dropdown-menu-group-heading.svelte dropdown-menu-group.svelte dropdown-menu-item.svelte dropdown-menu-label.svelte dropdown-menu-radio-group.svelte dropdown-menu-radio-item.svelte dropdown-menu-separator.svelte dropdown-menu-shortcut.svelte dropdown-menu-sub-content.svelte dropdown-menu-sub-trigger.svelte dropdown-menu-trigger.svelte index.tspopover
index.ts popover-close.svelte popover-content.svelte popover-portal.svelte popover-trigger.svelte popover.svelteselect
index.ts select-content.svelte select-group-heading.svelte select-group.svelte select-item.svelte select-label.svelte select-scroll-down-button.svelte select-scroll-up-button.svelte select-separator.svelte select-trigger.sveltesheet
index.ts sheet-close.svelte sheet-content.svelte sheet-description.svelte sheet-footer.svelte sheet-header.svelte sheet-overlay.svelte sheet-title.svelte sheet-trigger.sveltesidebar
constants.ts context.svelte.ts index.ts sidebar-content.svelte sidebar-footer.svelte sidebar-group-action.svelte sidebar-group-content.svelte sidebar-group-label.svelte sidebar-group.svelte sidebar-header.svelte sidebar-input.svelte sidebar-inset.svelte sidebar-menu-action.svelte sidebar-menu-badge.svelte sidebar-menu-button.svelte sidebar-menu-item.svelte sidebar-menu-skeleton.svelte sidebar-menu-sub-button.svelte sidebar-menu-sub-item.svelte sidebar-menu-sub.svelte sidebar-menu.svelte sidebar-provider.svelte sidebar-rail.svelte sidebar-separator.svelte sidebar-trigger.svelte sidebar.sveltetable
index.ts table-body.svelte table-caption.svelte table-cell.svelte table-footer.svelte table-head.svelte table-header.svelte table-row.svelte table.svelteconstants
auto-scroll.ts binary-detection.ts default-context.ts floating-ui-constraints.ts icons.ts input-classes.ts latex-protection.ts literal-html.ts localstorage-keys.ts max-bundle-size.ts precision.ts processing-info.ts settings-config.ts supported-file-types.ts table-html-restorer.ts tooltip-config.ts viewport.tsstores
chat.svelte.ts conversations.svelte.ts models.svelte.ts persisted.svelte.ts server.svelte.ts settings.svelte.tsutils
api-headers.ts api-key-validation.ts attachment-display.ts attachment-type.ts audio-recording.ts autoresize-textarea.ts branching.ts browser-only.ts clipboard.ts config-helpers.ts conversation-utils.ts convert-files-to-extra.ts file-preview.ts file-type.ts formatters.ts index.ts is-ime-composing.ts latex-protection.ts modality-file-validation.ts model-names.ts pdf-processing.ts portal-to-body.ts precision.ts process-uploaded-files.ts svg-to-png.ts syntax-highlight-language.ts text-files.ts text.ts webp-to-png.tstests
mapeditor.html
raw
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>ASCII Map Editor</title>
7 <style>
8 :root {
9 --gridline: rgba(0, 0, 0, 0.15);
10 --mono: "SFMono-Regular", Menlo, Consolas, monospace;
11 }
12
13 body {
14 margin: 0;
15 font-family: system-ui, sans-serif;
16 height: 100vh;
17 overflow: hidden;
18 display: flex;
19 flex-direction: column;
20 }
21
22 header {
23 padding: 12px 16px 8px;
24 }
25
26 header h1 {
27 margin: 0 0 4px;
28 font-size: 1rem;
29 }
30
31 header p {
32 margin: 0;
33 }
34
35 .layout {
36 display: grid;
37 grid-template-columns: 280px 1fr;
38 gap: 12px;
39 padding: 0 16px 16px;
40 flex: 1;
41 min-height: 0;
42 }
43
44 .panel,
45 .grid-wrap {
46 border: 1px solid #ccc;
47 padding: 8px;
48 overflow: auto;
49 min-height: 0;
50 }
51
52 .panel h2 {
53 font-size: 0.9rem;
54 margin: 0 0 8px;
55 }
56
57 .controls {
58 display: grid;
59 gap: 10px;
60 }
61
62 .control-group {
63 display: grid;
64 gap: 8px;
65 }
66
67 .row {
68 display: flex;
69 align-items: center;
70 gap: 8px;
71 }
72
73 .palette {
74 display: grid;
75 grid-template-columns: repeat(4, 1fr);
76 gap: 4px;
77 }
78
79 .tile-btn {
80 padding: 4px 0;
81 font-family: var(--mono);
82 }
83
84 .button {
85 padding: 4px 8px;
86 }
87
88 input[type="range"] {
89 width: 100%;
90 }
91
92 .stat {
93 font-family: var(--mono);
94 }
95
96 #gridCanvas {
97 display: block;
98 image-rendering: pixelated;
99 cursor: crosshair;
100 }
101
102 .footer-note {
103 font-size: 0.85rem;
104 }
105
106 @media (max-width: 980px) {
107 .layout {
108 grid-template-columns: 1fr;
109 }
110 }
111 </style>
112 </head>
113 <body>
114 <header></header>
115 <div class="layout">
116 <aside class="panel">
117 <h2>Tools</h2>
118 <div class="controls">
119 <div class="control-group">
120 <div class="row">
121 <span>Tile</span>
122 <span class="stat" id="activeTile">#</span>
123 </div>
124 <div class="palette" id="palette"></div>
125 </div>
126 <div class="control-group">
127 <div class="row">
128 <span>Brush</span>
129 <span class="stat" id="brushLabel">1</span>
130 </div>
131 <input type="range" id="brushSize" min="1" max="5" value="1" />
132 </div>
133 <div class="control-group">
134 <div class="row">
135 <span>Grid</span>
136 <span class="stat" id="gridSize">0 x 0</span>
137 </div>
138 <div class="row">
139 <button class="button secondary" id="toggleGrid">Toggle Gridlines</button>
140 <button class="button secondary" id="clearMap">Clear</button>
141 </div>
142 <div class="row">
143 <button class="button secondary" id="addBorder">Add Border</button>
144 <button class="button secondary" id="addDoubleBorder">Add Double Border</button>
145 </div>
146 <div class="row">
147 <label>
148 W
149 <input type="number" id="gridWidth" min="1" value="118" style="width: 64px" />
150 </label>
151 <label>
152 H
153 <input type="number" id="gridHeight" min="1" value="59" style="width: 64px" />
154 </label>
155 <button class="button secondary" id="resizeGrid">Resize</button>
156 </div>
157 </div>
158 <div class="control-group">
159 <div class="row">
160 <button class="button" id="saveMap">Download .txt</button>
161 <button class="button secondary" id="copyMap">Copy</button>
162 </div>
163 <div class="row">
164 <input type="file" id="fileInput" accept=".txt" />
165 </div>
166 <div class="footer-note" id="status">Loaded maps/map1.txt</div>
167 </div>
168 </div>
169 </aside>
170 <main class="grid-wrap" id="gridWrap">
171 <canvas id="gridCanvas" aria-label="Map grid" role="img"></canvas>
172 </main>
173 </div>
174
175 <script>
176 const storageKey = "mapeditor-state";
177 const defaultCols = 118;
178 const defaultRows = 59;
179 const tileOrder = [
180 "┌",
181 "┐",
182 "└",
183 "┘",
184 "─",
185 "│",
186 "╔",
187 "╗",
188 "╚",
189 "╝",
190 "═",
191 "║",
192 "0",
193 "1",
194 "2",
195 "3",
196 "4",
197 "5",
198 "6",
199 "7",
200 "8",
201 "9",
202 "#",
203 ".",
204 "~",
205 " ",
206 "N",
207 "B",
208 "S",
209 "G",
210 "$",
211 ];
212 const tileLabels = {
213 "#": "Wall",
214 ".": "Floor",
215 "~": "Water",
216 "N": "Cyan",
217 "B": "Red",
218 "S": "Red",
219 "G": "Red",
220 "$": "Gold",
221 "0": "Cyan",
222 "1": "Cyan",
223 "2": "Cyan",
224 "3": "Cyan",
225 "4": "Cyan",
226 "5": "Cyan",
227 "6": "Cyan",
228 "7": "Cyan",
229 "8": "Cyan",
230 "9": "Cyan",
231 " ": "Void",
232 "┌": "Border corner",
233 "┐": "Border corner",
234 "└": "Border corner",
235 "┘": "Border corner",
236 "─": "Border",
237 "│": "Border",
238 "╔": "Double border corner",
239 "╗": "Double border corner",
240 "╚": "Double border corner",
241 "╝": "Double border corner",
242 "═": "Double border",
243 "║": "Double border",
244 };
245
246 const canvas = document.getElementById("gridCanvas");
247 const ctx = canvas.getContext("2d");
248 const gridWrap = document.getElementById("gridWrap");
249 const paletteEl = document.getElementById("palette");
250 const activeTileEl = document.getElementById("activeTile");
251 const brushSizeEl = document.getElementById("brushSize");
252 const brushLabelEl = document.getElementById("brushLabel");
253 const gridSizeEl = document.getElementById("gridSize");
254 const toggleGridBtn = document.getElementById("toggleGrid");
255 const clearBtn = document.getElementById("clearMap");
256 const addBorderBtn = document.getElementById("addBorder");
257 const addDoubleBorderBtn = document.getElementById("addDoubleBorder");
258 const gridWidthEl = document.getElementById("gridWidth");
259 const gridHeightEl = document.getElementById("gridHeight");
260 const resizeGridBtn = document.getElementById("resizeGrid");
261 const saveBtn = document.getElementById("saveMap");
262 const copyBtn = document.getElementById("copyMap");
263 const fileInput = document.getElementById("fileInput");
264 const statusEl = document.getElementById("status");
265
266 let mapData = [];
267 let activeTile = "#";
268 let isPainting = false;
269 let lastCell = null;
270 let showGridLines = true;
271
272 const baseFontSize = 14;
273 const minZoom = 0.5;
274 const maxZoom = 4;
275 let zoom = 1;
276 let cellMetrics = { width: baseFontSize, height: baseFontSize, fontSize: baseFontSize };
277 let devicePixelRatioValue = window.devicePixelRatio || 1;
278 let autoSaveId = null;
279 const tileStyle = {
280 wall: { fill: "#8a6b3e", text: "#251a0b" },
281 floor: { fill: "#f7f7f7", text: "#b9b9b9" },
282 water: { fill: "#4a8fa3", text: "#e9f7f9" },
283 cyan: { fill: "#27c8d1", text: "#07353a" },
284 red: { fill: "#d84545", text: "#3d0a0a" },
285 gold: { fill: "#d7a627", text: "#3a2605" },
286 void: { fill: "#222", text: "#777" },
287 };
288
289 const tileClass = (tile) => {
290 if ("#┌┐└┘─│╔╗╚╝═║".includes(tile)) return "wall";
291 if (tile === ".") return "floor";
292 if (tile === "~") return "water";
293 if ("0123456789".includes(tile)) return "cyan";
294 if (tile === "N") return "cyan";
295 if ("BSG".includes(tile)) return "red";
296 if (tile === "$") return "gold";
297 return "void";
298 };
299
300 const setStatus = (text) => {
301 statusEl.textContent = text;
302 };
303
304 const normalizeMap = (lines) => {
305 const cleanLines = lines.filter((line) => line.length > 0);
306 const width = Math.max(...cleanLines.map((line) => line.length));
307 return cleanLines.map((line) => line.padEnd(width, " ").split(""));
308 };
309
310 const mapToText = () => mapData.map((row) => row.join("")).join("\n");
311
312 const buildCanvas = () => {
313 if (!mapData.length) return;
314 const rows = mapData.length;
315 const cols = mapData[0].length;
316 resizeCanvas(cols, rows);
317 gridSizeEl.textContent = `${cols} x ${rows}`;
318 gridWidthEl.value = cols;
319 gridHeightEl.value = rows;
320 renderAll();
321 };
322
323 const resizeCanvas = (cols, rows) => {
324 const fontSize = Math.max(8, Math.round(baseFontSize * zoom));
325 devicePixelRatioValue = window.devicePixelRatio || 1;
326 ctx.setTransform(devicePixelRatioValue, 0, 0, devicePixelRatioValue, 0, 0);
327 ctx.font = `${fontSize}px ${getComputedStyle(document.documentElement).getPropertyValue("--mono")}`;
328 const metrics = ctx.measureText("M");
329 const glyphWidth = Math.max(1, Math.ceil(metrics.width));
330 const glyphHeight = Math.max(
331 1,
332 Math.ceil((metrics.actualBoundingBoxAscent || fontSize) + (metrics.actualBoundingBoxDescent || 0))
333 );
334 cellMetrics = { width: glyphWidth, height: glyphHeight, fontSize };
335 canvas.style.width = `${cols * cellMetrics.width}px`;
336 canvas.style.height = `${rows * cellMetrics.height}px`;
337 canvas.width = Math.round(cols * cellMetrics.width * devicePixelRatioValue);
338 canvas.height = Math.round(rows * cellMetrics.height * devicePixelRatioValue);
339 ctx.setTransform(devicePixelRatioValue, 0, 0, devicePixelRatioValue, 0, 0);
340 ctx.font = `${fontSize}px ${getComputedStyle(document.documentElement).getPropertyValue("--mono")}`;
341 ctx.textAlign = "left";
342 ctx.textBaseline = "top";
343 };
344
345 const drawCell = (x, y, tile) => {
346 const type = tileClass(tile);
347 const style = tileStyle[type];
348 const px = x * cellMetrics.width;
349 const py = y * cellMetrics.height;
350 ctx.fillStyle = style.fill;
351 ctx.fillRect(px, py, cellMetrics.width, cellMetrics.height);
352 if (showGridLines) {
353 ctx.strokeStyle = getComputedStyle(document.documentElement)
354 .getPropertyValue("--gridline")
355 .trim();
356 ctx.strokeRect(
357 px + 0.5,
358 py + 0.5,
359 Math.max(1, cellMetrics.width - 1),
360 Math.max(1, cellMetrics.height - 1)
361 );
362 }
363 if (tile !== " ") {
364 ctx.fillStyle = style.text;
365 ctx.fillText(tile, px, py);
366 }
367 };
368
369 const renderAll = () => {
370 const rows = mapData.length;
371 const cols = mapData[0].length;
372 for (let y = 0; y < rows; y += 1) {
373 for (let x = 0; x < cols; x += 1) {
374 drawCell(x, y, mapData[y][x]);
375 }
376 }
377 };
378
379 const updateCell = (x, y, tile) => {
380 if (!mapData[y] || mapData[y][x] === undefined) return;
381 mapData[y][x] = tile;
382 drawCell(x, y, tile);
383 };
384
385 const paintAt = (x, y) => {
386 const size = Number(brushSizeEl.value);
387 const half = Math.floor(size / 2);
388 for (let dy = -half; dy <= half; dy += 1) {
389 for (let dx = -half; dx <= half; dx += 1) {
390 updateCell(x + dx, y + dy, activeTile);
391 }
392 }
393 };
394
395 const drawLine = (from, to) => {
396 const dx = Math.abs(to.x - from.x);
397 const dy = Math.abs(to.y - from.y);
398 const sx = from.x < to.x ? 1 : -1;
399 const sy = from.y < to.y ? 1 : -1;
400 let err = dx - dy;
401 let x = from.x;
402 let y = from.y;
403 while (true) {
404 paintAt(x, y);
405 if (x === to.x && y === to.y) break;
406 const e2 = err * 2;
407 if (e2 > -dy) {
408 err -= dy;
409 x += sx;
410 }
411 if (e2 < dx) {
412 err += dx;
413 y += sy;
414 }
415 }
416 };
417
418 const setActiveTile = (tile) => {
419 activeTile = tile;
420 activeTileEl.textContent = tile === " " ? "(space)" : tile;
421 paletteEl.querySelectorAll(".tile-btn").forEach((btn) => {
422 btn.classList.toggle("active", btn.dataset.tile === tile);
423 });
424 };
425
426 const setupPalette = () => {
427 paletteEl.innerHTML = "";
428 tileOrder.forEach((tile) => {
429 const btn = document.createElement("button");
430 btn.type = "button";
431 btn.className = "tile-btn";
432 btn.dataset.tile = tile;
433 btn.textContent = tile === " " ? "space" : tile;
434 btn.title = tileLabels[tile] || tile;
435 btn.addEventListener("click", () => setActiveTile(tile));
436 paletteEl.appendChild(btn);
437 });
438 setActiveTile(activeTile);
439 };
440
441 const loadFromText = (text, sourceLabel) => {
442 const lines = text.replace(/\r/g, "").split("\n");
443 mapData = normalizeMap(lines);
444 buildCanvas();
445 setStatus(`Loaded ${sourceLabel}`);
446 };
447
448 const loadDefaultMap = () => {
449 const row = ".".repeat(defaultCols);
450 const empty = Array.from({ length: defaultRows }, () => row).join("\n");
451 loadFromText(empty, "empty map");
452 };
453
454 const saveState = () => {
455 if (!mapData.length) return;
456 const state = {
457 map: mapToText(),
458 cols: mapData[0].length,
459 rows: mapData.length,
460 zoom,
461 brush: Number(brushSizeEl.value),
462 tile: activeTile,
463 grid: showGridLines,
464 };
465 try {
466 localStorage.setItem(storageKey, JSON.stringify(state));
467 setStatus("Autosaved");
468 } catch (error) {
469 setStatus("Autosave failed");
470 }
471 };
472
473 const loadState = () => {
474 try {
475 const raw = localStorage.getItem(storageKey);
476 if (!raw) return false;
477 const state = JSON.parse(raw);
478 if (!state?.map) return false;
479 loadFromText(state.map, "local autosave");
480 if (state.zoom) applyZoom(state.zoom);
481 if (state.brush) {
482 brushSizeEl.value = state.brush;
483 brushLabelEl.textContent = state.brush;
484 }
485 if (state.tile) setActiveTile(state.tile);
486 if (typeof state.grid === "boolean") showGridLines = state.grid;
487 renderAll();
488 return true;
489 } catch (error) {
490 return false;
491 }
492 };
493
494 brushSizeEl.addEventListener("input", () => {
495 brushLabelEl.textContent = brushSizeEl.value;
496 });
497
498 toggleGridBtn.addEventListener("click", () => {
499 showGridLines = !showGridLines;
500 renderAll();
501 });
502
503 clearBtn.addEventListener("click", () => {
504 mapData = mapData.map((row) => row.map(() => "."));
505 buildCanvas();
506 setStatus("Cleared to floor tiles");
507 });
508
509 resizeGridBtn.addEventListener("click", () => {
510 const newCols = Math.max(1, Number(gridWidthEl.value) || 1);
511 const newRows = Math.max(1, Number(gridHeightEl.value) || 1);
512 const oldRows = mapData.length;
513 const oldCols = mapData[0]?.length || 0;
514 const resized = [];
515 for (let y = 0; y < newRows; y += 1) {
516 const row = [];
517 for (let x = 0; x < newCols; x += 1) {
518 const tile = mapData[y]?.[x] ?? ".";
519 row.push(tile);
520 }
521 resized.push(row);
522 }
523 mapData = resized;
524 buildCanvas();
525 setStatus(`Resized from ${oldCols}x${oldRows} to ${newCols}x${newRows}`);
526 });
527
528 addBorderBtn.addEventListener("click", () => {
529 if (!mapData.length) return;
530 const rows = mapData.length;
531 const cols = mapData[0].length;
532 if (cols < 2 || rows < 2) return;
533 updateCell(0, 0, "┌");
534 updateCell(cols - 1, 0, "┐");
535 updateCell(0, rows - 1, "└");
536 updateCell(cols - 1, rows - 1, "┘");
537 for (let x = 1; x < cols - 1; x += 1) {
538 updateCell(x, 0, "─");
539 updateCell(x, rows - 1, "─");
540 }
541 for (let y = 1; y < rows - 1; y += 1) {
542 updateCell(0, y, "│");
543 updateCell(cols - 1, y, "│");
544 }
545 setStatus("Added box border tiles");
546 });
547
548 addDoubleBorderBtn.addEventListener("click", () => {
549 if (!mapData.length) return;
550 const rows = mapData.length;
551 const cols = mapData[0].length;
552 if (cols < 2 || rows < 2) return;
553 updateCell(0, 0, "╔");
554 updateCell(cols - 1, 0, "╗");
555 updateCell(0, rows - 1, "╚");
556 updateCell(cols - 1, rows - 1, "╝");
557 for (let x = 1; x < cols - 1; x += 1) {
558 updateCell(x, 0, "═");
559 updateCell(x, rows - 1, "═");
560 }
561 for (let y = 1; y < rows - 1; y += 1) {
562 updateCell(0, y, "║");
563 updateCell(cols - 1, y, "║");
564 }
565 setStatus("Added double border tiles");
566 });
567
568 saveBtn.addEventListener("click", () => {
569 const blob = new Blob([mapToText()], { type: "text/plain" });
570 const url = URL.createObjectURL(blob);
571 const link = document.createElement("a");
572 link.href = url;
573 link.download = "map.txt";
574 document.body.appendChild(link);
575 link.click();
576 link.remove();
577 URL.revokeObjectURL(url);
578 setStatus("Downloaded map.txt");
579 });
580
581 copyBtn.addEventListener("click", async () => {
582 try {
583 await navigator.clipboard.writeText(mapToText());
584 setStatus("Copied map to clipboard");
585 } catch (error) {
586 setStatus("Clipboard unavailable");
587 }
588 });
589
590 fileInput.addEventListener("change", async (event) => {
591 const file = event.target.files[0];
592 if (!file) return;
593 const text = await file.text();
594 loadFromText(text, file.name);
595 });
596
597 canvas.addEventListener("contextmenu", (event) => {
598 event.preventDefault();
599 });
600
601 const getCellFromEvent = (event) => {
602 const rect = canvas.getBoundingClientRect();
603 const x = Math.floor((event.clientX - rect.left) / cellMetrics.width);
604 const y = Math.floor((event.clientY - rect.top) / cellMetrics.height);
605 if (x < 0 || y < 0) return null;
606 if (!mapData.length) return null;
607 if (y >= mapData.length || x >= mapData[0].length) return null;
608 return { x, y };
609 };
610
611 const applyZoom = (newZoom, anchor) => {
612 const rows = mapData.length;
613 const cols = mapData[0]?.length || 0;
614 if (!rows || !cols) return;
615 const clamped = Math.min(maxZoom, Math.max(minZoom, newZoom));
616 if (clamped === zoom) return;
617 const sizeBeforeX = cellMetrics.width;
618 const sizeBeforeY = cellMetrics.height;
619 const offsetX = anchor?.x ?? gridWrap.clientWidth / 2;
620 const offsetY = anchor?.y ?? gridWrap.clientHeight / 2;
621 const worldX = (gridWrap.scrollLeft + offsetX) / sizeBeforeX;
622 const worldY = (gridWrap.scrollTop + offsetY) / sizeBeforeY;
623 zoom = clamped;
624 resizeCanvas(cols, rows);
625 renderAll();
626 gridWrap.scrollLeft = worldX * cellMetrics.width - offsetX;
627 gridWrap.scrollTop = worldY * cellMetrics.height - offsetY;
628 };
629
630 const handleCanvasEvent = (event, overrideTile = null) => {
631 const cell = getCellFromEvent(event);
632 if (!cell) return;
633 const previousTile = activeTile;
634 if (overrideTile !== null) activeTile = overrideTile;
635 if (event.shiftKey && lastCell) {
636 drawLine(lastCell, cell);
637 } else {
638 paintAt(cell.x, cell.y);
639 }
640 if (overrideTile !== null) activeTile = previousTile;
641 lastCell = cell;
642 };
643
644 canvas.addEventListener("mousedown", (event) => {
645 if (event.button === 2) {
646 isPainting = true;
647 handleCanvasEvent(event, ".");
648 return;
649 }
650 isPainting = true;
651 handleCanvasEvent(event);
652 });
653
654 canvas.addEventListener("mousemove", (event) => {
655 if (!isPainting) return;
656 if (event.buttons === 2) {
657 handleCanvasEvent(event, ".");
658 return;
659 }
660 handleCanvasEvent(event);
661 });
662
663 canvas.addEventListener("wheel", (event) => {
664 event.preventDefault();
665 const delta = event.deltaY > 0 ? -0.1 : 0.1;
666 const rect = canvas.getBoundingClientRect();
667 const anchor = {
668 x: event.clientX - rect.left,
669 y: event.clientY - rect.top,
670 };
671 applyZoom(zoom + delta, anchor);
672 });
673
674 document.addEventListener("mouseup", () => {
675 isPainting = false;
676 lastCell = null;
677 });
678
679 document.addEventListener("keydown", (event) => {
680 if (event.target.matches("input, textarea")) return;
681 if (event.key >= "1" && event.key <= "4") {
682 const tile = tileOrder[Number(event.key) - 1];
683 setActiveTile(tile);
684 }
685 });
686
687 setupPalette();
688 if (!loadState()) loadDefaultMap();
689 autoSaveId = setInterval(saveState, 5000);
690 </script>
691 </body>
692</html>