changeset 186:8cf4ec5e2191 hg-web

Fixed merge conflict.
author MrJuneJune <me@mrjunejune.com>
date Fri, 23 Jan 2026 22:38:59 -0800
parents fed99fc04e12 (diff) dfdd66825396 (current diff)
children a69485d9f2e1
files MODULE.bazel.lock gui_ze/gui_ze.bzl markdown_converter/BUILD mrjunejune/BUILD seobeo/s_web.c seobeo/seobeo.h seobeo/seobeo_internal.h
diffstat 15 files changed, 872 insertions(+), 374 deletions(-) [+]
line wrap: on
line diff
--- a/MODULE.bazel.lock	Fri Jan 23 22:22:30 2026 -0800
+++ b/MODULE.bazel.lock	Fri Jan 23 22:38:59 2026 -0800
@@ -10,6 +10,13 @@
     "https://bcr.bazel.build/modules/abseil-cpp/20230802.1/MODULE.bazel": "fa92e2eb41a04df73cdabeec37107316f7e5272650f81d6cc096418fe647b915",
     "https://bcr.bazel.build/modules/abseil-cpp/20240116.1/MODULE.bazel": "37bcdb4440fbb61df6a1c296ae01b327f19e9bb521f9b8e26ec854b6f97309ed",
     "https://bcr.bazel.build/modules/abseil-cpp/20240116.2/MODULE.bazel": "73939767a4686cd9a520d16af5ab440071ed75cec1a876bf2fcfaf1f71987a16",
+    "https://bcr.bazel.build/modules/abseil-cpp/20250127.0/MODULE.bazel": "d1086e248cda6576862b4b3fe9ad76a214e08c189af5b42557a6e1888812c5d5",
+    "https://bcr.bazel.build/modules/abseil-cpp/20250127.1/MODULE.bazel": "c4a89e7ceb9bf1e25cf84a9f830ff6b817b72874088bf5141b314726e46a57c1",
+    "https://bcr.bazel.build/modules/abseil-cpp/20250512.1/MODULE.bazel": "d209fdb6f36ffaf61c509fcc81b19e81b411a999a934a032e10cd009a0226215",
+    "https://bcr.bazel.build/modules/abseil-cpp/20250814.1/MODULE.bazel": "51f2312901470cdab0dbdf3b88c40cd21c62a7ed58a3de45b365ddc5b11bcab2",
+    "https://bcr.bazel.build/modules/abseil-cpp/20250814.1/source.json": "cea3901d7e299da7320700abbaafe57a65d039f10d0d7ea601c4a66938ea4b0c",
+    "https://bcr.bazel.build/modules/abseil-py/2.1.0/MODULE.bazel": "5ebe5bf853769c65707e5c28f216798f7a4b1042015e6a36e6d03094d94bec8a",
+    "https://bcr.bazel.build/modules/abseil-py/2.1.0/source.json": "0e8fc4f088ce07099c1cd6594c20c7ddbb48b4b3c0849b7d94ba94be88ff042b",
     "https://bcr.bazel.build/modules/abseil-cpp/20250127.1/MODULE.bazel": "c4a89e7ceb9bf1e25cf84a9f830ff6b817b72874088bf5141b314726e46a57c1",
     "https://bcr.bazel.build/modules/abseil-cpp/20250512.1/MODULE.bazel": "d209fdb6f36ffaf61c509fcc81b19e81b411a999a934a032e10cd009a0226215",
     "https://bcr.bazel.build/modules/abseil-cpp/20250814.1/MODULE.bazel": "51f2312901470cdab0dbdf3b88c40cd21c62a7ed58a3de45b365ddc5b11bcab2",
@@ -20,6 +27,11 @@
     "https://bcr.bazel.build/modules/apple_support/1.21.1/MODULE.bazel": "5809fa3efab15d1f3c3c635af6974044bac8a4919c62238cce06acee8a8c11f1",
     "https://bcr.bazel.build/modules/apple_support/1.24.2/MODULE.bazel": "0e62471818affb9f0b26f128831d5c40b074d32e6dda5a0d3852847215a41ca4",
     "https://bcr.bazel.build/modules/apple_support/1.24.2/source.json": "2c22c9827093250406c5568da6c54e6fdf0ef06238def3d99c71b12feb057a8d",
+    "https://bcr.bazel.build/modules/aspect_bazel_lib/1.42.3/MODULE.bazel": "e4529e12d8cd5b828e2b5960d07d3ec032541740d419d7d5b859cabbf5b056f9",
+    "https://bcr.bazel.build/modules/aspect_bazel_lib/1.42.3/source.json": "80cb66069ad626e0921555cd2bf278286fd7763fae2450e564e351792e8303f4",
+    "https://bcr.bazel.build/modules/aspect_rules_js/1.42.0/MODULE.bazel": "f19e6b4a16f77f8cf3728eac1f60dbfd8e043517fd4f4dbf17a75a6c50936d62",
+    "https://bcr.bazel.build/modules/aspect_rules_js/1.42.0/source.json": "abbb3eac3b6af76b8ce230a9a901c6d08d93f4f5ffd55314bf630827dddee57e",
+    "https://bcr.bazel.build/modules/bazel_features/1.1.0/MODULE.bazel": "cfd42ff3b815a5f39554d97182657f8c4b9719568eb7fded2b9135f084bf760b",
     "https://bcr.bazel.build/modules/aspect_bazel_lib/2.14.0/MODULE.bazel": "2b31ffcc9bdc8295b2167e07a757dbbc9ac8906e7028e5170a3708cecaac119f",
     "https://bcr.bazel.build/modules/aspect_bazel_lib/2.19.2/MODULE.bazel": "30dfabbfae0139b1f0036e01c201dd4c0167da3017f0b7ef3820d78e07622989",
     "https://bcr.bazel.build/modules/aspect_bazel_lib/2.19.2/source.json": "89d8e5d7088ae33972733099b756dae71e1647ae684ab50b26adfa853c506d01",
@@ -42,6 +54,7 @@
     "https://bcr.bazel.build/modules/bazel_features/1.3.0/MODULE.bazel": "cdcafe83ec318cda34e02948e81d790aab8df7a929cec6f6969f13a489ccecd9",
     "https://bcr.bazel.build/modules/bazel_features/1.30.0/MODULE.bazel": "a14b62d05969a293b80257e72e597c2da7f717e1e69fa8b339703ed6731bec87",
     "https://bcr.bazel.build/modules/bazel_features/1.33.0/MODULE.bazel": "8b8dc9d2a4c88609409c3191165bccec0e4cb044cd7a72ccbe826583303459f6",
+    "https://bcr.bazel.build/modules/bazel_features/1.33.0/source.json": "13617db3930328c2cd2807a0f13d52ca870ac05f96db9668655113265147b2a6",
     "https://bcr.bazel.build/modules/bazel_features/1.39.0/MODULE.bazel": "28739425c1fc283c91931619749c832b555e60bcd1010b40d8441ce0a5cf726d",
     "https://bcr.bazel.build/modules/bazel_features/1.39.0/source.json": "f63cbeb4c602098484d57001e5a07d31cb02bbccde9b5e2c9bf0b29d05283e93",
     "https://bcr.bazel.build/modules/bazel_features/1.4.1/MODULE.bazel": "e45b6bb2350aff3e442ae1111c555e27eac1d915e77775f6fdc4b351b758b5d7",
@@ -63,6 +76,21 @@
     "https://bcr.bazel.build/modules/bazel_skylib/1.8.1/MODULE.bazel": "88ade7293becda963e0e3ea33e7d54d3425127e0a326e0d17da085a5f1f03ff6",
     "https://bcr.bazel.build/modules/bazel_skylib/1.8.2/MODULE.bazel": "69ad6927098316848b34a9142bcc975e018ba27f08c4ff403f50c1b6e646ca67",
     "https://bcr.bazel.build/modules/bazel_skylib/1.8.2/source.json": "34a3c8bcf233b835eb74be9d628899bb32999d3e0eadef1947a0a562a2b16ffb",
+    "https://bcr.bazel.build/modules/bazel_worker_api/0.0.1/MODULE.bazel": "02a13b77321773b2042e70ee5e4c5e099c8ddee4cf2da9cd420442c36938d4bd",
+    "https://bcr.bazel.build/modules/bazel_worker_api/0.0.4/MODULE.bazel": "460aa12d01231a80cce03c548287b433b321d205b0028ae596728c35e5ee442e",
+    "https://bcr.bazel.build/modules/bazel_worker_api/0.0.4/source.json": "d353c410d47a8b65d09fa98e83d57ebec257a2c2b9c6e42d6fda1cb25e5464a5",
+    "https://bcr.bazel.build/modules/bazel_worker_java/0.0.4/MODULE.bazel": "82494a01018bb7ef06d4a17ec4cd7a758721f10eb8b6c820a818e70d669500db",
+    "https://bcr.bazel.build/modules/bazel_worker_java/0.0.4/source.json": "a2d30458fd86cf022c2b6331e652526fa08e17573b2f5034a9dbcacdf9c2583c",
+    "https://bcr.bazel.build/modules/buildozer/8.2.1/MODULE.bazel": "61e9433c574c2bd9519cad7fa66b9c1d2b8e8d5f3ae5d6528a2c2d26e68d874d",
+    "https://bcr.bazel.build/modules/buildozer/8.2.1/source.json": "7c33f6a26ee0216f85544b4bca5e9044579e0219b6898dd653f5fb449cf2e484",
+    "https://bcr.bazel.build/modules/emsdk/4.0.17/MODULE.bazel": "a10c49d7063e5dffeec0c5b43753ab2575179fbb679851059861af7d2a781c6a",
+    "https://bcr.bazel.build/modules/emsdk/4.0.17/source.json": "8463664bc6319425d29ef0d7edd8660daf1bf0ec33c367420243d4db35e0f482",
+    "https://bcr.bazel.build/modules/gazelle/0.32.0/MODULE.bazel": "b499f58a5d0d3537f3cf5b76d8ada18242f64ec474d8391247438bf04f58c7b8",
+    "https://bcr.bazel.build/modules/gazelle/0.33.0/MODULE.bazel": "a13a0f279b462b784fb8dd52a4074526c4a2afe70e114c7d09066097a46b3350",
+    "https://bcr.bazel.build/modules/gazelle/0.34.0/MODULE.bazel": "abdd8ce4d70978933209db92e436deb3a8b737859e9354fb5fd11fb5c2004c8a",
+    "https://bcr.bazel.build/modules/gazelle/0.36.0/MODULE.bazel": "e375d5d6e9a6ca59b0cb38b0540bc9a05b6aa926d322f2de268ad267a2ee74c0",
+    "https://bcr.bazel.build/modules/gazelle/0.40.0/MODULE.bazel": "42ba5378ebe845fca43989a53186ab436d956db498acde790685fe0e8f9c6146",
+    "https://bcr.bazel.build/modules/gazelle/0.40.0/source.json": "1e5ef6e4d8b9b6836d93273c781e78ff829ea2e077afef7a57298040fa4f010a",
     "https://bcr.bazel.build/modules/buildifier_prebuilt/7.1.2/MODULE.bazel": "d18b017dddf219626ea5b04028ff0db2397655fbdaa9d9258a6443ddafb303ab",
     "https://bcr.bazel.build/modules/buildifier_prebuilt/7.1.2/source.json": "5fb3a2433f6508f4b23934eaa2986d604ae65aff95d7476d1273bf5cd434eedc",
     "https://bcr.bazel.build/modules/buildozer/8.2.1/MODULE.bazel": "61e9433c574c2bd9519cad7fa66b9c1d2b8e8d5f3ae5d6528a2c2d26e68d874d",
