stateless_trash_can

无状态垃圾桶

@stateless_trash_can

  • i13l
    PipaQinse233 PipaQinse233 Post #457

    苏联空军55条例常礼

    穿上新制服,乐不可支但其实胖了,暂时还没穿上

    不过差不多一年了,可算集齐一套了

  • ❤️ Liked post in 幽默短片与抽象 meme 楼
    谈笑风生
    IO IO Post #228

    Rotating_earth_(large).gif
    可以了

  • 谈笑风生
    emu_otori 鳳 笑夢 Post #38

    电磁炉煎饼受热不均匀于是做出来的是醜醜早餐 🐹🥞

    IMG_1997.jpeg

  • ❤️ Liked post in 流水账 | 感想 | 发牢骚
    谈笑风生
    ascah ascah Post #230

    尼古猫有点意思,有种 family guy 的感觉

  • ❤️ Liked post in 听歌楼
    游戏 / 娱乐
    1313 ᐕ)ノ Post #51

    我也非常喜欢!非常喜欢这几首!
    第二章的音乐总是让我幻听到果冻里穿梭的音效:cat_mouse: 后面的追逐战也非常紧张刺激:parachute_cat: 醒来的那一段钢琴好平静 记得第二章蓝心处 写着一句诗

    Awake, my heart is a fortress. In dreams I am vulnerable.

    第五章寺庙的音乐 我也曾经试着弹了一下 和它的名字一样 quiet
    就像沉入大洋底部

    镜中世界,充满了不安,这部分音乐给我的印象也很深刻。正如 theo 见证了 madeline 一样,我的 5a 金也是在直播时好友的见证下炼成的

    第七章 向山顶重新出发的时候,旋律唤起了一路上的记忆:innocent_cat: 不知道我何时才能和 madeline 一样登上属于我自己的 celeste 山顶呢?

    另外除了这部分音乐 我还特别喜欢第九章的音乐,虽然我只能磕磕绊绊地通过这一高难度章节🫠

    那段时间只要在做可以听音乐的事情时 我都在循环地听 哪怕是睡觉也让它们在一旁播放 我已经感觉到它们成为了我那段生活里的 bgm,直到现在再次听它们的时候 闭上双眼还是会感觉回到了那些日子

  • 青年杰出贡献上交大門人 紫金港第一皮革<・)))><< Post #216

    第一次看《伊丽莎白》音乐剧 感觉很好看 而且叙事方式比较新颖

  • ❤️ Liked post in 流水账 | 感想 | 发牢骚
    谈笑风生
    ascah ascah Post #221

    这些天热起来了,只想各种冰饮。冰酸奶不错,喝着很爽。如果是绿茶冰一冰应该也很好。绿豆汤放凉了了也不错。以前在家的时候经常去一个老店,黄色的冰沙配上红豆,也挺不错的,好久没吃了

  • i13l
    TwlnfuSprig TwlnfuSprig Post #277

    是嘟😗,用的 astrbot 和梁圣 flash

  • sJtupid JacKeiner Post #209

    分享一点新加坡特色食物(搬运自水源,一些图床没法平移过来,所以是单独复制的)

    价位:整体在 1.2-2x 上海物价,在外面吃对应这个倍率的上海商圈物价,在学校吃对应这个倍率的交大食堂价格,小于 1.2x 的时候不多(也就是说跟国内相同单品一个价,个别这边产量大的可能有),翻一番还要多的情况也没咋见过

    特色菜:我朋友圈里发了几个
    不能说很好吃但绝对不算难吃,不是猎奇取向但足够新奇

    这几天吃下来比较有特色的:

    叉烧/脆皮烧肉/海南鸡[1]/烧鸭——四选一/二——饭/面 🤣

    各种小炸物蒸物,类似广东早茶(?)

    肉骨茶

    奇怪的没有萝卜的萝卜糕,包括炒制和泡汤里多种做法,ref:

    各种椰浆椰奶衍生物:
    椰浆饭

    各种虾衍生物:
    加奶油蘑菇汤配上当地特色香料叶子的也有,炸虾卷/球也有,制烧麦啥的也有

    各种咖喱衍生物(不赘)

    集以上之大成者:请输入文本

    总结

    image.png

    在住处附近食阁要了个 Lontong(椰奶蔬菜咖喱泡米糕 + 酱炸小鱼),又要了对炸鸡翅,4+1.5 新

    总结

    image.png

    image.png

    咸蛋黄鱼皮

    Summary

    image.png


    1. (区别于交大四餐一楼和六餐二楼豉油鸡,虽然这里有的地方也有“豉油鸡饭”) ↩︎

  • 谈笑风生
    emu_otori 鳳 笑夢 Post #31

    中国人没有生食习惯,买回来的蛋可以默认不能生食。不过现在超市都有卖“可生食鸡蛋”会比普通的贵一些些,不是很懂卫生程度怎么样,所以最好还是不要完全生吃

    可以煮温泉蛋 65C 煮 30min 能满足巴氏杀菌标准,煎单面蛋的话可能蛋黄温度会不太够,最好是等到蛋清部分完全熟了、只有蛋黄可以流动时出锅,我这个其实有点生了

  • 谈笑风生
    dvm Dvm Post #24

    学到了:baoquan: 后面准备整点预制饭菜晚上热热

  • 谈笑风生
    dvm Dvm Post #25

    20260811_181931.jpg
    can can word

  • 谈笑风生
    emu_otori 鳳 笑夢 Post #17

    普通摆盘(
    甚至饭团是放桌上的

    虽然没吃饱但感觉还可以,米饭+肉+蛋+菜感觉已经满足了

  • 谈笑风生
    emu_otori 鳳 笑夢 Post #15

    饭团是之前做好冷冻起来的。蒸一锅米饭,浇上一点米醋调节 pH 值,再撒一点盐调味,取大约 150 g 捏成三角形,裹一层海苔,用保鲜膜包着,放凉了就可以冻起来,最多能放几个月的。

    每次吃的时候取一颗放微波炉热几分钟就好,热气腾腾香喷喷的,口感和口味都不输新鲜米饭

    IMG_1870.jpeg

    检察一下冰箱中的饭团储量:充足 😼

    IMG_1953.jpeg

  • IO IO Post #1

    之前这篇是纯AI写的,估计没有几个门友能正常读完。

    考虑到新软件的功能众多,这里侧重写一些特色功能。100%由真人撰写。


    右上角的noti-menu将通知面板和菜单创造性地合而为一,当没有unread notifications时,点击avatar将直接展示菜单。

    菜单项众多,多到已经占满了行动装置屏幕的高度。

    菜单中的功能:

    • layout切换功能:支持在传统Discourse式平铺、Old Reddit式嵌套、BIDU.O Tieba式楼层UI布局间无缝切换。
    • 使用系统字体:本软件学习Sveltekit文档,使用Serif字体。该开关可以退回原行为:使用系统字体
    • 关闭系统通知:系统通知开关,前提是浏览器支持。
    • Logout + Build Info:显示当前部署运行软件的Git Revision和Build Time

    编辑器快捷键:

    Ctrl+Shift+.
    insert datetime
    Ctrl+C/X
    copy/cut whole line when nothing is selected,这个和KDE用户常用的Kate编辑器或Jetbrains IDE的行为保持一致。
    详情可以打开快捷键帮助查看:
    image.png

    行动装置上的按钮

    image.png
    创造性地复用底部导航栏中心的Topics按钮,在 post number>1 的topic中自动变成atlas按钮,从避免了在底部增加第6个按钮。点击atlas将弹出post导航菜单。

    用户Profile Page

    访问/my即可查看自己的Profile Page,自动导航到/u/< username>.
    在这里,你可以(以Blog的UI查看)该用户的全部发布的全部话题(original posts)、查看全部回复(posts),全部图片(Gallery),喜欢(Likes),Bookmarks(default to private),Badges.

    特色功能

    • 自动将所有上传的图片转换成AVIF,自动将所有上传的视频用libsvtav1编码器转换为AV1编码格式的Video,以节省空间和带宽。
    • 无需登录的发帖/回复
    • Fast Dev+Deploy Cycle:不同于Discourse的缓慢的容器生成和缓慢的unicorn server重启时间,新软件使用pnpm build生成用于部署的build文件夹的时间约为20s,node server重启时间约为3s.
    • 查看最新posts:不同于Discourse默认的首页展示Topics,我们的/posts Route展示最新的posts,避免了一个个点进去看最新回复,节约了用户的宝贵时间。
    • 朋友圈UI:我们的/moments Route提供了用户熟悉的界面和样貌。How Déjà vu
    • 快速emoji加载:不同于Discourse每个emoji都是一个图片:
      image.png
      我们的emoji除了非标/自定义emoji是AVIF图片,其他的都是unicode
      image.png
      这大大加快了打开emoji picker时emoji的加载速度。
    • 自动grid:编辑器中上传的多长图片连在一起时,将自动再两端分别包裹[ grid],[ /grid]
      这是一种BBCode格式,支持用mode="tile","slide","off"来选择默认的展示效果为:砖块型,胶片型,关。
  • 谈笑风生
    emu_otori 鳳 笑夢 Post #12

    简单的烤鳕鱼、蒸蛋以及炒油麦菜,朴实无华的夜晚

    IMG_1952.jpeg

  • ❤️ Liked post in 幽默短片与抽象 meme 楼
    谈笑风生
    IO IO Post #166

    的确是bug,已经修复了

  • ❤️ Liked post in 幽默短片与抽象 meme 楼
    谈笑风生
    一只玉米人 玉米🌽 Post #158

    ds能读图吧,只是不是原生多模态🤔

  • ❤️ Liked post in 魔芋爽的随笔&画画楼
    tck 我有魔芋爽 Post #59

    我也刷到过几个二创 我记得有显示好感度的眼镜哈哈哈哈哈
    感觉爱音live2D的脸型比动画尖锐一点是我的错觉吗()
    垃圾桶老师是爱音单推人吗?还是cp党?

  • ❤️ Liked post in 听歌楼
    游戏 / 娱乐
    tck 我有魔芋爽 Post #29

    好有梦核感的pv :smiling_face_with_3_hearts_cat:
    好悲伤的的歌词,但是声音听得人哈特软软
    已放入收藏夹

  • ❤️ Liked post in 魔芋爽的随笔&画画楼
    tck 我有魔芋爽 Post #58

    雀氏知道门的人不多🤔
    但是很多贴子质量都很不错✨️

  • ❤️ Liked post in 幽默短片与抽象 meme 楼
    谈笑风生
    青年杰出贡献上交大門人 紫金港第一皮革<・)))><< Post #155

    现在好像已经有了(虽然我很久没用它了)

  • ❤️ Liked post in 幽默短片与抽象 meme 楼
    谈笑风生
    一只玉米人 玉米🌽 Post #142

    今天有个AI研究员发信息给我说,这是她入行以来最好的夏天。 DeepSeek-V4-Flash正式版发布了,性能超过此前的V4-Pro-Preview,甚至能和Claude 4.8 Opus放在同一张表里比较。Agent基准测试全面飙升——Terminal Bench 2.1跑到 82.7, DeepSWE 54.4, Toolathlon 70.3。 一个2840亿参数、只激活130亿的“轻量版”, 在多项任务上追平了那个曾经遥不可及的 opus。 更离谱的是价格。V4-Pro输出价格只有Opus的4.8%,Flash版更是便宜到令人发指——两块钱处理100万tokens,读完《三体》三部曲再写万字分析,花不到一瓶矿泉水。在开发者群里,突然所有人停下手里的事,开始转发API文档。她说很开心,有种人类黄金时代的错觉。

  • ❤️ Liked post in 听歌楼
    游戏 / 娱乐
    tck 我有魔芋爽 Post #20

    【德语版】无尽敌意(4nim0sity|99.999999999%|)】
    名字叫无尽敌意,却是一首关于"爱"的歌曲:cat_mouse:

  • Hypocrisy rotartsinimdA Post #1

    本帖用于统一记录和分享我收集到的在读或曾在读 JTU,且毕业于或从业于计算机相关专业或行业的学长学姐、师兄师姐分享的内容。

    欢迎大家踊跃投稿或在帖子下直接分享。我这里支持匿名或署名,保护隐私。

    我创建的校友群目前也已约 250+ 成员,欢迎校友加入,尤其是已毕业的师兄师姐、学长学姐。有意向者私聊我即可。

    本帖将作为我唯一的分享和汇总处,因为微信群的内容保障实在难以恭维。

    门里一些你或许会感兴趣的内容:
    2023 年找工作个人 memo
    说说计算机系本科生如何找实习
    再开一个秋招记录贴
    xjtu 计算机保研经验分享
    距离秋招半年,该怎么准备呢
    困顿且挣扎——我的 2025 秋招旅程
    来自学长的毕设相关建议【计算机】
    结束了本科最后一门考试后,你有什么想法?
    Microsoft 人生体验随笔

  • ❤️ Liked post in 听歌楼
    游戏 / 娱乐
    tck 我有魔芋爽 Post #18

    不太懂音乐这方面欸,单纯觉得好听:pig2:
    多刷听歌楼就懂了

  • i13l
    PipaQinse233 PipaQinse233 Post #451

    哦说起来好像漏了 emo 山上的其他照片

    image.png
    image.png
    image.png
    image.png

    那天真的是溜大了,比冰还要爽;我真的是自打记事以来,第一次真真切切感受到了“原来我还可以这样存在”

  • Yukinochyan Yukinochyan Post #1

    分享一些计算机专业的课程资料(课程作业,课程实验,部分往年题等),包括:计算机网络专题实验、编译器专题实验、嵌入式智能系统、软件定义网络、RUST语言程序设计、数据库系统、操作系统原理、软件工程、数字逻辑电路、形式语言与编译。

  • i13l
    PipaQinse233 PipaQinse233 Post #443

    想不起来究竟是哪一张了,我猜当时应该是下面两图之一:

    哥萨克

    野外夜拍之后的整活;也有可能是青岛聚会的合影

    合影

  • i13l
    PipaQinse233 PipaQinse233 Post #439

    手机端传图片好慢,试试这边:

    正面侧面

  • i13l
    PipaQinse233 PipaQinse233 Post #434

    拼车群确实很方便,也差不多 30 分钟,只是司机多走小路,我不是很放心

  • ❤️ Liked post in 听歌楼
    游戏 / 娱乐
    tck 我有魔芋爽 Post #10
  • ❤️ Liked post in 听歌楼
    游戏 / 娱乐
    Xrystal-line 羽流弦ノ迹 Post #9

    昨天是登月57周年
    用通讯采样制作的两首摇滚MV
    标志性的NASA Quindar Tones

    • Go! by Public Service Broadcasting
    • F1配乐版本 by EdwardKerman, Lose My Mind by Hans Zimmer
  • ❤️ Liked post in 听歌楼
    游戏 / 娱乐
    Naaack Naaack Post #8

    最近特别喜欢听Matches(Acoustic)-Stephen Walking、Ephixa、Aaron Richard

  • ❤️ Liked post in 听歌楼
    游戏 / 娱乐
    1313 ᐕ)ノ Post #7

    這是昨晚睡前在聽的其中一個

  • IO IO Post #1

    前言

    本站使用的软件也运行了好多天了,目前各种小bug不断,但是好在没有什么致命的问题。
    为了帮助用户更好地理解本软件的开发情况,所以写一篇文章,介绍有哪些功能,数据是怎么迁移的。

    设计理念

    简单,好用
    侧重点:易用 > 易开发维护 > 拥有各种功能

    实现的功能

    本软件的设计,高度受Discourse启发。但不谋求实现Discourse的全部功能。

    Murmur: Modern Discussion Forum

    murmur is a high-performance, features-rich, Discourse/NimForum-style discussion forum. It is engineered with a hybrid SSR-First (Server-Side Rendering) architecture, delivering instant page loads and optimal SEO crawlers accessibility, while layering on real-time interactive widgets as progressive client-side enhancements.


    🛠️ The Tech Stack

    Murmur's architecture is divided into a high-performance web core, a relational database, and a specialized asset caching layer:

    • Web Core: SvelteKit 2 utilizing Svelte 5 Runes for robust state management.
    • Database & ORM: Drizzle ORM managing SQLite (better-sqlite3 driver) for synchronous operations.
    • Asset Performance: A custom caching proxy written in Rust using Axum (uploads-proxy).
    • Search Subsystem: SQLite FTS5 index combined with the sqlite-vec extension for vector operations, and @node-rs/jieba CJK tokenizer.
    • Real-time Features: Server-Sent Events (SSE) for pub/sub notifications, and Yjs text structures for wiki collaboration.
    • Styling: TailwindCSS v4 with Flowbite Svelte.

    🏛️ System Architecture Preview

    Murmur is designed to maximize speed by avoiding clientside rendering overhead during initial page loads.

    graph TD
        Client[Browser Client]
        SvelteKit[SvelteKit Node.js Server]
        SQLite[(SQLite DB / local.db)]
        Proxy[Rust Uploads-Proxy]
        Upstream[Upstream Assets Server]
        LLM[LLM API / Ollama / Gemini / OpenAI / Anthropic]
    
        Client -->|1. HTTP Page Request / SSE Stream| SvelteKit
        Client -->|2. HTTP Uploaded Files| Proxy
        Proxy -->|3. Cache Miss Fetch| Upstream
        Proxy -->|4. Save locally| Disk[(Local Uploads)]
        SvelteKit -->|5. Synchronous DB Queries| SQLite
        SvelteKit -->|6. Vectors & Generation| LLM
    
    1. Server-Side Rendering: SvelteKit processes the request, queries the SQLite database synchronously through the repository layer, compiles the page into static HTML, and responds.
    2. Client Hydration: The browser displays the HTML, downloads the JavaScript bundles, and hydrates components (e.g. Composer, Likes, Chat).
    3. Live Updates: An SSE connection is established to stream live updates (new posts, edits) to active tabs.
    4. Asset Offloading: The Rust uploads-proxy handles asset queries. If a file is cached locally on disk, it is served; otherwise, the proxy downloads it from the upstream server with retry logic, saves it locally, and serves it.

    📚 Documentation Index

    Below is the directory map of the Murmur system documentation. Click on any link to view detailed guides:

    ⚙️ [System Architecture & Scaling](file:///f/murmur/doc/architecture.md)

    • Detailed breakdown of the request-response lifecycle and hydration.
    • Rust uploads-proxy microservice design (thundering herd protection, atomic writes).
    • Horizontal scaling recommendations (reconciliation, moving from memory SSE to Redis pub/sub, object storage).

    Murmur: System Architecture

    This document describes the high-level architecture of Murmur, including the frontend framework, backend data flow, integration with external services, and horizontal scaling patterns.


    Technical Stack

    Murmur is built using a modern, lightweight JavaScript stack, layered with a high-performance Rust microservice:

    • Frontend / Meta-framework: SvelteKit 2 using Svelte 5 (Runes mode).
    • Styling: TailwindCSS v4 (CSS-first, no tailwind.config.js) & Flowbite Svelte components.
    • Database: SQLite (better-sqlite3 driver) wrapped with Drizzle ORM.
    • Search Vector calculations: SQLite extension sqlite-vec.
    • Search tokenization: @node-rs/jieba for CJK languages.
    • Real-Time Communication: Server-Sent Events (SSE).
    • Collaboration: Yjs text types synced stateless-ly over HTTP.
    • Assets Proxy: Custom Axum-based Rust microservice (uploads-proxy).

    Component Layout & Data Flow

    Murmur leverages Server-Side Rendering (SSR) by default to deliver HTML instantly to crawlers and users. Clientside Svelte components progressively hydrate the page to enable real-time messaging, infinite scroll, and rich editing.

    graph TD
        Client[Browser Client]
        SvelteKit[SvelteKit Node.js Server]
        SQLite[(SQLite DB / local.db)]
        Proxy[Rust Uploads-Proxy]
        Upstream[Upstream Assets Server]
        LLM[LLM API / Ollama / Gemini / OpenAI / Anthropic]
    
        Client -->|1. HTTP / SSE| SvelteKit
        Client -->|2. HTTP Static Assets| Proxy
        Proxy -->|3. Fetch cache miss| Upstream
        Proxy -->|4. Save locally| Disk[(Local Uploads)]
        SvelteKit -->|5. Synchronous Queries| SQLite
        SvelteKit -->|6. Vectors & Generation| LLM
    

    1. Request Lifecycle

    1. The browser requests a page (e.g., /t/slug/id).
    2. SvelteKit's routing resolves the path and calls the server-side load function (+page.server.ts), which queries the database synchronously via the repository layer (src/lib/server/repo.ts).
    3. SvelteKit renders the page as static HTML (SSR-first) and returns it.
    4. The client browser parses the HTML, displays it immediately, and fetches Svelte bundles to hydrate active widgets (e.g., the composer, emoji picker, likes).
    5. The client initiates a Server-Sent Events (SSE) connection to listen to live updates.

    Node.js & Rust uploads-proxy Integration

    User-uploaded attachments and legacy images are cached locally on disk. To prevent heavy Node.js threads from blocking on large file IO operations:

    • Node.js Server: Serves core HTML/JS payloads and handles API mutations.
    • Rust uploads-proxy: Serves files located under /uploads/. It listens on 127.0.0.1:4119 and handles file lookups.
      • Cache Hit: Serves the local file immediately using high-speed async file streaming via tokio-util.
      • Cache Miss: Downloads the asset from the configured upstream server (attempts up to 3 times, with a 2-second sleep duration between attempts), saves it locally, and serves it to the user.
      • Thundering Herd Coalesce: Employs a mutex and tokio::sync::Notify to block duplicate concurrent requests for the same missing asset, ensuring only one download request hits the upstream server.

    Real-Time Engine (SSE Pub/Sub)

    Murmur uses a single-process in-memory pub/sub pattern implemented in [events.ts](file:///f/murmur/src/lib/server/events.ts):

    • Route handlers or repository mutations call publish(topicId, event).
    • The Server-Sent Events endpoint [events/+server.ts](file:///f/murmur/src/routes/t/[slug]/[id]/events/+server.ts) subscribes to updates for a specific topic.
    • It streams JSON-LD chunk lines to the browser's EventSource instance.
    • The UI handles the event reactively to append new posts, update counts, or delete posts without page reloads.

    Horizontal Scaling Strategy

    Because SQLite and the SSE pub/sub system operate within a single process, horizontal scaling (running multiple instances of the Node.js process) requires:

    1. Database Layer: Replace the local better-sqlite3 instance with a replicated SQLite setup (such as LiteFS) or migrate Drizzle configuration to PostgreSQL.
    2. Pub/Sub Layer: Replace the in-memory singleton in [events.ts](file:///f/murmur/src/lib/server/events.ts) with a Redis or RabbitMQ pub/sub server. Route connections to the pub/sub interface so that messages publish across all Node server processes.
    3. Uploads caching: Ensure the uploads-proxy instances point to a shared directory (e.g., NFS, GlusterFS) or replace the local filesystem storage with an Object Store (like AWS S3) by modifying the SvelteKit file upload API.

    🗄️ [Data Model & Database Schema](file:///f/murmur/doc/data-model.md)

    • Relational schema definitions for SQLite.
    • Data design conventions (auto-increments, ISO-8601 timestamps, soft-deletions).
    • Database tables, relationships, and indices optimization.

    Murmur: Data Model & Database Schema

    This document details the database schema, SQLite conventions, and tables managed by Drizzle ORM in Murmur. The database schema is located in [schema.ts](file:///f/murmur/src/lib/server/db/schema.ts).


    Data Layer Conventions

    To simplify imports, backups, and exports from active Discourse installations, Murmur maintains the following database patterns:

    1. Integer Auto-increment Primary Keys: All tables use auto-increment integers. The system user is seeded with ID -1 (matching Discourse conventions).
    2. ISO-8601 Timestamps: Timestamps are stored as text (e.g. 2026-07-15T22:00:00.000Z) rather than SQLite Unix integers. They default to the current time via $defaultFn(() => new Date().toISOString()). This allows lexicographical sorting and matches Discourse CSV files.
    3. Booleans: Stored as integers (0 or 1) using Drizzle's integer(..., { mode: 'boolean' }).
    4. Post Rendering: Posts store both the raw Markdown text (raw) and the cooked sanitized HTML (cooked). Rendering is done via {@html cooked} without dynamic clientside compilation.
    5. Soft Deletions: Deleting topics or posts updates a deletedAt ISO timestamp column instead of deleting database rows.

    Database Tables

    Below is an overview of the Drizzle tables:

    1. users

    Represents forum members, administrators, and moderators.

    • id: Integer primary key (Auto-increment).
    • username: Unique username (Text).
    • name: Full display name (Text, nullable).
    • avatarUrl: URL of the user's avatar.
    • email: User email address.
    • githubId: Bound GitHub ID for SSO.
    • admin / moderator: Role toggles (Booleans).
    • trustLevel: Trust system ranking (0 to 4).
    • silencedUntil: ISO timestamp until which the user is banned from posting.
    • signatureRaw / signatureCooked: Markdown/HTML for post signatures.
    • chatGlobalMuted: Toggle to silence all chat badge notifications (Boolean).

    2. sessions & ssoSyncTickets

    Authentication sessions matching Oslo specifications.

    • sessions.id: SHA-256 hash of the session token.
    • sessions.userId: References users.id (on cascade delete).
    • sessions.expiresAt: Expiration timestamp.

    3. categories & categoryGroups

    Discussion categories which support nesting.

    • categories.id: Integer primary key.
    • categories.slug: Unique URL slug.
    • categories.parentCategoryId: Self-referential FK targeting the parent category.
    • categories.readRestricted: If true, restricts read permissions.
    • categoryGroups: Maps categoryId to groupId and defines permissionType (Read, Create, Reply).

    4. topics & posts

    Forum threads and post items.

    • topics.userId: Thread creator.
    • topics.bumpedAt: ISO timestamp updated on every reply to drive sorting.
    • topics.closed / topics.pinned: State flags.
    • posts.topicId: Parent thread.
    • posts.postNumber: 1-based sequential position within the topic (OP is 1).
    • posts.raw / posts.cooked: Markdown/cooked text.
    • posts.replyToPostNumber: Identifies the post being replied to.
    • posts.wiki: Toggle indicating if the post is editable by non-creators.

    5. postRevisions

    History of edits made outside the grace period.

    • postId: References posts.id.
    • userId: Author of the edit.
    • modifications: JSON string of modified properties (e.g. raw, cooked, title).
    • number: Version sequence number.
    • hidden: If true, hides modifications from normal users.

    6. postEmbeddings & topicRecommendations

    Vector semantic search embeddings and precomputed suggestions.

    • postEmbeddings.postId: References posts.id.
    • postEmbeddings.embedding: Raw vector float buffer (Float32Array).
    • topicRecommendations.topicId: References topics.id.
    • topicRecommendations.recommendations: JSON string list of recommended topic IDs.

    7. chatChannels, chatChannelMembers & chatMessages

    Real-time messaging channels.

    • chatMessages.channelId: Parent channel.
    • chatMessages.message / chatMessages.cooked: Raw/cooked text.
    • chatChannelMembers.muted: Individual channel muting preference.

    Schema DDL & Indices

    Drizzle automatically creates indices to optimize query performance and prevent database locks:

    • posts_topic_number_idx on (posts.topicId, posts.postNumber).
    • topics_category_bumped_idx on (topics.categoryId, topics.bumpedAt).
    • likes_post_user_unq unique index on (likes.postId, likes.userId).
    • topic_recommendations and post_embeddings map primary keys to their parent components to enable swift joins.

    ✍️ [Reply Composer, Drafts & Uploads](file:///f/murmur/doc/features/composer.md)

    • Composer UI controls (height adjustments, split ratios, fullscreen mode).
    • Local storage autosave and server-side syncing database logic.
    • Formatting toolbar helpers, emoji picker, and attachments uploads.
    • Kate-style whole-line keyboard shortcuts (Ctrl+X / Ctrl+C).

    Composer UI, Attachments & Draft Syncing

    This document describes the reply composer UI, clientside formatting shortcuts, the draft autosave engine, and the multi-file attachment pipeline.


    Composer Layout & Controls

    The composer component [Composer.svelte](file:///f/murmur/src/lib/components/Composer.svelte) is modeled after the Discourse editor. It resides in a sticky drawer at the bottom of the screen.

    +-------------------------------------------------------------------+
    |  [v] Drag Handle (Height)                                         |
    +-------------------------------------------------------------------+
    |  Title / Action Summary                      [ ] Full [ _ ] Min  |
    +-------------------------------------------------------------------+
    |  [ B ] [ I ] [ Link ] [ Quote ] [ Tt v ] [ + v ] [ Undo ] [ Redo ]|
    +-------------------------------------------------------------------+
    |  Textarea Input (Markdown)         |  Live HTML Preview           |
    |                                    |                              |
    |                                    |                              |
    |                                    |<---> Vertical Split Resizer  |
    +-------------------------------------------------------------------+
    

    Resizing Features

    • Height Resizing: Users can click and drag the top border handle. The chosen height is written to localStorage.
    • Split ratio Resizing: A vertical split bar sits between the editor and preview panels. Dragging it updates the split percentage dynamically.
    • Fullscreen mode: Toggles the composer to fill the viewport.
    • Minimization drawer: Minimizes the composer into a small, floating status bar at the bottom right.

    Rich Formatting Toolbar & Emojis

    1. Heading Selector (Tt)

    Inserts heading markdown (# to ####) at the cursor selection.

    2. Actions Selector (+)

    • Hide Details: Inserts a folding BBCode detail block:
      <details><summary>Summary</summary>
      
      
      content
      
      
      
    ``` - **Footnote**: Inserts inline footnote syntax (`^[footnote]`). - **LaTeX Math**: Inserts standard math symbols wrapper (`$x^2## 前言 本站使用的软件也运行了好多天了,目前各种小bug不断,但是好在没有什么致命的问题。 为了帮助用户更好地理解本软件的开发情况,所以写一篇文章,介绍有哪些功能,数据是怎么迁移的。

    设计理念

    简单,好用
    侧重点:易用 > 易开发维护 > 拥有各种功能

    实现的功能

    本软件的设计,高度受Discourse启发。但不谋求实现Discourse的全部功能。

    Murmur: Modern Discussion Forum

    murmur is a high-performance, features-rich, Discourse/NimForum-style discussion forum. It is engineered with a hybrid SSR-First (Server-Side Rendering) architecture, delivering instant page loads and optimal SEO crawlers accessibility, while layering on real-time interactive widgets as progressive client-side enhancements.


    🛠️ The Tech Stack

    Murmur's architecture is divided into a high-performance web core, a relational database, and a specialized asset caching layer:

    • Web Core: SvelteKit 2 utilizing Svelte 5 Runes for robust state management.
    • Database & ORM: Drizzle ORM managing SQLite (better-sqlite3 driver) for synchronous operations.
    • Asset Performance: A custom caching proxy written in Rust using Axum (uploads-proxy).
    • Search Subsystem: SQLite FTS5 index combined with the sqlite-vec extension for vector operations, and @node-rs/jieba CJK tokenizer.
    • Real-time Features: Server-Sent Events (SSE) for pub/sub notifications, and Yjs text structures for wiki collaboration.
    • Styling: TailwindCSS v4 with Flowbite Svelte.

    🏛️ System Architecture Preview

    Murmur is designed to maximize speed by avoiding clientside rendering overhead during initial page loads.

    graph TD
        Client[Browser Client]
        SvelteKit[SvelteKit Node.js Server]
        SQLite[(SQLite DB / local.db)]
        Proxy[Rust Uploads-Proxy]
        Upstream[Upstream Assets Server]
        LLM[LLM API / Ollama / Gemini / OpenAI / Anthropic]
    
        Client -->|1. HTTP Page Request / SSE Stream| SvelteKit
        Client -->|2. HTTP Uploaded Files| Proxy
        Proxy -->|3. Cache Miss Fetch| Upstream
        Proxy -->|4. Save locally| Disk[(Local Uploads)]
        SvelteKit -->|5. Synchronous DB Queries| SQLite
        SvelteKit -->|6. Vectors & Generation| LLM
    
    1. Server-Side Rendering: SvelteKit processes the request, queries the SQLite database synchronously through the repository layer, compiles the page into static HTML, and responds.
    2. Client Hydration: The browser displays the HTML, downloads the JavaScript bundles, and hydrates components (e.g. Composer, Likes, Chat).
    3. Live Updates: An SSE connection is established to stream live updates (new posts, edits) to active tabs.
    4. Asset Offloading: The Rust uploads-proxy handles asset queries. If a file is cached locally on disk, it is served; otherwise, the proxy downloads it from the upstream server with retry logic, saves it locally, and serves it.

    📚 Documentation Index

    Below is the directory map of the Murmur system documentation. Click on any link to view detailed guides:

    ⚙️ [System Architecture & Scaling](file:///f/murmur/doc/architecture.md)

    • Detailed breakdown of the request-response lifecycle and hydration.
    • Rust uploads-proxy microservice design (thundering herd protection, atomic writes).
    • Horizontal scaling recommendations (reconciliation, moving from memory SSE to Redis pub/sub, object storage).

    Murmur: System Architecture

    This document describes the high-level architecture of Murmur, including the frontend framework, backend data flow, integration with external services, and horizontal scaling patterns.


    Technical Stack

    Murmur is built using a modern, lightweight JavaScript stack, layered with a high-performance Rust microservice:

    • Frontend / Meta-framework: SvelteKit 2 using Svelte 5 (Runes mode).
    • Styling: TailwindCSS v4 (CSS-first, no tailwind.config.js) & Flowbite Svelte components.
    • Database: SQLite (better-sqlite3 driver) wrapped with Drizzle ORM.
    • Search Vector calculations: SQLite extension sqlite-vec.
    • Search tokenization: @node-rs/jieba for CJK languages.
    • Real-Time Communication: Server-Sent Events (SSE).
    • Collaboration: Yjs text types synced stateless-ly over HTTP.
    • Assets Proxy: Custom Axum-based Rust microservice (uploads-proxy).

    Component Layout & Data Flow

    Murmur leverages Server-Side Rendering (SSR) by default to deliver HTML instantly to crawlers and users. Clientside Svelte components progressively hydrate the page to enable real-time messaging, infinite scroll, and rich editing.

    graph TD
        Client[Browser Client]
        SvelteKit[SvelteKit Node.js Server]
        SQLite[(SQLite DB / local.db)]
        Proxy[Rust Uploads-Proxy]
        Upstream[Upstream Assets Server]
        LLM[LLM API / Ollama / Gemini / OpenAI / Anthropic]
    
        Client -->|1. HTTP / SSE| SvelteKit
        Client -->|2. HTTP Static Assets| Proxy
        Proxy -->|3. Fetch cache miss| Upstream
        Proxy -->|4. Save locally| Disk[(Local Uploads)]
        SvelteKit -->|5. Synchronous Queries| SQLite
        SvelteKit -->|6. Vectors & Generation| LLM
    

    1. Request Lifecycle

    1. The browser requests a page (e.g., /t/slug/id).
    2. SvelteKit's routing resolves the path and calls the server-side load function (+page.server.ts), which queries the database synchronously via the repository layer (src/lib/server/repo.ts).
    3. SvelteKit renders the page as static HTML (SSR-first) and returns it.
    4. The client browser parses the HTML, displays it immediately, and fetches Svelte bundles to hydrate active widgets (e.g., the composer, emoji picker, likes).
    5. The client initiates a Server-Sent Events (SSE) connection to listen to live updates.

    Node.js & Rust uploads-proxy Integration

    User-uploaded attachments and legacy images are cached locally on disk. To prevent heavy Node.js threads from blocking on large file IO operations:

    • Node.js Server: Serves core HTML/JS payloads and handles API mutations.
    • Rust uploads-proxy: Serves files located under /uploads/. It listens on 127.0.0.1:4119 and handles file lookups.
      • Cache Hit: Serves the local file immediately using high-speed async file streaming via tokio-util.
      • Cache Miss: Downloads the asset from the configured upstream server (attempts up to 3 times, with a 2-second sleep duration between attempts), saves it locally, and serves it to the user.
      • Thundering Herd Coalesce: Employs a mutex and tokio::sync::Notify to block duplicate concurrent requests for the same missing asset, ensuring only one download request hits the upstream server.

    Real-Time Engine (SSE Pub/Sub)

    Murmur uses a single-process in-memory pub/sub pattern implemented in [events.ts](file:///f/murmur/src/lib/server/events.ts):

    • Route handlers or repository mutations call publish(topicId, event).
    • The Server-Sent Events endpoint [events/+server.ts](file:///f/murmur/src/routes/t/[slug]/[id]/events/+server.ts) subscribes to updates for a specific topic.
    • It streams JSON-LD chunk lines to the browser's EventSource instance.
    • The UI handles the event reactively to append new posts, update counts, or delete posts without page reloads.

    Horizontal Scaling Strategy

    Because SQLite and the SSE pub/sub system operate within a single process, horizontal scaling (running multiple instances of the Node.js process) requires:

    1. Database Layer: Replace the local better-sqlite3 instance with a replicated SQLite setup (such as LiteFS) or migrate Drizzle configuration to PostgreSQL.
    2. Pub/Sub Layer: Replace the in-memory singleton in [events.ts](file:///f/murmur/src/lib/server/events.ts) with a Redis or RabbitMQ pub/sub server. Route connections to the pub/sub interface so that messages publish across all Node server processes.
    3. Uploads caching: Ensure the uploads-proxy instances point to a shared directory (e.g., NFS, GlusterFS) or replace the local filesystem storage with an Object Store (like AWS S3) by modifying the SvelteKit file upload API.

    🗄️ [Data Model & Database Schema](file:///f/murmur/doc/data-model.md)

    • Relational schema definitions for SQLite.
    • Data design conventions (auto-increments, ISO-8601 timestamps, soft-deletions).
    • Database tables, relationships, and indices optimization.

    Murmur: Data Model & Database Schema

    This document details the database schema, SQLite conventions, and tables managed by Drizzle ORM in Murmur. The database schema is located in [schema.ts](file:///f/murmur/src/lib/server/db/schema.ts).


    Data Layer Conventions

    To simplify imports, backups, and exports from active Discourse installations, Murmur maintains the following database patterns:

    1. Integer Auto-increment Primary Keys: All tables use auto-increment integers. The system user is seeded with ID -1 (matching Discourse conventions).
    2. ISO-8601 Timestamps: Timestamps are stored as text (e.g. 2026-07-15T22:00:00.000Z) rather than SQLite Unix integers. They default to the current time via $defaultFn(() => new Date().toISOString()). This allows lexicographical sorting and matches Discourse CSV files.
    3. Booleans: Stored as integers (0 or 1) using Drizzle's integer(..., { mode: 'boolean' }).
    4. Post Rendering: Posts store both the raw Markdown text (raw) and the cooked sanitized HTML (cooked). Rendering is done via {@html cooked} without dynamic clientside compilation.
    5. Soft Deletions: Deleting topics or posts updates a deletedAt ISO timestamp column instead of deleting database rows.

    Database Tables

    Below is an overview of the Drizzle tables:

    1. users

    Represents forum members, administrators, and moderators.

    • id: Integer primary key (Auto-increment).
    • username: Unique username (Text).
    • name: Full display name (Text, nullable).
    • avatarUrl: URL of the user's avatar.
    • email: User email address.
    • githubId: Bound GitHub ID for SSO.
    • admin / moderator: Role toggles (Booleans).
    • trustLevel: Trust system ranking (0 to 4).
    • silencedUntil: ISO timestamp until which the user is banned from posting.
    • signatureRaw / signatureCooked: Markdown/HTML for post signatures.
    • chatGlobalMuted: Toggle to silence all chat badge notifications (Boolean).

    2. sessions & ssoSyncTickets

    Authentication sessions matching Oslo specifications.

    • sessions.id: SHA-256 hash of the session token.
    • sessions.userId: References users.id (on cascade delete).
    • sessions.expiresAt: Expiration timestamp.

    3. categories & categoryGroups

    Discussion categories which support nesting.

    • categories.id: Integer primary key.
    • categories.slug: Unique URL slug.
    • categories.parentCategoryId: Self-referential FK targeting the parent category.
    • categories.readRestricted: If true, restricts read permissions.
    • categoryGroups: Maps categoryId to groupId and defines permissionType (Read, Create, Reply).

    4. topics & posts

    Forum threads and post items.

    • topics.userId: Thread creator.
    • topics.bumpedAt: ISO timestamp updated on every reply to drive sorting.
    • topics.closed / topics.pinned: State flags.
    • posts.topicId: Parent thread.
    • posts.postNumber: 1-based sequential position within the topic (OP is 1).
    • posts.raw / posts.cooked: Markdown/cooked text.
    • posts.replyToPostNumber: Identifies the post being replied to.
    • posts.wiki: Toggle indicating if the post is editable by non-creators.

    5. postRevisions

    History of edits made outside the grace period.

    • postId: References posts.id.
    • userId: Author of the edit.
    • modifications: JSON string of modified properties (e.g. raw, cooked, title).
    • number: Version sequence number.
    • hidden: If true, hides modifications from normal users.

    6. postEmbeddings & topicRecommendations

    Vector semantic search embeddings and precomputed suggestions.

    • postEmbeddings.postId: References posts.id.
    • postEmbeddings.embedding: Raw vector float buffer (Float32Array).
    • topicRecommendations.topicId: References topics.id.
    • topicRecommendations.recommendations: JSON string list of recommended topic IDs.

    7. chatChannels, chatChannelMembers & chatMessages

    Real-time messaging channels.

    • chatMessages.channelId: Parent channel.
    • chatMessages.message / chatMessages.cooked: Raw/cooked text.
    • chatChannelMembers.muted: Individual channel muting preference.

    Schema DDL & Indices

    Drizzle automatically creates indices to optimize query performance and prevent database locks:

    • posts_topic_number_idx on (posts.topicId, posts.postNumber).
    • topics_category_bumped_idx on (topics.categoryId, topics.bumpedAt).
    • likes_post_user_unq unique index on (likes.postId, likes.userId).
    • topic_recommendations and post_embeddings map primary keys to their parent components to enable swift joins.

    ✍️ [Reply Composer, Drafts & Uploads](file:///f/murmur/doc/features/composer.md)

    • Composer UI controls (height adjustments, split ratios, fullscreen mode).
    • Local storage autosave and server-side syncing database logic.
    • Formatting toolbar helpers, emoji picker, and attachments uploads.
    • Kate-style whole-line keyboard shortcuts (Ctrl+X / Ctrl+C).

    Composer UI, Attachments & Draft Syncing

    This document describes the reply composer UI, clientside formatting shortcuts, the draft autosave engine, and the multi-file attachment pipeline.


    Composer Layout & Controls

    The composer component [Composer.svelte](file:///f/murmur/src/lib/components/Composer.svelte) is modeled after the Discourse editor. It resides in a sticky drawer at the bottom of the screen.

    +-------------------------------------------------------------------+
    |  [v] Drag Handle (Height)                                         |
    +-------------------------------------------------------------------+
    |  Title / Action Summary                      [ ] Full [ _ ] Min  |
    +-------------------------------------------------------------------+
    |  [ B ] [ I ] [ Link ] [ Quote ] [ Tt v ] [ + v ] [ Undo ] [ Redo ]|
    +-------------------------------------------------------------------+
    |  Textarea Input (Markdown)         |  Live HTML Preview           |
    |                                    |                              |
    |                                    |                              |
    |                                    |<---> Vertical Split Resizer  |
    +-------------------------------------------------------------------+
    

    Resizing Features

    • Height Resizing: Users can click and drag the top border handle. The chosen height is written to localStorage.
    • Split ratio Resizing: A vertical split bar sits between the editor and preview panels. Dragging it updates the split percentage dynamically.
    • Fullscreen mode: Toggles the composer to fill the viewport.
    • Minimization drawer: Minimizes the composer into a small, floating status bar at the bottom right.

    Rich Formatting Toolbar & Emojis

    1. Heading Selector (Tt)

    Inserts heading markdown (# to ####) at the cursor selection.

    2. Actions Selector (+)

    • Hide Details: Inserts a folding BBCode detail block:
      <details><summary>Summary</summary>
      
      
      content
      
      
      
    ``` - **Footnote**: Inserts inline footnote syntax (`^[footnote]`). - **LaTeX Math**: Inserts standard math symbols wrapper (). - **Mermaid Flowchart**: Inserts standard Mermaid graphs template. - **Table / Date**: Generates formatted markdown tables or inserts local timestamps.

    3. Emoji Picker

    A custom Svelte 5 component [EmojiPicker.svelte](file:///f/murmur/src/lib/components/EmojiPicker.svelte) displays emoji categories. It supports text-based query filtering and inserts the selected emoji at the cursor.


    Kate-Like Whole Line Editing

    When typing inside the editor, Murmur supports KDE Kate-style shortcuts:

    • Ctrl+C (Copy): If no characters are selected (collapsed selection), copies the entire line containing the cursor (including the newline character).
    • Ctrl+X (Cut): If no selection is active, cuts the entire line and moves lines below it up.
    • Cursor Repositioning: After cutting, the cursor automatically snaps to the start of the next line, saving keystrokes.

    This logic is bound to the handleKeyDown function in the composer. It interacts directly with the browser's clipboard API and forces a tick update on Svelte's reactive states to maintain history.


    Draft Autosave & Database Synchronization

    To prevent data loss, the composer synchronizes drafts using a hybrid approach:

    sequenceDiagram
        participant Client as Browser Textarea
        participant Local as localStorage Cache
        participant API as /api/drafts/sync
        participant DB as SQLite drafts Table
    
        Client->>Local: Save drafts instantly on keystroke (Keydown)
        Client->>API: Periodic debounced save (Every 5 seconds)
        API->>DB: Write draft using compound key (userId + key)
        Note over API, DB: Last-write-wins merging by timestamp
    
    • LocalStorage: Serves as the primary cache to save text instantaneously.
    • Database Synchronization: Every 5 seconds (debounced), the client makes an HTTP POST request to /api/drafts/sync. It pushes drafts to the SQLite database, ensuring edits are preserved across devices.
    • Draft Disposal: Submitting a post deletes both the local storage key and the database row.

    Clipboard Paste & Attachment Pipeline

    The upload interface is triggered by clicking the paperclip icon or pasting an image directly from the clipboard into the editor:

    1. Upload Initiation: An async file reader creates a unique placeholder: [Uploading image.png...] at the current cursor position.
    2. Chromium List safeguard: Before beginning async processing, the file list is cloned into a static array. This prevents Chromium browsers from resetting the active input files when the input element value is cleared.
    3. API Upload: The file is sent via multipart POST to /api/upload. The endpoint validates files against allowed formats (e.g. jpg, png, gif, svg).
    4. Link Replacement: Once the API returns the permanent URL, the placeholder is replaced with the correct Markdown markup: ![filename|widthxheight](url).

    Codeblocks

    • auto-detection of codeblocks based on the first line of the codeblock.
    • mermaid flowcharts.
    • toggle line numbers
    • copy and full-screen buttons

    🔍 [Full-Text & Semantic Search (RAG)](file:///f/murmur/doc/features/search-and-rag.md)

    • SQLite FTS5 full-text indexing with Porter stemming.
    • CJK search query segmentation using Jieba tokenizer.
    • sqlite-vec semantic vector distance search pipeline.
    • RAG AI chatbot integration and context permission safeguards.

    Full-Text & Semantic Search (RAG)

    This document describes the search subsystem in Murmur, including full-text indexing, CJK segmentation, vector semantic calculations, and the RAG (Retrieval-Augmented Generation) chatbot.


    1. Full-Text Search Index (FTS5)

    Full-text search is powered by SQLite's FTS5 extension. During database initialization in [index.ts](file:///f/murmur/src/lib/server/db/index.ts), Murmur configures the virtual table search_index with Porter stemming to enable English word matching (e.g. "runs" matches "running"):

    CREATE VIRTUAL TABLE IF NOT EXISTS search_index USING fts5(
        title,
        content,
        topic_id UNINDEXED,
        post_id UNINDEXED,
        post_number UNINDEXED,
        user_id UNINDEXED,
        category_id UNINDEXED,
        created_at UNINDEXED,
        tokenize = 'porter unicode61'
    );
    

    CJK Query Segmentation (Jieba)

    Standard FTS5 tokenizers do not split Chinese, Japanese, or Korean (CJK) characters into individual words because they lack space boundaries.

    To solve this, Murmur intercepts search requests in [repo.ts](file:///f/murmur/src/lib/server/repo.ts) and parses queries using Jieba (@node-rs/jieba):

    1. RegEx Splitting: The query is split into CJK and non-CJK blocks.
    2. Segmentation: CJK blocks are parsed using jieba.cutAll() to extract words.
    3. FTS5 Wrapping: Extracted tokens are wrapped in double quotes and separated by spaces, converting the search into an implicit AND condition (e.g. 学习 Svelte becomes "学习" "Svelte").

    2. Semantic Search (sqlite-vec)

    Semantic search finds matches based on conceptual similarity rather than exact keyword matches. It uses the sqlite-vec extension to run vector operations inside SQLite.

    • Storage: Embeddings are stored as raw binary BLOBs (Float32Array) in the post_embeddings table to prevent thread blocking (converting JSON strings to arrays in JavaScript is CPU intensive).
    • Startup Migration: On startup, Murmur checks the database. If it finds legacy JSON string representations in the embeddings table, it converts them to binary buffers in batches of 500 rows.
    • Query Resolution: Employs a cosine similarity calculation in SQLite:
      SELECT post_id, vec_distance_cosine(embedding, ?) AS distance
      FROM post_embeddings
      ORDER BY distance ASC
      LIMIT 20;
      

    3. RAG Chatbot Integration

    When a user runs a search query, Murmur displays a RAG chatbot assistant panel next to the search results:

    +-----------------------------------------------------------------+
    | Search Results                     |  🤖 Chatbot Reply          |
    |------------------------------------|----------------------------|
    | 1. Topic: How to run Murmur        |  To run Murmur, execute:   |
    |    By user1 - "Run pnpm dev..."    |  `pnpm dev`. Refer to      |
    |                                    |  /t/murmur/1 for details.  |
    | 2. Topic: Production Setup         |                            |
    |    By user2 - "Compile build..."   |  [ AI thinking indicator ] |
    +-----------------------------------------------------------------+
    

    LLM Streaming client

    The server client in [chatLlm.ts](file:///f/murmur/src/lib/server/chatLlm.ts) streams responses from different providers:

    • Ollama: Parses line-by-line NDJSON chunks.
    • Gemini / OpenAI / Anthropic: Parses Server-Sent Events (SSE) data streams.

    SSE Stream Synthesis

    When a search is requested, the search API endpoint [stream/+server.ts](file:///f/murmur/src/routes/api/search/stream/+server.ts) initiates an SSE stream:

    1. It queries FTS5 and semantic databases for matches.
    2. Results are formatted as Markdown context blocks.
    3. It asks the LLM to summarize the findings and include links to the matched topics (e.g. /t/slug/id/post_number).
    4. It streams response tokens to the client browser via ai_answer_chunk events, which are compiled and rendered in the UI using markdown-it.

    4. Privacy & Permission Filtering

    To prevent information leaks (e.g., exposing private messages or restricted categories in search results or AI summaries):

    • Permission Checking: The search service retrieves category permission rules via getPermittedCategoryIds(user).
    • Pre-filtering: Topics and posts belonging to restricted categories are filtered out of the results before the data is cached or sent to the LLM.
    • RAG Security: The LLM context contains only the results the user has permission to read.

    ✍️ [Revisions & Wiki Editing](file:///f/murmur/doc/features/revisions-and-wiki.md)

    • Post edit revisions, grace period overrides, and modifications storage.
    • Myers split-diff comparison modal and revision rollbacks.
    • Wiki post permissions and stateless Yjs collaborative sync protocol.

    Revisions, Wiki Posts & Yjs Collaborative Editing

    This document explains the version control system for post edits, wiki post privileges, and the collaborative Yjs document synchronization engine.


    1. Post Revisions System

    Murmur tracks the history of changes made to posts and topic details (titles and categories) over time.

    Grace Period & Versioning

    • Editing Grace Period: Managed by the editing_grace_period site setting. If a user edits a post within this threshold (e.g. 5 minutes) after creation, modifications are overwritten without creating a new revision.
    • Revision Capture: Edits made after the grace period increment the post's version (and publicVersion) and save the differences to post_revisions.
    • Modifications Storage: Differences are stored as JSON-serialized change objects containing the previous values of modified fields:
      {
      	"raw": ["Old post raw markdown", "New post raw markdown"],
      	"title": ["Old topic title", "New topic title"]
      }
      

    2. Visual Diff Comparison Modal

    When a post contains revisions, a clickable edit counter (e.g., ✍️ 2) is displayed next to the post's timestamp. Clicking it opens the [PostRevisionsModal.svelte](file:///f/murmur/src/lib/components/PostRevisionsModal.svelte) component:

    • Myers Diff Algorithm: Compares line-by-line differences in raw markdown or HTML tags.
    • Cooked HTML Preview: Toggles to render the formatted HTML output.
    • Reversion: Users with edit permissions can click the "Revert" button to roll back the post to a chosen revision.
    • Visibility controls: Staff members (administrators and moderators) can toggle the visibility of individual revisions to hide sensitive information.

    3. Wiki Posts

    Wiki posts allow collaborative editing by members of the community rather than just the author.

    • Wiki Toggle: Staff members can convert any post into a wiki post.
    • Permissions: Admins can configure the minimum user group or trust level required to edit wiki posts (defaults to staff and trust_level_2 users).
    • Indicators: Displays a wiki badge next to the post to show users they have permission to edit it.

    4. Stateless Yjs HTTP Sync Protocol

    To enable concurrent collaborative editing without keeping persistent WebSocket connections open, Murmur uses a stateless HTTP synchronization protocol:

    sequenceDiagram
        participant Client as Browser Editor
        participant Server as /api/posts/[id]/wiki-sync
        participant DB as SQLite DB
    
        Client->>Server: Send stateVector (base64) & update (base64)
        Server->>DB: Fetch post yjsState BLOB
        Server->>Server: Apply client update to Y.Doc
        Server->>Server: Compute diff using client stateVector
        Server->>DB: Save updated Y.Doc buffer to yjs_state
        Server->>Client: Return update (base64) & merged text
    

    Protocol Steps

    1. Initiation: The editor initializes a Yjs document (Y.Doc) and extracts the text content.
    2. Synchronization Request: The client sends an HTTP POST request to /api/posts/[id]/wiki-sync containing its local Yjs state vector and updates, encoded in base64.
    3. Server Merge:
      • The server loads the post's current yjsState from SQLite.
      • It instantiates a server-side Y.Doc and applies the database state.
      • It applies the base64 update received from the client.
    4. Calculations: The server computes the missing updates for the client using the client's state vector.
    5. DB Update: If the merged text differs from the post's current content, the server runs editPost() to update raw/cooked columns and saves the new Yjs state buffer.
    6. Response: The server returns the missing updates and the updated text to the client.

    💬 [Real-Time Chat & Badges](file:///f/murmur/doc/features/chat.md)

    • Public channels, direct message groups, and message management.
    • Svelte 5 runes unread badge calculation and global/channel muting.

    Real-Time Chat & Unread Badge system

    This document describes the real-time chat subsystem in Murmur, including channels, user privileges, message edits, and the Svelte 5 runes unread badge system.


    1. Chat Channels & DM Rooms

    Murmur includes a built-in real-time chat workspace. It supports two channel types:

    • Public Channels: General channels accessible by all registered members.
    • Direct Messages (DMs): Private chat rooms created between two or more users.

    2. Real-Time Message Flow

    The chat interface streams updates in real time using Server-Sent Events (SSE):

    sequenceDiagram
        participant ClientA as Sender Browser
        participant API as /api/chat/messages
        participant DB as SQLite DB
        participant SSE as SSE Stream Channel
        participant ClientB as Receiver Browser
    
        ClientA->>API: POST send message
        API->>DB: Write message to chat_messages
        API->>SSE: Publish message event
        SSE->>ClientB: Stream JSON event
        Note over ClientB: Append message to UI
    

    Message Modifications

    Users can edit or delete their own messages after they are sent. Moderators and administrators can edit or delete any message. Modifying a message updates the database and publishes an update event over SSE to sync all active clients.


    3. Svelte 5 Runes Unread Badge System

    To manage notification badges dynamically across the app, Murmur uses a client-side global state store powered by Svelte 5 runes:

    • State Store: Defined in a client-safe state module. It tracks active channels, members, read states, and global mute preferences.
    • Dynamic Badge calculations: Unread counts are computed using $derived values:
      // Simplified logic
      const unreadCount = $derived(() => {
      	if (globalMuted) return 0;
      	return channels
      		.filter((c) => !c.muted)
      		.reduce((sum, c) => sum + (c.messagesCount - c.lastReadMessageId), 0);
      });
      
    • Real-Time Updates: When a new message event is received via the SSE connection, the client-side store increments the channel's message count. The UI updates the unread badge automatically.
    • Read State Sync: Reading a channel updates the client-side state and sends an API call to save the lastReadMessageId in the database, syncing the read state across devices.

    4. Muting Preferences

    To prevent notification fatigue, users can configure their alert preferences:

    • Channel Muting: Users can mute individual channels. Muted channels do not trigger badge alerts or red dot indicators.
    • Global Muting: Users can enable global muting in their profile settings. This silences all chat notifications across the site.

    💻 [Developer Guide & Conventions](file:///f/murmur/doc/developer-guide.md)

    • Coding standards (runes, TailwindCSS v4, synchronous queries, types).
    • File-based routing structure.
    • ESLint and Prettier code formatting standards.

    Developer Guide & Conventions

    This document outlines the coding standards, repository layout, and development guidelines for developers working on the Murmur project.


    1. Project Conventions

    Svelte 5 Runes Mode

    • Murmur uses Svelte 5 runes. Use $props(), $state(), $derived(), and $effect() for state management.
    • Do not use legacy Svelte APIs (such as export let, $:, or store.subscribe).
    • The compiler options are inlined in the vite.config.ts file under the Svelte plugin configurations.

    Database Layer

    • SQLite database operations run synchronously.
    • Do not await database queries: Drizzle queries use .all(), .get(), and .run() synchronously.
    • Implement database mutations in [repo.ts](file:///f/murmur/src/lib/server/repo.ts). Do not write inline database queries in routes or page endpoints.

    Code Separation

    • Server-Only Code: Any logic placed under src/lib/server/** is excluded from browser bundles.
    • Shared Types: Clientside modules cannot import server code. Shared structures must be defined in [types.ts](file:///f/murmur/src/lib/types.ts).

    Styling & CSS (TailwindCSS v4)

    • TailwindCSS v4 is integrated as a CSS-first design tool. There is no tailwind.config.js.
    • Configurations, theme overrides, and custom fonts are managed in [layout.css](file:///f/murmur/src/routes/layout.css) using CSS variables.

    2. Directory Routing Structure

    The routing layer uses standard SvelteKit file-based routing under src/routes/:

    • / : Landing page displaying latest topics.
    • /c/[slug]/[id] : Topics filtered by category.
    • /t/[slug]/[id] : Topic thread displaying posts and reply forms. Includes SSE streaming listeners.
    • /new : Full-page composer workspace.
    • /login : Authentication page (GitHub SSO & dev login).
    • /admin/settings : Dashboard for administrators to configure site settings.
    • /sitemap.xml : Dynamic sitemap generation for web crawlers.

    3. Formatting Standards

    Code styling is enforced using Prettier and ESLint:

    • Indent: Tabs.
    • Quotes: Single quotes (').
    • Trailing Commas: None.
    • Line Width: 100 characters.

    Verify formatting using the formatting scripts before submitting changes:

    pnpm lint      # Checks linting and formatting compliance
    pnpm format    # Automatically formats files
    

    🛠️ [Operations & Maintenance Scripts](file:///f/murmur/doc/scripts.md)

    • Standalone operational scripts under scripts/ (import tools, reindexing, post rebaking, and embedding calculations).

    Data Migrations & Operations Scripts

    This document details the standalone command-line scripts located in the /scripts/ folder. These scripts are run using tsx (e.g. pnpm tsx scripts/rebake-posts.ts).


    1. Data Imports & Exports

    [import_discourse_csv.ts](file:///f/murmur/scripts/import_discourse_csv.ts)

    Imports forum databases from a Discourse CSV export.

    • Workflow: Parses user profiles, categories, topics, and posts.
    • Parsing: Translates Discourse emojis, system formatting, and image layouts into Murmur equivalents.
    • Rebaking: Automatically runs the HTML cooking function on all imported posts to populate the cooked column.

    [import_post_revisions.ts](file:///f/murmur/scripts/import_post_revisions.ts)

    Reads historical edit logs from a Discourse CSV export and populates the postRevisions table.

    • Workflow: Converts Discourse YAML modifications into JSON and links them to their respective post IDs.
    • Recalculation: Adjusts version and publicVersion sequences on affected posts.

    2. Topic recommendations & Embeddings

    [embed-posts.ts](file:///f/murmur/scripts/embed-posts.ts)

    Generates vector float embeddings for search calculations.

    • Workflow: Identifies posts that do not have embeddings in the post_embeddings table.
    • Generation: Calls the configured LLM API (e.g. Ollama, OpenAI) to generate vector embeddings.
    • Persistence: Writes vector embeddings to the database as binary buffers.

    [generate-related-topics.ts](file:///f/murmur/scripts/generate-related-topics.ts)

    Computes topic similarity recommendations.

    • Workflow: Calculates vector distances between topic posts.
    • Storage: Saves the top 50 matches for each topic as a JSON list in topic_recommendations.

    3. Maintenance & Optimizations

    [rebake-posts.ts](file:///f/murmur/scripts/rebake-posts.ts)

    Re-processes post content using the Markdown compiler.

    • Usage: Run this script after modifying markdown parser rules, BBCode preprocessors, or HTML sanitization options to update all compiled posts.

    [rehash-compress-replace.ts](file:///f/murmur/scripts/rehash-compress-replace.ts)

    Compresses and optimizes uploaded assets.

    • Workflow: Compresses images on disk, updates their URLs, and updates file paths in raw and cooked post content.

    [reindex.ts](file:///f/murmur/scripts/reindex.ts)

    Rebuilds the virtual FTS5 search index.

    • Workflow: Wipes search_index and indexes all posts in the database.
    • CJK splitting: Uses Jieba to segment Chinese, Japanese, and Korean content.

    ⚡ Quick Start

    1. Prerequisite Installations

    Ensure you have Node.js 26+ and pnpm 11+ installed. If you plan to run the uploads proxy or semantic search, make sure cargo (Rust toolchain) and SQLite are available.

    2. Development Setup

    Clone the repository and install dependencies:

    pnpm install
    

    Configure your environment variables:

    cp .env.example .env
    

    Modify .env to define:

    • DATABASE_URL: Path to your database file (defaults to local.db).
    • ORIGIN: The server domain (e.g. http://localhost:5173).
    • GITHUB_CLIENT_ID / GITHUB_CLIENT_SECRET: OAuth credentials.

    Initialize and seed the database with mock categories, users, and topics:

    pnpm db:push
    pnpm db:seed
    

    Start the Vite development server:

    pnpm dev
    

    Open /login and use the Dev Login button to authenticate as any user without configuring GitHub OAuth.


    📁 Repository Layout

    • src/routes/ - SvelteKit routing layer (pages, endpoints, SSE streams).
    • src/lib/ - Shared code, UI components, and state modules.
    • src/lib/server/ - Server-side modules (database pools, auth Oslo, markdown cooker, search caches).
    • scripts/ - Standalone tools (data imports, re-indexing, embedding pre-calculations).
    • uploads-proxy/ - Rust Axum asset cache proxy server.
    • doc/ - System documentation markdown files.
  • IO IO Post #14

    贴表情有的,different with discourse, 可以贴任意多,你可以体验一下,使用Discourse SSO登录,还支持聊天功能

    进度条是个好建议,应该还要有 jump to first and jump to last

  • ❤️ Liked post in 巧克力测评
    谈笑风生
    IO IO Post #1

    我非常爱食巧克力,从小学三年级同桌送我一条巧克力,我视为珍宝,不舍得吃,即使热化掉一次,又再凝固成新的形状。最终该条巧克力到过期也没被我吃掉。

    从小吃的最多的就是Dove巧克力,一般包含葡萄干、牛奶白、普通巧克力三种。
    因为小时候吃得太多,现在不太想吃Dove.

    高中时妈妈买过一次Kinder建达牛奶巧克力,外包装上儿童头像。外面是巧克力,里面是奶粉。非常甜,每次吃都和长痘强相关。尽管如此,还是买过好几次。感觉很有营养。

    Dove黑巧克力,有 66, 72, 82 % 多种度数。66的味道忘记了,72和82都是十分苦的,我买了200g,放在冰箱里,直到过期了2个月才吃完。

    meiji 70%纯黑巧克力,口感一般般。

    Hershey Kisses水滴型浓纯黑巧克力,50元500g一大包,水滴型小巧的一个个,如果用手捻,包装铝箔纸上面的红色漆会掉。放口袋里容易化,而且铝箔包装不紧,容易漏出来。所以我买了一次就没再买过了。
    后来买过一次常规塑料条块装,30元210g。
    Hershey的优势是全都是Malaysia进口,不像以上全是国产的。口感很不错。但它家的黑巧克力现在缺货,很迷。

    瑞士莲70%排块,进口,临期价 20元多 100g,是我目前为止吃过味道最好的巧克力。
    image

  • 周末 😀 Post #541

    我好像理解了为什么读博会抑郁,当然我没有去看过精卫所以不能说我的心理出现了疾病,但是这一次回家,我怀着对于科研没有底气的心面对着家里对自己学业有成走上人生巅峰的期待,我被其他亲戚以近乎造神的态度拿来和小孩对比来教育小孩,我在我希望可以塑造自己未来人生道路的年纪被家人开始赋予结婚生子的期待,我很痛苦我好像理解了为什么读博会抑郁,当然我没有去看过精卫所以不能说我的心理出现了疾病,但是这一次回家,我怀着对于科研没有底气的心面对着家里对自己学业有成走上人生巅峰的期待,我被其他亲戚以近乎造神的态度拿来和小孩对比来教育小孩,我在我希望可以塑造自己未来人生道路的年纪被家人开始赋予结婚生子的期待,我很痛苦

    cite from 一个水源我爱看的日记楼,因为是日记楼就不放链接了。

    但我终于对于读博可能带来的心理问题有了可以换位思考的同感,还是记一下。希望这位源友读博顺利

  • ❤️ Liked post in [记录贴] 26年西湖暑研
    谈笑风生
    Soyo34325 Soyo34325 Post #7

    谢谢垃圾桶老师!

  • IO IO Post #1

    Are you in favour of replacing Discourse with new Sveltekit-based community app?

    • Approve
    • Reject

    Background:
    The current xjtu.men serves a new community app based on Sveltekit, a framework for rapidly developing robust, performant web applications using Svelte.

    While Discourse is a mature and feature-rich community app, we often feel it is too heavy. The initial loading time can be quite long in bad network circumstances. The deployment and configuration are quite complex, because of too many features it offer and especially because of the plugin and theme component system.

    Discourse is good if you are in a good network condition and you need so many features and customization, while what we need is a community app that just works. We can develop features we need relatively easily thanks to the Sveltekit framework we use, compared to the Ruby on Rails + Emebrjs framework Discourse uses.

  • ❤️ Liked post in [记录贴] 26年西湖暑研
    谈笑风生
    Soyo34325 Soyo34325 Post #5

    最后和一起做访问的实验室同门合租了准备
    但是他要晚几天才能到,所以必须是我去看房
    啊啊啊啊,可我真的一点生活经验都没有,希望不要被骗 🥲
    但是想想还是很不错,两个人一起租一个小公寓,2500,感觉还不错,之前看的公寓一个月就要2000
    3号去学校领工牌、认工位,然后4号实验室应该是放假,然后就去看房
    之后可以去宜家买一些用的上的东西!
    但是杭州的宜家离云栖小镇好远
    曼妙的实习生活要开始了,一切都是崭新崭新的

  • ❤️ Liked post in 好玩滴夢
    谈笑风生
    setsuna 做回自己世莉架 Post #6

    是这样的呢。说不定我其实是布列塔尼的皇子,避祸隐藏身份生活在日本,但不幸被当局找到,抹除记忆。(笑


    对记忆太过较真只会陷入虚无,但完全相信自己的记忆,比如考试完全心算不打草稿也不大可能。取乎其中吧,至少此刻的记忆是真实的,过好当下。

    至于梦境的跨度太大以至于是否无法排除自己的幻觉因素,我对于 “两个相距几年、具有因果关系的梦” 也不是全部都肯定是真的,但有一部分因为自己在几年前写了日记,告诉了父母朋友,所以有办法验证真实性

  • setsuna 做回自己世莉架 Post #3

    突然意识到为什么xjtu.app叫土原,大概相对于上交的水源吧?

  • ❤️ Liked post in [记录贴] 26年西湖暑研
    谈笑风生
    Soyo34325 Soyo34325 Post #2

    期末周何时结束!!!
    7月1号会先去南京玩一圈,有没有uu推荐的南京必吃还有南京夜生活去处 🥲

    456b8ae078f07c601baef31f594b86c0

  • ❤️ Liked post in 好玩滴夢
    谈笑风生
    DHT DHT Post #3

    🌿。好久没做过夢了,好久没做过美夢了。

  • ❤️ Liked post in 13 的塬塬空间
    谈笑风生
    1313 ᐕ)ノ Post #286

    不知道蘋果更新了什麼小巧思,給網頁應用加了個返回前進按鈕,導致我無法點到chat的發送 🫪

    出差回來發生了好多事,只覺得頭好痛 😵‍💫

  • ❤️ Liked post in [记录贴] 26年西湖暑研
    谈笑风生
    Soyo34325 Soyo34325 Post #1

    今年拿到了西湖大学某个神秘课题组的暑研名额,第一次有机会做线下实习
    目前定下几个小小目标想要在这个暑假完成

    • 去千岛湖骑车
    • 自己租房,想要一个带厨房的公寓,想自己做饭吃
    • 想买一把电吉他,组一辈子乐队
  • ❤️ Liked post in 水·暑期狂想曲
    谈笑风生
    setsuna 做回自己世莉架 Post #1

    水一帖。

    不知道门友对暑日和假期有什么印象,又有什么规划。成长在中国火炉的我,记忆里的夏日总是无尽灿烂的烈日倾盆而下,裹覆着大地和生灵万物。从江边袭来的氤氲水汽,锁住已经被粘腻汗水打湿的毛孔。身体的热量无处散发,积郁在胸腔和脑腔。热,真的很热,人像是蒸笼里的螃蟹,被炙烤、汽蒸着心神,被折磨、剥夺理智。

    但我喜欢夏天。冬天太阴郁,春天和秋天太短暂。只有夏日有明媚的蔚蓝色天空。深沉的蓝色好像windows7的壁纸一样纯净。还有西瓜,夏季给人类的馈赠,没有比西瓜更好吃的水果,多汁清甜,但不酸不腻,只是我妈不允许我多吃,避免我拉肚子。

    夏天还有一年中最长的假期。暑假里我有时间拜访舅舅,和表弟在老家漫山遍野地乱跑。折下树枝,然后去山另一边的小卖部买彩纸做风车;晚上睡在一起但比力气摔跤到半夜;拿把柴刀砍树,制作两把圣剑然后决斗... 种种行为大人们看来幼稚,但给我们无尽快乐。遗憾的是年龄渐长,两人没有了共同话题。我还是当年那般幼稚,但他已经长大成熟了。

    暑假里还会和同学相约,每日一同沿江骑行,到区图书馆看书、游戏。家乡的河流真的非常、非常美丽,笔直宽阔的滨江路沿着江水延伸,沿江一侧是茂密的树林,另一侧是缓缓的山体和防滑坡墙,山脚的洋房,一所大学和区图书馆。我和同学在午后两点顶着四十度的高温骑车到图书馆,在图书馆看书、打游戏。我喜好阅读科幻文学,在图书馆里看完了刘慈欣所有的小说,长篇如三体、球闪,短篇如山、赡养上帝等(超新星纪元的各版本还是在网上才阅读完全);因为没有电话卡,连不上网,我们只能玩平板上的单机游戏。桥梁建筑(poly bridge)成为我们的最爱,两个人交替提出改进意见,优化桥梁设计直到通关。我们至今仍然保持联系,但他在北京上学,所以平时无法见面。

    人总是会美化经历,抛却记忆中的不快,塑造美好的回忆,但曾经的快乐的确真实。

    日子总不会太好,也不会太差。我是贪婪的物种,即使未来会创造新的快乐回忆,但因为过去的快乐无法重现,我也分外怀念那些逝去的日子。

    这个暑假,机械专业实习和重修结束后,我又要返回家乡了。这次我期待着家里肥美的狸花猫,想狠狠撸它;期待着能用攒的钱买一台无人机,在镜头里俯瞰我的家乡;期待着看完《计网自顶向下》后开始学习计组...

    最后,祝福所有门友暑假快乐,度过一个难忘的快乐夏天!

  • ❤️ Liked post in 解构栈
    Soyo34325 Soyo34325 Post #54

    好图!偷了