@@ -103,6 +131,9 @@
     "https://bcr.bazel.build/modules/protobuf/29.0-rc3/MODULE.bazel": "33c2dfa286578573afc55a7acaea3cada4122b9631007c594bf0729f41c8de92",
     "https://bcr.bazel.build/modules/protobuf/29.1/MODULE.bazel": "557c3457560ff49e122ed76c0bc3397a64af9574691cb8201b4e46d4ab2ecb95",
     "https://bcr.bazel.build/modules/protobuf/3.19.0/MODULE.bazel": "6b5fbb433f760a99a22b18b6850ed5784ef0e9928a72668b66e4d7ccd47db9b0",
+    "https://bcr.bazel.build/modules/protobuf/3.19.2/MODULE.bazel": "532ffe5f2186b69fdde039efe6df13ba726ff338c6bc82275ad433013fa10573",
+    "https://bcr.bazel.build/modules/protobuf/3.19.6/MODULE.bazel": "9233edc5e1f2ee276a60de3eaa47ac4132302ef9643238f23128fea53ea12858",
+    "https://bcr.bazel.build/modules/protobuf/31.1/MODULE.bazel": "379a389bb330b7b8c1cdf331cc90bf3e13de5614799b3b52cdb7c6f389f6b38e",
     "https://bcr.bazel.build/modules/protobuf/32.1/MODULE.bazel": "89cd2866a9cb07fee9ff74c41ceace11554f32e0d849de4e23ac55515cfada4d",
     "https://bcr.bazel.build/modules/protobuf/33.4/MODULE.bazel": "114775b816b38b6d0ca620450d6b02550c60ceedfdc8d9a229833b34a223dc42",
     "https://bcr.bazel.build/modules/protobuf/33.4/source.json": "555f8686b4c7d6b5ba731fbea13bf656b4bfd9a7ff629c1d9d3f6e1d6155de79",
@@ -149,7 +180,9 @@
     "https://bcr.bazel.build/modules/rules_java/7.10.0/MODULE.bazel": "530c3beb3067e870561739f1144329a21c851ff771cd752a49e06e3dc9c2e71a",
     "https://bcr.bazel.build/modules/rules_java/7.12.2/MODULE.bazel": "579c505165ee757a4280ef83cda0150eea193eed3bef50b1004ba88b99da6de6",
     "https://bcr.bazel.build/modules/rules_java/7.2.0/MODULE.bazel": "06c0334c9be61e6cef2c8c84a7800cef502063269a5af25ceb100b192453d4ab",
+    "https://bcr.bazel.build/modules/rules_java/7.4.0/MODULE.bazel": "a592852f8a3dd539e82ee6542013bf2cadfc4c6946be8941e189d224500a8934",
     "https://bcr.bazel.build/modules/rules_java/7.6.1/MODULE.bazel": "2f14b7e8a1aa2f67ae92bc69d1ec0fa8d9f827c4e17ff5e5f02e91caa3b2d0fe",
+    "https://bcr.bazel.build/modules/rules_java/8.13.0/MODULE.bazel": "0444ebf737d144cf2bb2ccb368e7f1cce735264285f2a3711785827c1686625e",
     "https://bcr.bazel.build/modules/rules_java/8.3.2/MODULE.bazel": "7336d5511ad5af0b8615fdc7477535a2e4e723a357b6713af439fe8cf0195017",
     "https://bcr.bazel.build/modules/rules_java/8.5.1/MODULE.bazel": "d8a9e38cc5228881f7055a6079f6f7821a073df3744d441978e7a43e20226939",
     "https://bcr.bazel.build/modules/rules_java/8.6.1/MODULE.bazel": "f4808e2ab5b0197f094cabce9f4b006a27766beb6a9975931da07099560ca9c2",
@@ -158,9 +191,11 @@
     "https://bcr.bazel.build/modules/rules_jvm_external/4.4.2/MODULE.bazel": "a56b85e418c83eb1839819f0b515c431010160383306d13ec21959ac412d2fe7",
     "https://bcr.bazel.build/modules/rules_jvm_external/5.1/MODULE.bazel": "33f6f999e03183f7d088c9be518a63467dfd0be94a11d0055fe2d210f89aa909",
     "https://bcr.bazel.build/modules/rules_jvm_external/5.2/MODULE.bazel": "d9351ba35217ad0de03816ef3ed63f89d411349353077348a45348b096615036",
+    "https://bcr.bazel.build/modules/rules_jvm_external/6.2/MODULE.bazel": "36a6e52487a855f33cb960724eb56547fa87e2c98a0474c3acad94339d7f8e99",
     "https://bcr.bazel.build/modules/rules_jvm_external/6.3/MODULE.bazel": "c998e060b85f71e00de5ec552019347c8bca255062c990ac02d051bb80a38df0",
     "https://bcr.bazel.build/modules/rules_jvm_external/6.7/MODULE.bazel": "e717beabc4d091ecb2c803c2d341b88590e9116b8bf7947915eeb33aab4f96dd",
     "https://bcr.bazel.build/modules/rules_jvm_external/6.7/source.json": "5426f412d0a7fc6b611643376c7e4a82dec991491b9ce5cb1cfdd25fe2e92be4",
+    "https://bcr.bazel.build/modules/rules_kotlin/1.9.5/MODULE.bazel": "043a16a572f610558ec2030db3ff0c9938574e7dd9f58bded1bb07c0192ef025",
     "https://bcr.bazel.build/modules/rules_kotlin/1.9.6/MODULE.bazel": "d269a01a18ee74d0335450b10f62c9ed81f2321d7958a2934e44272fe82dcef3",
     "https://bcr.bazel.build/modules/rules_kotlin/1.9.6/source.json": "2faa4794364282db7c06600b7e5e34867a564ae91bda7cae7c29c64e9466b7d5",
     "https://bcr.bazel.build/modules/rules_license/0.0.3/MODULE.bazel": "627e9ab0247f7d1e05736b59dbb1b6871373de5ad31c3011880b4133cafd4bd0",
@@ -189,12 +224,16 @@
     "https://bcr.bazel.build/modules/rules_python/0.31.0/MODULE.bazel": "93a43dc47ee570e6ec9f5779b2e64c1476a6ce921c48cc9a1678a91dd5f8fd58",
     "https://bcr.bazel.build/modules/rules_python/0.33.2/MODULE.bazel": "3e036c4ad8d804a4dad897d333d8dce200d943df4827cb849840055be8d2e937",
     "https://bcr.bazel.build/modules/rules_python/0.4.0/MODULE.bazel": "9208ee05fd48bf09ac60ed269791cf17fb343db56c8226a720fbb1cdf467166c",
+    "https://bcr.bazel.build/modules/rules_python/1.0.0/MODULE.bazel": "898a3d999c22caa585eb062b600f88654bf92efb204fa346fb55f6f8edffca43",
     "https://bcr.bazel.build/modules/rules_python/1.1.0/MODULE.bazel": "57e01abae22956eb96d891572490d20e07d983e0c065de0b2170cafe5053e788",
     "https://bcr.bazel.build/modules/rules_python/1.3.0/MODULE.bazel": "8361d57eafb67c09b75bf4bbe6be360e1b8f4f18118ab48037f2bd50aa2ccb13",
     "https://bcr.bazel.build/modules/rules_python/1.4.1/MODULE.bazel": "8991ad45bdc25018301d6b7e1d3626afc3c8af8aaf4bc04f23d0b99c938b73a6",
     "https://bcr.bazel.build/modules/rules_python/1.6.0/MODULE.bazel": "7e04ad8f8d5bea40451cf80b1bd8262552aa73f841415d20db96b7241bd027d8",
     "https://bcr.bazel.build/modules/rules_python/1.7.0/MODULE.bazel": "d01f995ecd137abf30238ad9ce97f8fc3ac57289c8b24bd0bf53324d937a14f8",
     "https://bcr.bazel.build/modules/rules_python/1.7.0/source.json": "028a084b65dcf8f4dc4f82f8778dbe65df133f234b316828a82e060d81bdce32",
+    "https://bcr.bazel.build/modules/rules_robolectric/4.14.1.2/MODULE.bazel": "d44fec647d0aeb67b9f3b980cf68ba634976f3ae7ccd6c07d790b59b87a4f251",
+    "https://bcr.bazel.build/modules/rules_robolectric/4.14.1.2/source.json": "37c10335f2361c337c5c1f34ed36d2da70534c23088062b33a8bdaab68aa9dea",
+    "https://bcr.bazel.build/modules/rules_shell/0.1.2/MODULE.bazel": "66e4ca3ce084b04af0b9ff05ff14cab4e5df7503973818bb91cbc6cda08d32fc",
     "https://bcr.bazel.build/modules/rules_shell/0.2.0/MODULE.bazel": "fda8a652ab3c7d8fee214de05e7a9916d8b28082234e8d2c0094505c5268ed3c",
     "https://bcr.bazel.build/modules/rules_shell/0.3.0/MODULE.bazel": "de4402cd12f4cc8fda2354fce179fdb068c0b9ca1ec2d2b17b3e21b24c1a937b",
     "https://bcr.bazel.build/modules/rules_shell/0.4.0/MODULE.bazel": "0f8f11bb3cd11755f0b48c1de0bbcf62b4b34421023aa41a2fc74ef68d9584f0",
@@ -208,6 +247,7 @@
     "https://bcr.bazel.build/modules/rules_swift/3.1.2/source.json": "e85761f3098a6faf40b8187695e3de6d97944e98abd0d8ce579cb2daf6319a66",
     "https://bcr.bazel.build/modules/stardoc/0.5.1/MODULE.bazel": "1a05d92974d0c122f5ccf09291442580317cdd859f07a8655f1db9a60374f9f8",
     "https://bcr.bazel.build/modules/stardoc/0.5.3/MODULE.bazel": "c7f6948dae6999bf0db32c1858ae345f112cacf98f174c7a8bb707e41b974f1c",
+    "https://bcr.bazel.build/modules/stardoc/0.5.4/MODULE.bazel": "6569966df04610b8520957cb8e97cf2e9faac2c0309657c537ab51c16c18a2a4",
     "https://bcr.bazel.build/modules/stardoc/0.6.2/MODULE.bazel": "7060193196395f5dd668eda046ccbeacebfd98efc77fed418dbe2b82ffaa39fd",
     "https://bcr.bazel.build/modules/stardoc/0.7.0/MODULE.bazel": "05e3d6d30c099b6770e97da986c53bd31844d7f13d41412480ea265ac9e8079c",
     "https://bcr.bazel.build/modules/stardoc/0.7.2/MODULE.bazel": "fc152419aa2ea0f51c29583fab1e8c99ddefd5b3778421845606ee628629e0e5",
@@ -227,6 +267,7 @@
     "https://bcr.bazel.build/modules/zlib/1.3.1/MODULE.bazel": "751c9940dcfe869f5f7274e1295422a34623555916eb98c174c1e945594bf198"
   },
   "selectedYankedVersions": {},
+  "moduleExtensions": {},
   "moduleExtensions": {
     "@@buildifier_prebuilt+//:defs.bzl%buildifier_prebuilt_deps_extension": {
       "general": {
--- a/gui_ze/gui_ze.bzl	Fri Jan 23 22:22:30 2026 -0800
+++ b/gui_ze/gui_ze.bzl	Fri Jan 23 22:38:59 2026 -0800
@@ -111,11 +111,14 @@
     command = """
       cp -r third_party/bun/** . \
       && cp -r {src_folder}/** .  \
-      && export NODE_PATH=./node_modules && {bun_path} build {input_path} --outfile {output_path}
+      && cp -r ./bazel-out/k8-fastbuild/bin/hg-web/src/** src \
+      && ls src \
+      && export NODE_PATH=./node_modules && {bun_path} build {input_path} --outfile {output_path} --target browser
       """.format(
       bun_path = ctx.executable._bun.path,
       src_folder = ctx.attr.src_folder,
-      input_path = ctx.file.src.path.split("/")[-1],
+      # Fix this lol
+      input_path = "/".join(ctx.file.src.path.split("/")[-2:]),
       output_path = out.path,
     ),
     progress_message = "Bundling {} with Bun!\n\n".format(ctx.file.src.path),
@@ -208,7 +211,7 @@
     ctx.actions.run_shell(
       inputs = [src],
       outputs = [out],
-      command = "cp \"$1\" \"$2\"",
+      command = "cp -r \"$1\" \"$2\"",
       arguments = [src.path, out.path],
     )
     outs.append(out)
--- a/hg-web/BUILD	Fri Jan 23 22:22:30 2026 -0800
+++ b/hg-web/BUILD	Fri Jan 23 22:38:59 2026 -0800
@@ -1,8 +1,9 @@
 load("@rules_cc//cc:cc_binary.bzl", "cc_binary")
-load("//gui_ze:gui_ze.bzl", "move_files_into_dir", "bundle")
+load("//gui_ze:gui_ze.bzl", "move_files_into_dir", "bundle", "bun_build")
 
+# External
 move_files_into_dir(
-  name = "compiled_ts",
+  name = "external_js_ts_moved",
   srcs = [
     "//markdown_converter:markdown_to_html",
   ],
@@ -10,16 +11,67 @@
 )
 
 filegroup(
-  name = "src_files",
-  srcs = glob(["src/**"]) + [":compiled_ts"],
+  name = "external_js_ts",
+  srcs = [":external_js_ts_moved"],
+)
+
+# Internal
+filegroup(
+  name = "raw_file",
+  srcs = glob(["src/**"]),
+)
+
+filegroup(
+  name = "all_ts_files",
+  srcs = [":external_js_ts"] + glob([
+      "**/*.ts",
+      "**/*.tsx",
+      "**/*.js",
+      "**/*.jsx",
+  ], allow_empty=True)
 )
 
+# Generate js file...
+bun_build(
+  name = "page",
+  src = "src/main.tsx",
+  src_folder = "hg-web",
+  data = [
+    "//third_party/bun:bun_files",
+    ":all_ts_files",
+  ],
+  visibility = ["//visibility:public"],
+)
+
+move_files_into_dir(
+  name = "compiled_ts",
+  srcs = [
+    ":page",
+  ],
+  dest = "src",
+)
+
+move_files_into_dir(
+  name = "public_files",
+  srcs = [
+    "//mrjunejune:public_files"
+  ],
+  dest = "src/public",
+)
+
+filegroup(
+  name = "src_files",
+  srcs = [":raw_file", ":compiled_ts", "public_files"],
+)
+
+# Binary
 cc_binary(
   name = "hg_web_server",
   srcs = ["main.c"],
-  deps = ["//seobeo:seobeo_server"],
+  deps = [
+    "//seobeo:seobeo",
+  ],
   data = [":src_files"],
-  defines = ["REPO_ROOT=\\\"\"/home/mrjunejune/zenbu\"\\\""],
 )
 
 bundle(
@@ -27,12 +79,10 @@
   binary = ":hg_web_server",
 )
 
-
 cc_binary(
   name = "hg_web_server_debug",
   srcs = ["main.c"],
-  deps = ["//seobeo:seobeo_tcp_server_ws_debug"],
+  deps = ["//seobeo:seobeo"],
   data = [":src_files"],
-  defines = ["REPO_ROOT=\\\"\"/home/mrjunejune/zenbu\"\\\""],
 )
 
--- a/hg-web/main.c	Fri Jan 23 22:22:30 2026 -0800
+++ b/hg-web/main.c	Fri Jan 23 22:38:59 2026 -0800
@@ -11,44 +11,17 @@
 #include <netdb.h>
 
 #define HG_SERVE_HOST "127.0.0.1"
-#define HG_SERVE_PORT 4444
+#define HG_SERVE_PORT "4444"
 
 #define MAX_PATH 4096
 
-// TODO: Move this to seobeo....
-// Asked AI to create this lol, probably should learn to decode it myself..
-static void url_decode(char *dst, const char *src)
-{
-  char a, b;
-  while (*src) {
-    if ((*src == '%') &&
-        ((a = src[1]) && (b = src[2])) &&
-        (isxdigit(a) && isxdigit(b))) {
-      if (a >= 'a') a -= 'a'-'A';
-      if (a >= 'A') a -= ('A' - 10);
-      else a -= '0';
-      if (b >= 'a') b -= 'a'-'A';
-      if (b >= 'A') b -= ('A' - 10);
-      else b -= '0';
-      *dst++ = 16*a+b;
-      src+=3;
-    } else if (*src == '+') {
-      *dst++ = ' ';
-      src++;
-    } else {
-      *dst++ = *src++;
-    }
-  }
-  *dst = '\0';
-}
-
 static char* sanitize_path(const char *input_path, Dowa_Arena *arena)
 {
   if (!input_path || strlen(input_path) == 0)
   {
-    char *empty = Dowa_Arena_Allocate(arena, 1);
-    empty[0] = '\0';
-    return empty;
+  char *empty = Dowa_Arena_Allocate(arena, 1);
+  empty[0] = '\0';
+  return empty;
   }
 
   size_t len = strlen(input_path);
@@ -59,9 +32,9 @@
   {
     if (input_path[i] == '.' && (i == 0 || input_path[i-1] == '/')) {
       if (i + 1 < len && input_path[i+1] == '.') {
-        // Skip ".."
-        i++;
-        continue;
+      // Skip ".."
+      i++;
+      continue;
       }
       // Skip "."
       continue;
@@ -72,153 +45,40 @@
 
   // Remove leading/trailing slashes
   while (result[0] == '/')
-    memmove(result, result + 1, strlen(result));
+  memmove(result, result + 1, strlen(result));
   while (j > 0 && result[j-1] == '/')
-    result[--j] = '\0';
+  result[--j] = '\0';
 
   return result;
 }
 
-// Helper to connect to hg serve
-static int hg_proxy_connect(void)
+Seobeo_Client_Response  *hg_proxy_request(
+  const char *method,
+  const char *path,
+  const char *req_body,
+  const char *hg_custom)
 {
-    int sock = socket(AF_INET, SOCK_STREAM, 0);
-    if (sock < 0)
-    {
-        Seobeo_Log(SEOBEO_DEBUG, "Failed to create socket\n");
-        return -1;
-    }
-
-    struct sockaddr_in server_addr;
-    memset(&server_addr, 0, sizeof(server_addr));
-    server_addr.sin_family = AF_INET;
-    server_addr.sin_port = htons(HG_SERVE_PORT);
-    inet_pton(AF_INET, HG_SERVE_HOST, &server_addr.sin_addr);
-
-    if (connect(sock, (struct sockaddr*)&server_addr, sizeof(server_addr)) < 0)
-    {
-        Seobeo_Log(SEOBEO_DEBUG, "Failed to connect to hg serve at %s:%d\n", HG_SERVE_HOST, HG_SERVE_PORT);
-        close(sock);
-        return -1;
-    }
-
-    return sock;
-}
-
-// Generic helper to proxy a request to hg serve and get the response body
-// Returns allocated body on success, NULL on failure
-// out_status and out_content_type are optional output parameters
-// out_body_len returns the actual body length (for binary content)
-static char* hg_proxy_request(
-    const char *method,
-    const char *path,
-    const char *req_body,
-    size_t body_len,
-    char *out_status,       // should be at least 4 bytes
-    char *out_content_type, // should be at least 256 bytes
-    size_t *out_body_len,   // optional: returns actual body length
-    Dowa_Arena *arena)
-{
-    int sock = hg_proxy_connect();
-    if (sock < 0) return NULL;
-
-    // Build HTTP request
-    char http_request[MAX_PATH * 2];
-    snprintf(http_request, sizeof(http_request),
-        "%s %s HTTP/1.1\r\n"
-        "Host: %s:%d\r\n"
-        "Connection: close\r\n"
-        "Accept: application/json, text/plain, */*\r\n"
-        "Content-Length: %zu\r\n"
-        "\r\n",
-        method, path, HG_SERVE_HOST, HG_SERVE_PORT, body_len);
-
-    Seobeo_Log(SEOBEO_DEBUG, "HG Proxy request: %s %s\n", method, path);
-
-    if (send(sock, http_request, strlen(http_request), 0) < 0)
-    {
-        close(sock);
-        return NULL;
-    }
-
-    if (body_len > 0 && req_body)
-    {
-        send(sock, req_body, body_len, 0);
-    }
+  char full_path[MAX_PATH];
+  snprintf(full_path, MAX_PATH, "http://%s:%s%s", HG_SERVE_HOST, HG_SERVE_PORT, path);
+  Seobeo_Log(SEOBEO_DEBUG, "HG Proxy PATH %s\n", full_path);
+  Seobeo_Client_Request *p_req = Seobeo_Client_Request_Create(full_path);
+  Seobeo_Client_Request_Set_Method(p_req, method);
+  Seobeo_Client_Request_Add_Header_Array(p_req, "User-Agent: Seobeo/1.0");
+  Seobeo_Client_Request_Add_Header_Array(p_req, "Accept: application/json");
 
-    // Read response
-    int buffer_size = 1024 * 1024 * 5; // 5MB
-    char *response_buf = Dowa_Arena_Allocate(arena, buffer_size);
-    size_t total_read = 0;
-    ssize_t bytes_read;
-
-    while ((bytes_read = recv(sock, response_buf + total_read, buffer_size - total_read - 1, 0)) > 0)
-    {
-        total_read += bytes_read;
-        if (total_read >= (size_t)(buffer_size - 1)) break;
-    }
-    response_buf[total_read] = '\0';
-    close(sock);
-
-    Seobeo_Log(SEOBEO_DEBUG, "HG Proxy: received %zu bytes\n", total_read);
-
-    // Parse response headers - use memmem to handle binary content
-    char *headers_end = NULL;
-    for (size_t i = 0; i + 3 < total_read; i++)
-    {
-        if (response_buf[i] == '\r' && response_buf[i+1] == '\n' &&
-            response_buf[i+2] == '\r' && response_buf[i+3] == '\n')
-        {
-            headers_end = response_buf + i;
-            break;
-        }
-    }
-    if (!headers_end) return NULL;
+  if (hg_custom && hg_custom[0] != '\0')
+  {
+    char buffer[1024];
+    snprintf(buffer, 1024, "x-hgarg-1: %s", hg_custom);
+    Seobeo_Client_Request_Add_Header_Array(p_req, buffer);
+    Seobeo_Log(SEOBEO_DEBUG, "HG CUSTOM %s\n", buffer);
+  }
 
-    // Extract status
-    if (out_status && strncmp(response_buf, "HTTP/", 5) == 0)
-    {
-        char *status_start = strchr(response_buf, ' ');
-        if (status_start)
-        {
-            strncpy(out_status, status_start + 1, 3);
-            out_status[3] = '\0';
-        }
-    }
-
-    // Extract content-type
-    if (out_content_type)
-    {
-        out_content_type[0] = '\0';
-        char *ct_header = strcasestr(response_buf, "Content-Type:");
-        if (ct_header && ct_header < headers_end)
-        {
-            ct_header += 13;
-            while (*ct_header == ' ') ct_header++;
-            char *ct_end = strpbrk(ct_header, "\r\n");
-            if (ct_end)
-            {
-                size_t ct_len = ct_end - ct_header;
-                if (ct_len < 256)
-                {
-                    strncpy(out_content_type, ct_header, ct_len);
-                    out_content_type[ct_len] = '\0';
-                }
-            }
-        }
-    }
-
-    // Return body (copy to fresh allocation for clean pointer)
-    char *body = headers_end + 4;
-    size_t body_size = total_read - (body - response_buf);
-
-    if (out_body_len) *out_body_len = body_size;
-
-    char *result = Dowa_Arena_Allocate(arena, body_size + 1);
-    memcpy(result, body, body_size);
-    result[body_size] = '\0';
-
-    return result;
+  if (req_body)
+    Seobeo_Client_Request_Set_Body(p_req, req_body, strlen(req_body));
+  Seobeo_Client_Response *p_resp = Seobeo_Client_Request_Execute(p_req);
+  Seobeo_Client_Request_Destroy(p_req);
+  return p_resp;
 }
 
 Seobeo_Request_Entry* ApiListDirectory(Seobeo_Request_Entry *req, Dowa_Arena *arena)
@@ -229,7 +89,7 @@
   const char *rel_path = path_kv ? ((Seobeo_Request_Entry*)path_kv)->value : "";
 
   char *decoded_path = Dowa_Arena_Allocate(arena, strlen(rel_path) + 1);
-  url_decode(decoded_path, rel_path);
+  Seobeo_Url_Decode(decoded_path, rel_path);
 
   char *safe_path = sanitize_path(decoded_path, arena);
 
@@ -241,14 +101,11 @@
   else
     snprintf(hg_path, sizeof(hg_path), "/file/tip/?style=json");
 
-  char status[4] = "200";
-  char content_type[256] = "";
-  size_t body_len = 0;
-  char *hg_response = hg_proxy_request("GET", hg_path, NULL, 0, status, content_type, &body_len, arena);
+  Seobeo_Client_Response  *hg_response = hg_proxy_request("GET", hg_path, NULL, NULL);
 
-  Seobeo_Log(SEOBEO_DEBUG, "ApiListDirectory: status=%s body_len=%zu\n", status, body_len);
+  Seobeo_Log(SEOBEO_DEBUG, "ApiListDirectory: status=%i body_len=%zu\n", hg_response->status_code, hg_response->body_length);
 
-  if (!hg_response || status[0] != '2')
+  if (hg_response->status_code != 200)
   {
     Seobeo_Log(SEOBEO_DEBUG, "Failed to get directory from hg serve\n");
     Dowa_HashMap_Push_Arena(resp, "status", "502", arena);
@@ -256,12 +113,15 @@
     Dowa_HashMap_Push_Arena(resp, "body", "{\"error\":\"Failed to connect to hg serve\"}", arena);
     return resp;
   }
-  char *json = hg_response;
+
+  char *temp1 = Dowa_Arena_Copy(arena, hg_response->body, hg_response->body_length);
+  char *temp2 = Dowa_Arena_Allocate(arena, 256);
+  snprintf(temp2, 256, "%zu", hg_response->body_length);
 
   Dowa_HashMap_Push_Arena(resp, "status", "200", arena);
   Dowa_HashMap_Push_Arena(resp, "content-type", "application/json", arena);
-  Dowa_HashMap_Push_Arena(resp, "body", json, arena);
-
+  Dowa_HashMap_Push_Arena(resp, "body", temp1, arena);
+  Dowa_HashMap_Push_Arena(resp, "content-length", temp2, arena);
   return resp;
 }
 
@@ -272,7 +132,7 @@
   void *path_kv = Dowa_HashMap_Get_Ptr(req, "query_path");
   const char *rel_path = path_kv ? ((Seobeo_Request_Entry*)path_kv)->value : "";
   char *decoded_path = Dowa_Arena_Allocate(arena, strlen(rel_path) + 1);
-  url_decode(decoded_path, rel_path);
+  Seobeo_Url_Decode(decoded_path, rel_path);
   char *safe_path = sanitize_path(decoded_path, arena);
 
   Seobeo_Log(SEOBEO_INFO, "ApiGetFile: safe_path='%s'\n", safe_path);
@@ -285,18 +145,16 @@
     return resp;
   }
 
-  // Build hg serve URL: /raw-file/tip/<path>
   char hg_path[MAX_PATH];
   snprintf(hg_path, sizeof(hg_path), "/raw-file/tip/%s", safe_path);
+  Seobeo_Client_Response  *hg_response = hg_proxy_request("GET", hg_path, NULL, NULL);
 
-  char status[4] = "200";
-  char content_type[256] = "";
-  size_t body_len = 0;
-  char *body = hg_proxy_request("GET", hg_path, NULL, 0, status, content_type, &body_len, arena);
+  Seobeo_Log(SEOBEO_DEBUG, "ApiGetFile: status=%i body_len=%zu\n", hg_response->status_code, hg_response->body_length);
 
-  Seobeo_Log(SEOBEO_DEBUG, "ApiGetFile: status=%s body_len=%zu\n", status, body_len);
+  char status[4];
+  snprintf(status, 3, "%i", hg_response->status_code);
 
-  if (!body)
+  if (!hg_response->body)
   {
     Dowa_HashMap_Push_Arena(resp, "status", "502", arena);
     Dowa_HashMap_Push_Arena(resp, "content-type", "text/plain", arena);
@@ -304,31 +162,24 @@
     return resp;
   }
 
-  if (status[0] != '2')
+  if (hg_response->status_code != 200)
   {
-    Seobeo_Log(SEOBEO_DEBUG, "ApiGetFile: error response: %s\n", body);
+    Seobeo_Log(SEOBEO_DEBUG, "ApiGetFile: error hg_response: %s\n", hg_response->body);
     Dowa_HashMap_Push_Arena(resp, "status", status, arena);
     Dowa_HashMap_Push_Arena(resp, "content-type", "text/plain", arena);
-    // Return actual error from hg serve if available
-    Dowa_HashMap_Push_Arena(resp, "body", body_len > 0 ? body : "File not found", arena);
+    Dowa_HashMap_Push_Arena(resp, "body", hg_response->body, arena);
     return resp;
   }
 
-  // Use content-type from hg serve, or determine from extension
-  const char *final_content_type = content_type;
-  if (strlen(content_type) == 0 || strcmp(content_type, "application/octet-stream") == 0)
-  {
-    final_content_type = "text/plain";
-    if (strstr(safe_path, ".md")) final_content_type = "text/markdown";
-    else if (strstr(safe_path, ".html")) final_content_type = "text/html";
-    else if (strstr(safe_path, ".css")) final_content_type = "text/css";
-    else if (strstr(safe_path, ".js")) final_content_type = "application/javascript";
-    else if (strstr(safe_path, ".json")) final_content_type = "application/json";
-  }
+
+  char *temp1 = Dowa_Arena_Copy(arena, hg_response->body, hg_response->body_length);
+  char *temp2 = Dowa_Arena_Allocate(arena, 256);
+  snprintf(temp2, 256, "%zu", hg_response->body_length);
 
-  Dowa_HashMap_Push_Arena(resp, "status", "200", arena);
-  Dowa_HashMap_Push_Arena(resp, "content-type", final_content_type, arena);
-  Dowa_HashMap_Push_Arena(resp, "body", body, arena);
+  Dowa_HashMap_Push_Arena(resp, "status", status, arena);
+  Dowa_HashMap_Push_Arena(resp, "content-type", "text/plain", arena);
+  Dowa_HashMap_Push_Arena(resp, "body", temp1, arena);
+  Dowa_HashMap_Push_Arena(resp, "content-length", temp2, arena);
 
   return resp;
 }
@@ -337,139 +188,159 @@
   return ApiGetFile(req, arena);
 }
 
+// Streaming handler for hg wire protocol - pipes data directly without buffering
+void StreamHgWireProtocol(Seobeo_Handle *p_client, Seobeo_Request_Entry *req, Dowa_Arena *arena)
+{
+  void *method_kv = Dowa_HashMap_Get_Ptr(req, "HTTP_Method");
+  const char *method = method_kv ? ((Seobeo_Request_Entry*)method_kv)->value : "GET";
+
+  void *query_kv = Dowa_HashMap_Get_Ptr(req, "QueryString");
+  const char *query_string = query_kv ? ((Seobeo_Request_Entry*)query_kv)->value : "";
+
+  void *body_kv = Dowa_HashMap_Get_Ptr(req, "Body");
+  const char *req_body = body_kv ? ((Seobeo_Request_Entry*)body_kv)->value : "";
+
+  const char *hg_custom = req[7].value;
+
+  Seobeo_Log(SEOBEO_DEBUG, "HG Stream Proxy: method=%s query=%s\n", method, query_string);
+
+  // THINKING: Connect to hg serve
+  // This kinda blows, but not a good way to handle it since my client API assumes it is all stored in
+  // buffer and what not.
+  Seobeo_Handle *p_upstream = Seobeo_Stream_Handle_Client_Create(HG_SERVE_HOST, HG_SERVE_PORT, FALSE);
+  if (!p_upstream || p_upstream->socket < 0)
+  {
+    const char *err_resp = "HTTP/1.1 502 Bad Gateway\r\nContent-Length: 26\r\n\r\nFailed to connect upstream";
+    Seobeo_Handle_Queue(p_client, (uint8*)err_resp, strlen(err_resp));
+    Seobeo_Handle_Flush(p_client);
+    if (p_upstream)
+      Seobeo_Handle_Destroy(p_upstream);
+    return;
+  }
+
+  // Create headers
+  // we only allow x-hgarg-1 and content-length
+  char request_buf[8192];
+  int req_len = snprintf(request_buf, sizeof(request_buf),
+    "%s /?%s HTTP/1.1\r\n"
+    "Host: %s:%s\r\n"
+    "User-Agent: Seobeo/1.0\r\n"
+    "Connection: close\r\n",
+    method, query_string, HG_SERVE_HOST, HG_SERVE_PORT);
+
+  if (hg_custom && hg_custom[0] != '\0')
+    req_len += snprintf(request_buf + req_len, sizeof(request_buf) - req_len, "x-hgarg-1: %s\r\n", hg_custom);
+
+  if (req_body && req_body[0] != '\0')
+    req_len += snprintf(request_buf + req_len, sizeof(request_buf) - req_len, "Content-Length: %zu\r\n\r\n%s", strlen(req_body), req_body);
+  else
+    req_len += snprintf(request_buf + req_len, sizeof(request_buf) - req_len, "\r\n");
+
+  Seobeo_Handle_Queue(p_upstream, (uint8*)request_buf, req_len);
+  if (Seobeo_Handle_Flush(p_upstream) < 0)
+  {
+    const char *err_resp = "HTTP/1.1 502 Bad Gateway\r\nContent-Length: 21\r\n\r\nUpstream write failed";
+    Seobeo_Handle_Queue(p_client, (uint8*)err_resp, strlen(err_resp));
+    Seobeo_Handle_Flush(p_client);
+    Seobeo_Handle_Destroy(p_upstream);
+    return;
+  }
+
+  // Responses 
+  while (1)
+  {
+    int r = Seobeo_Handle_Read(p_upstream);
+    if (r < 0)
+    {
+      Seobeo_Handle_Destroy(p_upstream);
+      return;
+    }
+    if (p_upstream->read_buffer_len >= 4 &&
+        strstr((char*)p_upstream->read_buffer, "\r\n\r\n") != NULL)
+      break;
+    if (r == 0)
+      continue;
+  }
+
+  // TODO: Maybe make this into a separate function instead of internal function as doing this over and over again blows.
+  char *hdr_end = strstr((char*)p_upstream->read_buffer, "\r\n\r\n");
+  if (!hdr_end)
+  {
+    Seobeo_Handle_Destroy(p_upstream);
+    return;
+  }
+  size_t hdr_len = hdr_end - (char*)p_upstream->read_buffer + 4;
+  Seobeo_Handle_Queue(p_client, p_upstream->read_buffer, hdr_len);
+  Seobeo_Handle_Flush(p_client);
+
+  // All body 
+  size_t body_in_buffer = p_upstream->read_buffer_len - hdr_len;
+  if (body_in_buffer > 0)
+  {
+    Seobeo_Handle_Queue(p_client, p_upstream->read_buffer + hdr_len, body_in_buffer);
+    Seobeo_Handle_Flush(p_client);
+  }
+  Seobeo_Handle_Consume(p_upstream, p_upstream->read_buffer_len);
+  while (1)
+  {
+    int n = Seobeo_Handle_Read(p_upstream);
+    if (n > 0)
+    {
+      Seobeo_Handle_Queue(p_client, p_upstream->read_buffer, p_upstream->read_buffer_len);
+      Seobeo_Handle_Flush(p_client);
+      Seobeo_Handle_Consume(p_upstream, p_upstream->read_buffer_len);
+    }
+    else if (n == -2)
+      break;
+    else if (n < 0)
+      break;
+  }
+
+  Seobeo_Handle_Destroy(p_upstream);
+}
+
 Seobeo_Request_Entry* ApiHgWireProtocol(Seobeo_Request_Entry *req, Dowa_Arena *arena)
 {
-    Seobeo_Request_Entry *resp = NULL;
-
-    // Get method
-    void *method_kv = Dowa_HashMap_Get_Ptr(req, "HTTP_Method");
-    const char *method = method_kv ? ((Seobeo_Request_Entry*)method_kv)->value : "GET";
-
-    // Get query string
-    void *query_kv = Dowa_HashMap_Get_Ptr(req, "QueryString");
-    const char *query_string = query_kv ? ((Seobeo_Request_Entry*)query_kv)->value : "";
+  Seobeo_Request_Entry *resp = NULL;
 
-    // Get request body for POST
-    void *body_kv = Dowa_HashMap_Get_Ptr(req, "Body");
-    const char *req_body = body_kv ? ((Seobeo_Request_Entry*)body_kv)->value : "";
-    size_t body_len = strlen(req_body);
-
-    Seobeo_Log(SEOBEO_DEBUG, "HG Proxy: method=%s query=%s body_len=%zu\n", method, query_string, body_len);
+  void *method_kv = Dowa_HashMap_Get_Ptr(req, "HTTP_Method");
+  const char *method = method_kv ? ((Seobeo_Request_Entry*)method_kv)->value : "GET";
 
-    // Connect to hg serve
-    int sock = hg_proxy_connect();
-    if (sock < 0)
-    {
-        Dowa_HashMap_Push_Arena(resp, "status", "502", arena);
-        Dowa_HashMap_Push_Arena(resp, "content-type", "text/plain", arena);
-        Dowa_HashMap_Push_Arena(resp, "body", "Failed to connect to hg serve", arena);
-        return resp;
-    }
+  void *query_kv = Dowa_HashMap_Get_Ptr(req, "QueryString");
+  const char *query_string = query_kv ? ((Seobeo_Request_Entry*)query_kv)->value : "";
 
-    // Build the HTTP request to forward to hg serve
-    char http_request[MAX_PATH * 2];
-    if (strlen(query_string) > 0)
-    {
-        snprintf(http_request, sizeof(http_request),
-            "%s /?%s HTTP/1.1\r\n"
-            "Host: %s:%d\r\n"
-            "Connection: close\r\n"
-            "Content-Length: %zu\r\n"
-            "\r\n",
-            method, query_string, HG_SERVE_HOST, HG_SERVE_PORT, body_len);
-    }
-    else
-    {
-        snprintf(http_request, sizeof(http_request),
-            "%s / HTTP/1.1\r\n"
-            "Host: %s:%d\r\n"
-            "Connection: close\r\n"
-            "Content-Length: %zu\r\n"
-            "\r\n",
-            method, HG_SERVE_HOST, HG_SERVE_PORT, body_len);
-    }
+  void *body_kv = Dowa_HashMap_Get_Ptr(req, "Body");
+  const char *req_body = body_kv ? ((Seobeo_Request_Entry*)body_kv)->value : "";
+  size_t body_len = strlen(req_body);
 
-    // Send HTTP request headers
-    if (send(sock, http_request, strlen(http_request), 0) < 0)
-    {
-        Seobeo_Log(SEOBEO_DEBUG, "Failed to send request to hg serve\n");
-        close(sock);
-        Dowa_HashMap_Push_Arena(resp, "status", "502", arena);
-        Dowa_HashMap_Push_Arena(resp, "content-type", "text/plain", arena);
-        Dowa_HashMap_Push_Arena(resp, "body", "Failed to send to hg serve", arena);
-        return resp;
-    }
+  const char *hg_custom = req[7].value;
+  Seobeo_Log(SEOBEO_DEBUG, "HG Proxy: method=%s query=%s body_len=%zu\n", method, query_string, body_len);
+
+  Seobeo_Client_Response *hg_response;
+
+  char hg_path[MAX_PATH];
+  snprintf(hg_path, sizeof(hg_path), "/?%s", query_string);
 
-    // Send body if present
-    if (body_len > 0)
-    {
-        send(sock, req_body, body_len, 0);
-    }
+  hg_response = hg_proxy_request(method, hg_path, req_body, hg_custom);
 
-    // Read response from hg serve
-    int buffer_size = 1024 * 1024 * 5; // 5MB
-    char *response_buf = Dowa_Arena_Allocate(arena, buffer_size);
-    size_t total_read = 0;
-    ssize_t bytes_read;
+  Seobeo_Log(SEOBEO_DEBUG, "HG Proxy: received %zu bytes\n", hg_response->body_length);
 
-    while ((bytes_read = recv(sock, response_buf + total_read, buffer_size - total_read - 1, 0)) > 0)
-    {
-        total_read += bytes_read;
-        if (total_read >= (size_t)(buffer_size - 1)) break;
-    }
-    response_buf[total_read] = '\0';
-    close(sock);
-
-    Seobeo_Log(SEOBEO_DEBUG, "HG Proxy: received %zu bytes\n", total_read);
+  Seobeo_Request_Entry *kv = Dowa_HashMap_Get_Ptr(hg_response->headers, "Content-Type");
 
-    // Parse HTTP response - find headers end
-    char *headers_end = strstr(response_buf, "\r\n\r\n");
-    if (!headers_end)
-    {
-        Dowa_HashMap_Push_Arena(resp, "status", "502", arena);
-        Dowa_HashMap_Push_Arena(resp, "content-type", "text/plain", arena);
-        Dowa_HashMap_Push_Arena(resp, "body", "Invalid response from hg serve", arena);
-        return resp;
-    }
+  char *status = Dowa_Arena_Allocate(arena, 5);
+  snprintf(status, 4, "%i", hg_response->status_code);
 
-    // Extract status code from first line (e.g., "HTTP/1.1 200 OK")
-    char status_code[4] = "200";
-    if (strncmp(response_buf, "HTTP/", 5) == 0)
-    {
-        char *status_start = strchr(response_buf, ' ');
-        if (status_start)
-        {
-            strncpy(status_code, status_start + 1, 3);
-            status_code[3] = '\0';
-        }
-    }
+  // Use binary-safe copy to handle null bytes in mercurial bundle data
+  char *temp1 = Dowa_Arena_Copy(arena, hg_response->body, hg_response->body_length);
+  char *temp2 = Dowa_Arena_Allocate(arena, 256);
+  snprintf(temp2, 256, "%zu", hg_response->body_length);
 
-    // Extract content-type from headers
-    const char *content_type = "application/mercurial-0.1";
-    char *ct_header = strcasestr(response_buf, "Content-Type:");
-    if (ct_header && ct_header < headers_end)
-    {
-        ct_header += 13; // Skip "Content-Type:"
-        while (*ct_header == ' ') ct_header++;
-        char *ct_end = strpbrk(ct_header, "\r\n");
-        if (ct_end)
-        {
-            size_t ct_len = ct_end - ct_header;
-            char *ct_copy = Dowa_Arena_Allocate(arena, ct_len + 1);
-            strncpy(ct_copy, ct_header, ct_len);
-            ct_copy[ct_len] = '\0';
-            content_type = ct_copy;
-        }
-    }
+  Dowa_HashMap_Push_Arena(resp, "status", status, arena);
+  Dowa_HashMap_Push_Arena(resp, "content-type", kv->value, arena);
+  Dowa_HashMap_Push_Arena(resp, "body", temp1, arena);
+  Dowa_HashMap_Push_Arena(resp, "content-length", temp2, arena);
 
-    // Body starts after \r\n\r\n
-    char *body = headers_end + 4;
-
-    Dowa_HashMap_Push_Arena(resp, "status", status_code, arena);
-    Dowa_HashMap_Push_Arena(resp, "content-type", content_type, arena);
-    Dowa_HashMap_Push_Arena(resp, "body", body, arena);
-
-    return resp;
+  return resp;
 }
 
 int main(void) {
@@ -479,13 +350,13 @@
   Seobeo_Router_Register("GET", "/api/repo/file", ApiGetFile);
   Seobeo_Router_Register("GET", "/api/repo/readme", ApiGetReadme);
 
-  Seobeo_Router_Register("GET", "/repo", ApiHgWireProtocol);
-  Seobeo_Router_Register("POST", "/repo", ApiHgWireProtocol);
+  // Use streaming handler for hg wire protocol... 
+  Seobeo_Router_Register_Stream("GET", "/repo", StreamHgWireProtocol);
+  Seobeo_Router_Register_Stream("POST", "/repo", StreamHgWireProtocol);
 
   printf("Starting on Port 6970...\n");
-  printf("Repository: %s\n", REPO_ROOT);
 
-  int result = Seobeo_Web_Server_Start("hg-web/src", "6970", SEOBEO_MODE_EDGE, 4);
+  int result = Seobeo_Web_Server_Start("hg-web/src", "6970", SEOBEO_MODE_EDGE, 1);
 
   Seobeo_Router_Destroy();
 
--- a/hg-web/src/index.html	Fri Jan 23 22:22:30 2026 -0800
+++ b/hg-web/src/index.html	Fri Jan 23 22:38:59 2026 -0800
@@ -4,19 +4,20 @@
     <meta charset="UTF-8">
     <meta name="viewport" content="width=device-width, initial-scale=1.0">
     <title>Zenbu Repository</title>
-    <link rel="stylesheet" href="/base.css">
-    <link rel="stylesheet" href="/index.css">
+    <link rel="icon" type="image/svg+xml" href="/public/epi_all_colors.svg">
 </head>
 <body>
     <main>
-        <div class="header">
+      <div id="root"></div>
+
+<!--        <div class="header">
             <h1>Zenbu Repository</h1>
             <p class="description">Browse and clone this mercurial repository</p>
         </div>
 
         <div class="clone-info">
             <strong>Clone this repository:</strong><br>
-            <code>hg clone http://zenbu.babocoder.com</code>
+            <code>hg clone http://zenbu.babocoder.com/repo</code>
         </div>
 
         <div class="breadcrumb" id="breadcrumb"></div>
@@ -30,10 +31,10 @@
 
         <div class="empty-state" id="emptyState" style="display: none;">
             <p>No files found in this directory</p>
-        </div>
+        </div> -->
     </main>
 
     <script src="/markdown_to_html.js"></script>
-    <script src="/index.js"></script>
+    <script src="/page.js"></script>
 </body>
 </html>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/hg-web/src/main.tsx	Fri Jan 23 22:38:59 2026 -0800
@@ -0,0 +1,8 @@
+import React from 'react';
+import ReactDOM from 'react-dom/client';
+import { RepoBrowser } from "./repo-browser";
+
+const root = ReactDOM.createRoot(document.getElementById('root'));
+
+// Use JSX syntax (<RepoBrowser />) 
+root.render(<RepoBrowser />);
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/hg-web/src/repo-browser.tsx	Fri Jan 23 22:38:59 2026 -0800
@@ -0,0 +1,406 @@
+import React, { useState, useEffect } from 'react';
+import { renderMarkdown } from './src/markdown_to_html.js';
+
+// --- ICONS (Using CDN Links) ---
+const ICONS = {
+  folder: "https://cdn-icons-png.flaticon.com/512/11471/11471391.png",
+  file: "https://raw.githubusercontent.com/PKief/vscode-material-icon-theme/main/icons/document.svg",
+  home: "https://cdn-icons-png.flaticon.com/512/1946/1946488.png",
+  repo: "/public/epi_all_colors.svg",
+  clone: "https://cdn-icons-png.flaticon.com/512/11471/11471391.png"
+};
+
+const API_BASE = '/api/repo';
+
+/**
+ * Component: Styles
+ * Injected CSS for the polished look
+ */
+const GlobalStyles = () => (
+  <style>{`
+    :root {
+      --bg-color: #ffffff;
+      --bg-subtle: #f6f8fa;
+      --border-color: #d0d7de;
+      --accent-color: #0969da;
+      --text-primary: #1f2328;
+      --text-secondary: #656d76;
+      --hover-color: #f3f4f6;
+      --radius: 6px;
+    }
+
+    .repo-container {
+      font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
+      max-width: 980px;
+      margin: 40px auto;
+      color: var(--text-primary);
+      padding: 0 20px;
+    }
+
+    /* Header */
+    .header {
+      display: flex;
+      align-items: center;
+      margin-bottom: 20px;
+      gap: 15px;
+    }
+    .header-icon { width: 32px; height: 32px; opacity: 0.8; }
+    .header h1 { margin: 0; font-size: 24px; font-weight: 600; }
+    .description { color: var(--text-secondary); margin: 0; font-size: 14px; }
+
+    /* Clone Box */
+    .clone-box {
+      background: var(--bg-subtle);
+      border: 1px solid var(--border-color);
+      border-radius: var(--radius);
+      padding: 12px 16px;
+      margin-bottom: 24px;
+      display: flex;
+      justify-content: space-between;
+      align-items: center;
+    }
+    .clone-label { font-weight: 600; font-size: 13px; margin-right: 10px; color: var(--text-primary); }
+    .clone-url { 
+      font-family: 'SFMono-Regular', Consolas, 'Liberation Mono', Menlo, monospace;
+      background: white;
+      border: 1px solid var(--border-color);
+      padding: 4px 8px;
+      border-radius: 4px;
+      font-size: 12px;
+      color: var(--text-secondary);
+      flex-grow: 1;
+    }
+
+    /* Breadcrumb */
+    #breadcrumb {
+      display: flex;
+      align-items: center;
+      font-size: 14px;
+      margin-bottom: 16px;
+      color: var(--text-secondary);
+      padding: 8px 0;
+    }
+    #breadcrumb a { 
+      color: var(--accent-color); 
+      text-decoration: none; 
+      border-radius: 4px;
+      padding: 2px 6px;
+    }
+    #breadcrumb a:hover { background: var(--bg-subtle); text-decoration: underline; }
+    #breadcrumb .separator { margin: 0 4px; color: var(--text-secondary); opacity: 0.5; }
+    #breadcrumb .nav-item.active { font-weight: 600; color: var(--text-primary); padding: 2px 6px;}
+
+    /* File List Table Structure */
+    .file-list-container {
+      border: 1px solid var(--border-color);
+      border-radius: var(--radius);
+      overflow: hidden;
+    }
+    .file-header {
+      background: var(--bg-subtle);
+      border-bottom: 1px solid var(--border-color);
+      padding: 12px 16px;
+      font-size: 13px;
+      font-weight: 600;
+      color: var(--text-secondary);
+    }
+    .empty-state { padding: 40px; text-align: center; color: var(--text-secondary); }
+    .error-message { 
+      padding: 15px; border: 1px solid #ffdce0; 
+      background: #ffebe9; color: #cf222e; 
+      border-radius: var(--radius); margin-bottom: 20px; 
+    }
+
+    /* File Row */
+    .file-row {
+      display: flex;
+      align-items: center;
+      padding: 10px 16px;
+      border-bottom: 1px solid var(--border-color);
+      transition: background 0.1s;
+    }
+    .file-row:last-child { border-bottom: none; }
+    .file-row:hover { background: var(--hover-color); }
+    
+    .file-row .icon img { width: 20px; height: 20px; vertical-align: middle; margin-right: 12px; }
+    .file-row .name a { 
+      color: var(--text-primary); 
+      text-decoration: none; 
+      font-size: 14px; 
+    }
+    .file-row .name a:hover { color: var(--accent-color); text-decoration: underline; }
+
+    /* Readme */
+    #readmeSection { margin-top: 32px; border: 1px solid var(--border-color); border-radius: var(--radius); }
+    .readme-header { 
+      background: var(--bg-subtle); 
+      padding: 10px 16px; 
+      font-size: 12px; font-weight: 600; 
+      border-bottom: 1px solid var(--border-color);
+      display: flex; align-items: center; gap: 8px;
+    }
+    #readmeContent { padding: 32px; background: white; overflow-x: auto; }
+  `}</style>
+);
+
+/**
+ * Component: Breadcrumb
+ */
+function Breadcrumb({ currentPath, onNavigate }) {
+  if (!currentPath) {
+    return (
+      <nav id="breadcrumb">
+        <span className="nav-item active">root</span>
+      </nav>
+    );
+  }
+
+  const parts = currentPath.split('/').filter(p => p);
+  const crumbs = parts.map((part, index) => ({
+    name: part,
+    fullPath: parts.slice(0, index + 1).join('/')
+  }));
+
+  return (
+    <nav id="breadcrumb">
+      <a 
+        href="/" 
+        onClick={(e) => { e.preventDefault(); onNavigate(''); }}
+        title="Go to Root"
+      >
+        root
+      </a>
+      {crumbs.map((crumb, index) => {
+        const isLast = index === crumbs.length - 1;
+        return (
+          <React.Fragment key={crumb.fullPath}>
+            <span className="separator">/</span>
+            {isLast ? (
+              <span className="nav-item active">{crumb.name}</span>
+            ) : (
+              <a 
+                href={`?path=${encodeURIComponent(crumb.fullPath)}`}
+                onClick={(e) => { e.preventDefault(); onNavigate(crumb.fullPath); }}
+              >
+                {crumb.name}
+              </a>
+            )}
+          </React.Fragment>
+        );
+      })}
+    </nav>
+  );
+}
+
+/**
+ * Component: FileList
+ */
+function FileList({ directories, files, onNavigate }) {
+  const isEmpty = directories.length === 0 && files.length === 0;
+
+  if (isEmpty) {
+    return (
+      <div className="file-list-container">
+        <div className="empty-state">This directory is empty.</div>
+      </div>
+    );
+  }
+
+  return (
+    <div className="file-list-container">
+       {/* Optional header row like GitHub */}
+      <div className="file-header">
+        Files
+      </div>
+
+      <div id="fileListBody">
+        {directories.map((dir) => (
+          <FileRow 
+            key={dir.abspath}
+            item={dir}
+            iconUrl={ICONS.folder}
+            isDir={true}
+            onNavigate={onNavigate}
+          />
+        ))}
+
+        {files.map((file) => (
+          <FileRow 
+            key={file.abspath}
+            item={file}
+            iconUrl={ICONS.file}
+            isDir={false}
+          />
+        ))}
+      </div>
+    </div>
+  );
+}
+
+/**
+ * Component: FileRow
+ */
+function FileRow({ item, iconUrl, isDir, onNavigate }) {
+  const handleClick = (e) => {
+    if (isDir) {
+      e.preventDefault();
+      onNavigate(item.abspath);
+    }
+  };
+
+  const href = isDir 
+    ? `?path=${encodeURIComponent(item.abspath)}`
+    : `/api/repo/file?path=${encodeURIComponent(item.abspath)}`;
+  
+  const target = isDir ? undefined : "_blank";
+
+  return (
+    <div className="file-row">
+      <span className="icon">
+        <img src={iconUrl} alt={isDir ? "Directory" : "File"} />
+      </span>
+      <span className="name">
+        <a href={href} onClick={handleClick} target={target} rel="noreferrer">
+          {item.basename}
+        </a>
+      </span>
+    </div>
+  );
+}
+
+/**
+ * Component: ReadmeViewer
+ */
+function ReadmeViewer({ content }) {
+  if (!content) return null;
+
+  useEffect(() => renderMarkdown(content, readmeContent), [content]);
+
+  return (
+    <div id="readmeSection">
+      <div className="readme-header">
+        <img src="https://img.icons8.com/material-outlined/24/000000/menu--v1.png" width="16" alt="" style={{opacity:0.5}} />
+        README.md
+      </div>
+      <div id="readmeContent"></div>
+    </div>
+  );
+}
+
+/**
+ * Main Application Component
+ */
+function RepoBrowser() {
+  const [currentPath, setCurrentPath] = useState(getCurrentPath());
+  const [content, setContent] = useState({ files: [], directories: [] });
+  const [readme, setReadme] = useState(null);
+  const [error, setError] = useState(null);
+  const [loading, setLoading] = useState(false);
+
+  function getCurrentPath() {
+    const params = new URLSearchParams(window.location.search);
+    return params.get('path') || '';
+  }
+
+  useEffect(() => {
+    const handlePopState = () => setCurrentPath(getCurrentPath());
+    window.addEventListener('popstate', handlePopState);
+    return () => window.removeEventListener('popstate', handlePopState);
+  }, []);
+
+  useEffect(() => {
+    fetchDirectory(currentPath);
+    fetchReadme(currentPath);
+  }, [currentPath]);
+
+  const navigate = (path) => {
+    const newUrl = path ? `?path=${encodeURIComponent(path)}` : '/';
+    window.history.pushState({ path }, '', newUrl);
+    setCurrentPath(path);
+  };
+
+  const fetchDirectory = async (path) => {
+    setLoading(true);
+    setError(null);
+    try {
+      const url = path 
+        ? `${API_BASE}/list?path=${encodeURIComponent(path)}` 
+        : `${API_BASE}/list`;
+      
+      const response = await fetch(url);
+      const data = await response.json();
+
+      if (data.error) throw new Error(data.error);
+      
+      setContent({
+        files: data.files || [],
+        directories: data.directories || []
+      });
+    } catch (err) {
+      console.error('Error loading directory:', err);
+      setError(err.message);
+    } finally {
+      setLoading(false);
+    }
+  };
+
+  const fetchReadme = async (path) => {
+    setReadme(null);
+    try {
+      const readmePath = path ? `${path}/README.md` : 'README.md';
+      const response = await fetch(`${API_BASE}/readme?path=${encodeURIComponent(readmePath)}`);
+      
+      if (response.ok) {
+        const text = await response.text();
+        setReadme(text);
+      }
+    } catch (err) { /* Silently fail */ }
+  };
+
+  return (
+    <>
+      <GlobalStyles />
+      <div className="repo-container">
+        
+        {/* Header */}
+        <div className="header">
+          <img src={ICONS.repo} alt="Repo" className="header-icon" />
+          <div>
+            <h1>Zenbu Repository</h1>
+            <p className="description">Browse and manage the mercurial codebase</p>
+          </div>
+        </div>
+
+        {/* Clone Bar */}
+        <div className="clone-box">
+          <div style={{display:'flex', alignItems:'center', width:'100%'}}>
+             <span className="clone-label">Clone HTTPS</span>
+             <code className="clone-url">hg clone http://zenbu.babocoder.com/repo</code>
+          </div>
+        </div>
+
+        {/* Navigation & Content */}
+        <Breadcrumb currentPath={currentPath} onNavigate={navigate} />
+        
+        {error && <div className="error-message">Error: {error}</div>}
+        
+        {loading ? (
+          <div className="file-list-container" style={{padding: '40px', textAlign: 'center', color:'#666'}}>
+             Loading files...
+          </div>
+        ) : (
+          <>
+            <FileList 
+              directories={content.directories} 
+              files={content.files} 
+              onNavigate={navigate} 
+            />
+            <ReadmeViewer content={readme} />
+          </>
+        )}
+      </div>
+    </>
+  );
+}
+
+export { RepoBrowser };
--- a/mrjunejune/BUILD	Fri Jan 23 22:22:30 2026 -0800
+++ b/mrjunejune/BUILD	Fri Jan 23 22:38:59 2026 -0800
@@ -27,6 +27,12 @@
   ],
   dest = "src/public/highlight",
 )
+
+filegroup(
+  name = "public_files",
+  srcs = glob(["src/public/**"]),
+  visibility = ["//visibility:public"],
+)
     
 filegroup(
   name = "src_files",
Binary file mrjunejune/src/public/epi-photos/.DS_Store has changed
Binary file mrjunejune/src/public/epi-photos/webp/.DS_Store has changed
--- a/seobeo/BUILD	Fri Jan 23 22:22:30 2026 -0800
+++ b/seobeo/BUILD	Fri Jan 23 22:38:59 2026 -0800
@@ -308,7 +308,7 @@
     "//dowa:dowa",
     "@openssl//:ssl",
   ],
-  defines = ["SEOBEO_WEBSOCKET_SERVER"],
+  defines = ["SEOBEO_WEBSOCKET_SERVER", "SEOBEO_ENABLE_DEBUG"],
   target_compatible_with = [
     "@platforms//os:osx",
   ],
@@ -334,7 +334,7 @@
     "//dowa:dowa",
     "@openssl//:ssl",
   ],
-  defines = ["SEOBEO_WEBSOCKET_SERVER"],
+  defines = ["SEOBEO_WEBSOCKET_SERVER", "SEOBEO_ENABLE_DEBUG"],
   target_compatible_with = [
     "@platforms//os:linux",
   ],
--- a/seobeo/s_http_client.c	Fri Jan 23 22:22:30 2026 -0800
+++ b/seobeo/s_http_client.c	Fri Jan 23 22:38:59 2026 -0800
@@ -69,7 +69,7 @@
 
   memset(p_req, 0, sizeof(Seobeo_Client_Request));
 
-  p_req->p_arena = Dowa_Arena_Create(1024 * 1024);
+  p_req->p_arena = Dowa_Arena_Create(1024 * 1024 * 5);
   if (!p_req->p_arena)
   {
     free(p_req);
@@ -231,7 +231,7 @@
 
   memset(p_resp, 0, sizeof(Seobeo_Client_Response));
 
-  p_resp->p_arena = Dowa_Arena_Create(1024 * 1024 * 5); // 5 MB 
+  p_resp->p_arena = Dowa_Arena_Create(1024 * 1024 * 10); // 10 MB 
   if (!p_resp->p_arena)
   {
     free(p_resp);
@@ -391,7 +391,7 @@
   }
   else
   {
-    size_t cap = 1024 * 1024 * 3;
+    size_t cap = 1024 * 1024 * 5;
     size_t used = 0;
     char *body = download_path ? NULL : Dowa_Arena_Allocate(p_resp->p_arena, cap);
 
@@ -414,7 +414,6 @@
           }
           memcpy(body + used, p_handle->read_buffer, p_handle->read_buffer_len);
           used += p_handle->read_buffer_len;
-          Seobeo_Log(SEOBEO_DEBUG, "Copied %zu bytes, total %zu/%zu\n", used, used + p_handle->read_buffer_len, body_len);
         }
         Seobeo_Handle_Consume(p_handle, (uint32)p_handle->read_buffer_len);
       }
--- a/seobeo/s_web.c	Fri Jan 23 22:22:30 2026 -0800
+++ b/seobeo/s_web.c	Fri Jan 23 22:38:59 2026 -0800
@@ -151,6 +151,11 @@
   void *p_conn_kv = Dowa_HashMap_Get_Ptr(p_req_map, "Connection");
   const char *conn_header = p_conn_kv ? ((Seobeo_Request_Entry*)p_conn_kv)->value : NULL;
 
+  void *p_real_ip_kv = Dowa_HashMap_Get_Ptr(p_req_map, "X-Real-IP");
+  const char *real_ip = p_real_ip_kv ? ((Seobeo_Request_Entry*)p_real_ip_kv)->value : NULL;
+  if (!real_ip)
+    real_ip = p_cli_handle->host;
+
   if (conn_header)
   {
     if (connection_header_contains(conn_header, "close"))
@@ -185,6 +190,7 @@
 
   // --- Check for WebSocket upgrade request ---
   #ifdef SEOBEO_WEBSOCKET_SERVER
+  Seobeo_Log(SEOBEO_DEBUG, "Web socket path \n");
   if (Seobeo_WebSocket_Server_Handle_Upgrade(p_cli_handle, p_req_map, path))
   {
     Seobeo_Log(SEOBEO_INFO, "WebSocket connection established\n");
@@ -194,7 +200,15 @@
   }
   #endif
 
-  // --- Try to match API route first ---
+  // --- Try to match streaming route first ---
+  Seobeo_Stream_Handler stream_handler = Seobeo_Router_Find_Stream_Handler(method, path, &p_req_map, p_request_arena);
+  if (stream_handler != NULL)
+  {
+    stream_handler(p_cli_handle, p_req_map, p_response_arena);
+    goto clean_up;
+  }
+
+  // --- Try to match API route ---
   Seobeo_Route_Handler handler = Seobeo_Router_Find_Handler(method, path, &p_req_map, p_request_arena);
   if (handler != NULL)
   {
@@ -333,7 +347,10 @@
       break;
 
     if (r == 0)
-      return 1;     // EAGAIN, try again later TODO: Add this as part of Handle struct.
+    {
+      Seobeo_Log(SEOBEO_INFO, "Waiting?\n");
+      continue;     // EAGAIN, try again later TODO: Add this as part of Handle struct.
+    }
   }
 
   // "METHOD SP PATH SP VERSION CRLF"
@@ -448,7 +465,6 @@
     char *next = strstr(line, "\r\n");
     if (!next) break;
 
-    // split at colon
     char *colon = memchr(line, ':', next - line);
     if (colon)
     {
@@ -472,7 +488,6 @@
       memcpy(val, val_start, value_len);
       val[value_len] = '\0';
 
-      // Both key and value are arena-allocated, hashmap will use them
       Dowa_HashMap_Push_Arena(*pp_map, key, val, p_arena);
     }
 
@@ -490,7 +505,6 @@
 
     Seobeo_Log(SEOBEO_DEBUG, "Content-Length=%zu, reading body in chunks...\n", body_len);
 
-    // Allocate buffer for entire body
     char *body = Dowa_Arena_Allocate(p_arena, body_len + 1);
     if (!body)
     {
@@ -606,6 +620,7 @@
   char *method; // "GET", "POST", "PUT", "DELETE"
   char *path_pattern; // "/v1/users/:id/posts/:post_id"
   Seobeo_Route_Handler handler;
+  Seobeo_Stream_Handler stream_handler; // For streaming responses
 
   // Pre-parsed path segments for efficient matching
   char **path_segments; // ["v1", "users", ":id", "posts", ":post_id"]
@@ -627,6 +642,25 @@
   route.method = strdup(method);
   route.path_pattern = strdup(path_pattern);
   route.handler = handler;
+  route.stream_handler = NULL;
+  route.path_segments = Dowa_String_Split(path_pattern, "/", strlen(path_pattern), 1, NULL);
+  route.segment_count = Dowa_Array_Length(route.path_segments);
+  route.is_param = (boolean*)malloc(sizeof(boolean) * route.segment_count);
+
+  for (size_t i = 0; i < route.segment_count; i++)
+    route.is_param[i] = (route.path_segments[i][0] == ':');
+
+  Dowa_Array_Push(g_routes, route);
+}
+
+void Seobeo_Router_Register_Stream(const char *method, const char *path_pattern, Seobeo_Stream_Handler handler)
+{
+  Seobeo_Route route = {0};
+
+  route.method = strdup(method);
+  route.path_pattern = strdup(path_pattern);
+  route.handler = NULL;
+  route.stream_handler = handler;
   route.path_segments = Dowa_String_Split(path_pattern, "/", strlen(path_pattern), 1, NULL);
   route.segment_count = Dowa_Array_Length(route.path_segments);
   route.is_param = (boolean*)malloc(sizeof(boolean) * route.segment_count);
@@ -709,6 +743,29 @@
   return NULL;
 }
 
+Seobeo_Stream_Handler Seobeo_Router_Find_Stream_Handler(
+  const char *method,
+  const char *path,
+  Seobeo_Request_Entry **pp_request_map,
+  Dowa_Arena *p_arena)
+{
+  if (g_routes == NULL || method == NULL || path == NULL)
+    return NULL;
+
+  size_t route_count = Dowa_Array_Length(g_routes);
+  for (size_t i = 0; i < route_count; i++)
+  {
+    Seobeo_Route *route = &g_routes[i];
+    if (strcmp(route->method, method) != 0)
+      continue;
+
+    if (route->stream_handler && match_route_and_extract(route, path, pp_request_map, p_arena))
+      return route->stream_handler;
+  }
+
+  return NULL;
+}
+
 void Seobeo_Router_Send_Response(
     Seobeo_Handle *p_handle,
     Seobeo_Request_Entry *p_response_map,
@@ -744,24 +801,23 @@
   const char *body = "";
   void *p_body_kv = Dowa_HashMap_Get_Ptr(p_response_map, "body");
   if (p_body_kv)
-  {
     body = ((Seobeo_Request_Entry*)p_body_kv)->value;
-  }
 
   const char *content_type = "text/html";
   void *p_content_type_kv = Dowa_HashMap_Get_Ptr(p_response_map, "content-type");
   if (p_content_type_kv)
-  {
     content_type = ((Seobeo_Request_Entry*)p_content_type_kv)->value;
-  }
 
-  size_t body_length = strlen(body);
+  // TODO: Update this to be integer
+  size_t body_length;
   void *p_content_length_kv = Dowa_HashMap_Get_Ptr(p_response_map, "content-length");
   if (p_content_length_kv)
   {
     const char *content_length_str = ((Seobeo_Request_Entry*)p_content_length_kv)->value;
     body_length = atoi(content_length_str);
   }
+  else
+    body_length = strlen(body);
 
   char *header = Dowa_Arena_Allocate(p_arena, 4096);
   Seobeo_Web_Header_Generate_KeepAlive(header, status, content_type, body_length, keep_alive);
@@ -782,6 +838,8 @@
     free(temp);
   }
 
+  printf("hEADER %s\n", header);
+
   Seobeo_Handle_Queue(p_handle, (uint8_t*)header, strlen(header));
   Seobeo_Handle_Queue(p_handle, (uint8_t*)body, body_length);
   Seobeo_Handle_Flush(p_handle);
@@ -804,3 +862,44 @@
   Dowa_Array_Free(g_routes);
   g_routes = NULL;
 }
+
+// Written by AI. I don't know what it does.
+void Seobeo_Url_Decode(char *dst, const char *src)
+{
+  char a, b;
+  while (*src) {
+    /* Check if we have a % followed by two valid hex characters */
+    if (*src == '%' && src[1] && src[2]) {
+      a = src[1];
+      b = src[2];
+
+      /* Manual isxdigit check and conversion for 'a' */
+      int a_val = -1;
+      if (a >= '0' && a <= '9')    a_val = a - '0';
+      else if (a >= 'a' && a <= 'f') a_val = a - 'a' + 10;
+      else if (a >= 'A' && a <= 'F') a_val = a - 'A' + 10;
+
+      /* Manual isxdigit check and conversion for 'b' */
+      int b_val = -1;
+      if (b >= '0' && b <= '9')    b_val = b - '0';
+      else if (b >= 'a' && b <= 'f') b_val = b - 'a' + 10;
+      else if (b >= 'A' && b <= 'F') b_val = b - 'A' + 10;
+
+      /* If both were valid hex, combine them */
+      if (a_val != -1 && b_val != -1) {
+        *dst++ = (char)((a_val << 4) | b_val);
+        src += 3;
+        continue;
+      }
+    }
+
+    /* Handle '+' as space, otherwise copy character literally */
+    if (*src == '+') {
+      *dst++ = ' ';
+    } else {
+      *dst++ = *src;
+    }
+    src++;
+  }
+  *dst = '\0';
+}
--- a/seobeo/seobeo.h	Fri Jan 23 22:22:30 2026 -0800
+++ b/seobeo/seobeo.h	Fri Jan 23 22:38:59 2026 -0800
@@ -91,6 +91,9 @@
 /* Destroy response and free all resources. */
 extern void                    Seobeo_Client_Response_Destroy(Seobeo_Client_Response *p_resp);
 
+// --- HTTP Web related helper functions --- //
+extern void    Seobeo_Url_Decode(char *dst, const char *src);
+
 /**
  * WebSocket Client API
  * ------
@@ -324,18 +327,22 @@
 extern void                               Seobeo_WebSocket_Server_Connection_Close(Seobeo_WebSocket_Server_Connection *p_conn, uint16 code, const char *reason);
 
 /* Initialize the router system (called automatically by Seobeo_Web_Server_Start) */
-extern void           Seobeo_Router_Init();
+extern void                  Seobeo_Router_Init();
 /* Register an API route handler. Call before starting server. */
-extern void           Seobeo_Router_Register(const char *method, const char *path_pattern, Seobeo_Route_Handler handler);
+extern void                  Seobeo_Router_Register(const char *method, const char *path_pattern, Seobeo_Route_Handler handler);
+/* Register a streaming route handler. Handler receives client handle for direct streaming. */
+extern void                  Seobeo_Router_Register_Stream(const char *method, const char *path_pattern, Seobeo_Stream_Handler handler);
 /* Clean up router resources */
-extern void           Seobeo_Router_Destroy();
+extern void                  Seobeo_Router_Destroy();
 /* Find matching route handler (internal use) */
-extern Seobeo_Route_Handler Seobeo_Router_Find_Handler(const char *method, const char *path, Seobeo_Request_Entry **pp_request_map, Dowa_Arena *p_arena);
+extern Seobeo_Route_Handler  Seobeo_Router_Find_Handler(const char *method, const char *path, Seobeo_Request_Entry **pp_request_map, Dowa_Arena *p_arena);
 /* Send HTTP response from response map (internal use) */
-extern void           Seobeo_Router_Send_Response(Seobeo_Handle *p_handle, Seobeo_Request_Entry *p_response_map, Dowa_Arena *p_arena);
+extern void                  Seobeo_Router_Send_Response(Seobeo_Handle *p_handle, Seobeo_Request_Entry *p_response_map, Dowa_Arena *p_arena);
 /* Send HTTP response with keep-alive option */
-extern void           Seobeo_Router_Send_Response_KeepAlive(Seobeo_Handle *p_handle, Seobeo_Request_Entry *p_response_map, Dowa_Arena *p_arena, boolean keep_alive);
-extern char          *Seobeo_Web_LoadFile(const char *file_path, size_t *p_file_size);
+extern void                  Seobeo_Router_Send_Response_KeepAlive(Seobeo_Handle *p_handle, Seobeo_Request_Entry *p_response_map, Dowa_Arena *p_arena, boolean keep_alive);
+extern char                 *Seobeo_Web_LoadFile(const char *file_path, size_t *p_file_size);
+/* Being a proxy and keeping the client open */
+extern Seobeo_Stream_Handler Seobeo_Router_Find_Stream_Handler(const char *method, const char *path, Seobeo_Request_Entry **pp_request_map, Dowa_Arena *p_arena);
 
 // --- Helper functions --- //
 /* Destroy handle. It will handle all NULL poointers. */
--- a/seobeo/seobeo_internal.h	Fri Jan 23 22:22:30 2026 -0800
+++ b/seobeo/seobeo_internal.h	Fri Jan 23 22:38:59 2026 -0800
@@ -87,6 +87,13 @@
     Dowa_Arena *p_arena
 );
 
+// Streaming handler - gets direct access to client handle for streaming responses
+typedef void (*Seobeo_Stream_Handler)(
+    Seobeo_Handle *p_client_handle,
+    Seobeo_Request_Entry *p_request_map,
+    Dowa_Arena *p_arena
+);
+
 // --- Parse Header into Dowa Map ---//
 extern int            Seobeo_Web_Header_Parse(Seobeo_Handle *p_handle, Seobeo_Request_Entry **pp_map, Dowa_Arena *p_arena);