Get started

การออกแบบการนำเข้าธีมกำหนดเองจาก Tweakcn

การออกแบบการนำเข้าธีมกำหนดเองจาก Tweakcn

สถานะ: ได้รับอนุมัติในเทอร์มินัลเมื่อ 2026-04-22

สรุป

เพิ่มสล็อตธีมกำหนดเองของ UI ควบคุมแบบภายในเบราว์เซอร์เพียงหนึ่งสล็อตที่สามารถนำเข้าได้จากลิงก์แชร์ของ tweakcn กลุ่มธีมในตัวที่มีอยู่ยังคงเป็น claw, knot และ dash กลุ่มใหม่ custom ทำงานเหมือนกลุ่มธีม OpenClaw ปกติ และรองรับโหมด light, dark และ system เมื่อเพย์โหลด tweakcn ที่นำเข้ามีชุดโทเค็นทั้งแบบสว่างและมืด

ธีมที่นำเข้าจะถูกจัดเก็บเฉพาะในโปรไฟล์เบราว์เซอร์ปัจจุบันร่วมกับการตั้งค่า UI ควบคุมที่เหลือ ไม่ถูกเขียนลงในการกำหนดค่า Gateway และไม่ซิงก์ข้ามอุปกรณ์หรือเบราว์เซอร์

ปัญหา

ระบบธีมของ UI ควบคุมในปัจจุบันถูกจำกัดไว้กับกลุ่มธีมที่ฮาร์ดโค้ดไว้สามกลุ่ม:

  • ui/src/ui/theme.ts
  • ui/src/ui/views/config.ts
  • ui/src/styles/base.css

ผู้ใช้สามารถสลับระหว่างกลุ่มในตัวและตัวแปรโหมดได้ แต่ไม่สามารถนำเข้าธีมจาก tweakcn ได้โดยไม่แก้ไข CSS ของ repo ผลลัพธ์ที่ต้องการมีขอบเขตเล็กกว่าระบบธีมทั่วไป: คงธีมในตัวสามธีมไว้ และเพิ่มสล็อตนำเข้าที่ผู้ใช้ควบคุมได้หนึ่งสล็อตซึ่งสามารถแทนที่ได้จากลิงก์ tweakcn

เป้าหมาย

  • คงกลุ่มธีมในตัวที่มีอยู่ไว้โดยไม่เปลี่ยนแปลง
  • เพิ่มสล็อตกำหนดเองที่นำเข้าได้เพียงหนึ่งสล็อต ไม่ใช่คลังธีม
  • รับลิงก์แชร์ของ tweakcn หรือ URL ตรง https://tweakcn.com/r/themes/{id}
  • บันทึกธีมที่นำเข้าไว้ในพื้นที่จัดเก็บภายในของเบราว์เซอร์เท่านั้น
  • ทำให้สล็อตที่นำเข้าใช้งานได้กับตัวควบคุมโหมด light, dark และ system ที่มีอยู่
  • รักษาพฤติกรรมเมื่อเกิดความล้มเหลวให้ปลอดภัย: การนำเข้าที่ไม่ถูกต้องต้องไม่ทำให้ธีม UI ที่ใช้งานอยู่เสียหาย

สิ่งที่ไม่ใช่เป้าหมาย

  • ไม่มีคลังหลายธีมหรือรายการนำเข้าแบบภายในเบราว์เซอร์
  • ไม่มีการบันทึกฝั่ง Gateway หรือการซิงก์ข้ามอุปกรณ์
  • ไม่มีตัวแก้ไข CSS อิสระหรือตัวแก้ไข JSON ธีมดิบ
  • ไม่มีการโหลดแอสเซ็ตฟอนต์ระยะไกลจาก tweakcn โดยอัตโนมัติ
  • ไม่พยายามรองรับเพย์โหลด tweakcn ที่เปิดเผยเพียงโหมดเดียว
  • ไม่มีการปรับโครงสร้างระบบธีมทั่วทั้ง repo นอกเหนือจาก seam ที่จำเป็นสำหรับ UI ควบคุม

การตัดสินใจของผู้ใช้ที่ทำไว้แล้ว

  • คงธีมในตัวสามธีมไว้
  • เพิ่มสล็อตนำเข้าที่ขับเคลื่อนด้วย tweakcn หนึ่งสล็อต
  • จัดเก็บธีมที่นำเข้าไว้ในเบราว์เซอร์ ไม่ใช่การกำหนดค่า Gateway
  • รองรับ light, dark และ system สำหรับสล็อตที่นำเข้า
  • การเขียนทับสล็อตกำหนดเองด้วยการนำเข้าครั้งถัดไปคือพฤติกรรมที่ตั้งใจไว้

แนวทางที่แนะนำ

เพิ่มรหัสกลุ่มธีมที่สี่คือ custom ในโมเดลธีมของ UI ควบคุม กลุ่ม custom จะเลือกได้เฉพาะเมื่อมีการนำเข้า tweakcn ที่ถูกต้องเท่านั้น เพย์โหลดที่นำเข้าจะถูกทำให้เป็นมาตรฐานเป็นระเบียนธีมกำหนดเองเฉพาะ OpenClaw และจัดเก็บในพื้นที่จัดเก็บภายในของเบราว์เซอร์ร่วมกับการตั้งค่า UI ที่เหลือ

ขณะรัน OpenClaw จะแสดงแท็ก <style> ที่จัดการอยู่ ซึ่งกำหนดบล็อกตัวแปร CSS กำหนดเองที่ resolve แล้ว:

:root[data-theme="custom"] { ... }
:root[data-theme="custom-light"] { ... }

วิธีนี้ทำให้ตัวแปรธีมกำหนดเองถูกจำกัดขอบเขตไว้กับกลุ่ม custom และหลีกเลี่ยงการรั่วไหลของตัวแปร CSS แบบ inline เข้าไปในกลุ่มในตัว

สถาปัตยกรรม

โมเดลธีม

อัปเดต ui/src/ui/theme.ts:

  • ขยาย ThemeName ให้รวม custom
  • ขยาย ResolvedTheme ให้รวม custom และ custom-light
  • ขยาย VALID_THEME_NAMES
  • อัปเดต resolveTheme() เพื่อให้ custom สะท้อนพฤติกรรมกลุ่มที่มีอยู่:
    • custom + dark -> custom
    • custom + light -> custom-light
    • custom + system -> custom หรือ custom-light ตามค่ากำหนดของ OS

ไม่มีการเพิ่ม alias เดิมสำหรับ custom

โมเดลการบันทึกข้อมูล

ขยายการบันทึก UiSettings ใน ui/src/ui/storage.ts ด้วยเพย์โหลดธีมกำหนดเองแบบไม่บังคับหนึ่งรายการ:

  • customTheme?: ImportedCustomTheme

รูปทรงข้อมูลที่แนะนำให้จัดเก็บ:

type ImportedCustomTheme = {
  sourceUrl: string;
  themeId: string;
  label: string;
  importedAt: string;
  light: Record<string, string>;
  dark: Record<string, string>;
};

หมายเหตุ:

  • sourceUrl จัดเก็บอินพุตเดิมของผู้ใช้หลังการทำให้เป็นมาตรฐาน
  • themeId คือรหัสธีม tweakcn ที่ดึงออกมาจาก URL
  • label คือฟิลด์ name ของ tweakcn เมื่อมีอยู่ มิฉะนั้นใช้ Custom
  • light และ dark คือแมปโทเค็น OpenClaw ที่ทำให้เป็นมาตรฐานแล้ว ไม่ใช่เพย์โหลด tweakcn ดิบ
  • เพย์โหลดที่นำเข้าอยู่ข้างการตั้งค่าแบบภายในเบราว์เซอร์อื่น ๆ และถูก serialize ในเอกสาร local-storage เดียวกัน
  • หากข้อมูลธีมกำหนดเองที่จัดเก็บไว้หายไปหรือไม่ถูกต้องระหว่างโหลด ให้ละเว้นเพย์โหลดและ fallback เป็น theme: "claw" เมื่อกลุ่มที่บันทึกไว้เป็น custom

การใช้งานขณะรัน

เพิ่มตัวจัดการ stylesheet ธีมกำหนดเองแบบแคบใน runtime ของ UI ควบคุม โดยเป็นเจ้าของใกล้ ui/src/ui/app-settings.ts และ ui/src/ui/theme.ts

ความรับผิดชอบ:

  • สร้างหรืออัปเดตแท็ก <style id="openclaw-custom-theme"> ที่เสถียรหนึ่งแท็กใน document.head
  • ปล่อย CSS เฉพาะเมื่อมีเพย์โหลดธีมกำหนดเองที่ถูกต้อง
  • ลบเนื้อหาแท็ก style เมื่อเพย์โหลดถูกล้าง
  • คง CSS ของกลุ่มในตัวไว้ใน ui/src/styles/base.css; อย่าแทรกโทเค็นที่นำเข้าเข้าไปใน stylesheet ที่ check in ไว้

ตัวจัดการนี้ทำงานทุกครั้งที่มีการโหลด บันทึก นำเข้า หรือล้างการตั้งค่า

ตัวเลือกโหมดสว่าง

การใช้งานควรเลือกใช้ data-theme-mode="light" สำหรับสไตล์โหมดสว่างข้ามกลุ่ม แทนการกรณีพิเศษ custom-light หาก selector ที่มีอยู่ถูกตรึงกับ data-theme="light" และจำเป็นต้องใช้กับทุกกลุ่มแบบสว่าง ให้ขยาย selector นั้นเป็นส่วนหนึ่งของงานนี้

UX การนำเข้า

อัปเดต ui/src/ui/views/config.ts ในส่วน Appearance:

  • เพิ่มการ์ดธีม Custom ข้าง Claw, Knot และ Dash
  • แสดงการ์ดเป็น disabled เมื่อไม่มีธีมกำหนดเองที่นำเข้าไว้
  • เพิ่มแผงนำเข้าใต้กริดธีมพร้อม:
    • ช่องกรอกข้อความหนึ่งช่องสำหรับลิงก์แชร์ของ tweakcn หรือ URL /r/themes/{id}
    • ปุ่ม Import หนึ่งปุ่ม
    • เส้นทาง Replace หนึ่งเส้นทางเมื่อมีเพย์โหลดกำหนดเองอยู่แล้ว
    • การกระทำ Clear หนึ่งรายการเมื่อมีเพย์โหลดกำหนดเองอยู่แล้ว
  • แสดงป้ายกำกับธีมที่นำเข้าและโฮสต์ต้นทางเมื่อมีเพย์โหลดอยู่
  • หากธีมที่ใช้งานอยู่คือ custom การนำเข้าการแทนที่จะมีผลทันที
  • หากธีมที่ใช้งานอยู่ไม่ใช่ custom การนำเข้าจะเพียงจัดเก็บเพย์โหลดใหม่ไว้จนกว่าผู้ใช้จะเลือกการ์ด Custom

ตัวเลือกธีมในการตั้งค่าแบบเร็วใน ui/src/ui/views/config-quick.ts ควรแสดง Custom เฉพาะเมื่อมีเพย์โหลดอยู่ด้วย

การแยกวิเคราะห์ URL และการ fetch ระยะไกล

เส้นทางนำเข้าในเบราว์เซอร์รับ:

  • https://tweakcn.com/themes/{id}
  • https://tweakcn.com/r/themes/{id}

การใช้งานควรทำให้ทั้งสองรูปแบบเป็นมาตรฐานเป็น:

  • https://tweakcn.com/r/themes/{id}

จากนั้นเบราว์เซอร์จะ fetch endpoint /r/themes/{id} ที่ทำให้เป็นมาตรฐานแล้วโดยตรง

ใช้ตัวตรวจสอบ schema แบบแคบสำหรับเพย์โหลดภายนอก แนะนำให้ใช้ zod เพราะนี่คือขอบเขตภายนอกที่ไม่น่าเชื่อถือ

ฟิลด์ระยะไกลที่จำเป็น:

  • name ระดับบนสุดเป็น string แบบไม่บังคับ
  • cssVars.theme เป็น object แบบไม่บังคับ
  • cssVars.light เป็น object
  • cssVars.dark เป็น object

หาก cssVars.light หรือ cssVars.dark ขาดหายไป ให้ปฏิเสธการนำเข้า นี่เป็นเจตนา: พฤติกรรมผลิตภัณฑ์ที่อนุมัติคือการรองรับโหมดเต็มรูปแบบ ไม่ใช่การสังเคราะห์ด้านที่ขาดหายไปแบบพยายามให้ดีที่สุด

การแมปโทเค็น

อย่าสะท้อนตัวแปร tweakcn แบบตรง ๆ โดยไม่คัดกรอง ให้ทำ subset ที่มีขอบเขตให้เป็นมาตรฐานเป็นโทเค็น OpenClaw และ derive ส่วนที่เหลือใน helper

โทเค็นที่นำเข้าโดยตรง

จากแต่ละบล็อกโหมดของ tweakcn:

  • background
  • foreground
  • card
  • card-foreground
  • popover
  • popover-foreground
  • primary
  • primary-foreground
  • secondary
  • secondary-foreground
  • muted
  • muted-foreground
  • accent
  • accent-foreground
  • destructive
  • destructive-foreground
  • border
  • input
  • ring
  • radius

จาก cssVars.theme ที่ใช้ร่วมกันเมื่อมีอยู่:

  • font-sans
  • font-mono

หากบล็อกโหมด override font-sans, font-mono หรือ radius ค่าภายในโหมดจะชนะ

โทเค็นที่ derive สำหรับ OpenClaw

ตัวนำเข้าจะ derive ตัวแปรเฉพาะ OpenClaw จากสีฐานที่นำเข้า:

  • --bg-accent
  • --bg-elevated
  • --bg-hover
  • --panel
  • --panel-strong
  • --panel-hover
  • --chrome
  • --chrome-strong
  • --text
  • --text-strong
  • --chat-text
  • --muted
  • --muted-strong
  • --accent-hover
  • --accent-muted
  • --accent-subtle
  • --accent-glow
  • --focus
  • --focus-ring
  • --focus-glow
  • --secondary
  • --secondary-foreground
  • --danger
  • --danger-muted
  • --danger-subtle

กฎการ derive อยู่ใน helper แบบ pure เพื่อให้ทดสอบได้โดยอิสระ สูตรผสมสีที่แน่นอนเป็นรายละเอียดการใช้งาน แต่ helper ต้องเป็นไปตามข้อจำกัดสองข้อ:

  • รักษาคอนทราสต์ให้อ่านได้ใกล้เคียงกับเจตนาของธีมที่นำเข้า
  • ให้เอาต์พุตที่เสถียรสำหรับเพย์โหลดที่นำเข้าเดียวกัน

โทเค็นที่ละเว้นใน v1

โทเค็น tweakcn เหล่านี้ถูกละเว้นโดยเจตนาในเวอร์ชันแรก:

  • chart-*
  • sidebar-*
  • font-serif
  • shadow-*
  • tracking-*
  • letter-spacing
  • spacing

วิธีนี้จำกัดขอบเขตไว้ที่โทเค็นที่ UI ควบคุมปัจจุบันต้องการจริง ๆ

ฟอนต์

สตริง font stack จะถูกนำเข้าหากมีอยู่ แต่ OpenClaw จะไม่โหลดแอสเซ็ตฟอนต์ระยะไกลใน v1 หาก stack ที่นำเข้าอ้างอิงฟอนต์ที่ไม่มีในเบราว์เซอร์ พฤติกรรม fallback ปกติจะถูกใช้

พฤติกรรมเมื่อเกิดความล้มเหลว

การนำเข้าที่ไม่ถูกต้องต้อง fail closed

  • รูปแบบ URL ไม่ถูกต้อง: แสดงข้อผิดพลาด validation แบบ inline และไม่ fetch
  • โฮสต์หรือรูปแบบ path ไม่รองรับ: แสดงข้อผิดพลาด validation แบบ inline และไม่ fetch
  • เครือข่ายล้มเหลว, response ไม่ OK หรือ JSON ผิดรูปแบบ: แสดงข้อผิดพลาด inline และคงเพย์โหลดที่จัดเก็บปัจจุบันไว้โดยไม่แตะต้อง
  • Schema ล้มเหลวหรือบล็อก light/dark ขาดหาย: แสดงข้อผิดพลาด inline และคงเพย์โหลดที่จัดเก็บปัจจุบันไว้โดยไม่แตะต้อง
  • การกระทำ Clear:
    • ลบเพย์โหลดกำหนดเองที่จัดเก็บไว้
    • ลบเนื้อหาแท็ก style กำหนดเองที่จัดการอยู่
    • หาก custom ใช้งานอยู่ ให้สลับกลุ่มธีมกลับเป็น claw
  • เพย์โหลดกำหนดเองที่จัดเก็บไว้ไม่ถูกต้องในการโหลดครั้งแรก:
    • ละเว้นเพย์โหลดที่จัดเก็บไว้
    • ไม่ปล่อย CSS กำหนดเอง
    • หากกลุ่มธีมที่บันทึกไว้เป็น custom ให้ fallback เป็น claw

ไม่ควรมีจุดใดที่การนำเข้าล้มเหลวแล้วทำให้เอกสารที่ใช้งานอยู่มีตัวแปร CSS กำหนดเองบางส่วนถูกใช้งาน

ไฟล์ที่คาดว่าจะเปลี่ยนในการใช้งาน

ไฟล์หลัก:

  • ui/src/ui/theme.ts
  • ui/src/ui/storage.ts
  • ui/src/ui/app-settings.ts
  • ui/src/ui/views/config.ts
  • ui/src/ui/views/config-quick.ts
  • ui/src/styles/base.css

Helper ใหม่ที่เป็นไปได้:

  • ui/src/ui/custom-theme.ts

การทดสอบ:

  • ui/src/ui/app-settings.test.ts
  • ui/src/ui/storage.node.test.ts
  • ui/src/ui/views/config.browser.test.ts
  • การทดสอบแบบมุ่งเน้นใหม่สำหรับการแยกวิเคราะห์ URL และการทำให้เพย์โหลดเป็นมาตรฐาน

การทดสอบ

coverage ขั้นต่ำของการใช้งาน:

  • แยกวิเคราะห์ URL ลิงก์แชร์เป็นรหัสธีม tweakcn
  • ทำให้ /themes/{id} และ /r/themes/{id} เป็นมาตรฐานเป็น URL สำหรับ fetch
  • ปฏิเสธโฮสต์ที่ไม่รองรับและรหัสที่ผิดรูปแบบ
  • ตรวจสอบรูปทรงเพย์โหลด tweakcn
  • แมปเพย์โหลด tweakcn ที่ถูกต้องเป็นแมปโทเค็น OpenClaw แบบ light และ dark ที่ทำให้เป็นมาตรฐาน
  • โหลดและบันทึกเพย์โหลดกำหนดเองในการตั้งค่าแบบภายในเบราว์เซอร์
  • resolve custom สำหรับ light, dark และ system
  • ปิดใช้งานการเลือก Custom เมื่อไม่มีเพย์โหลด
  • ใช้ธีมที่นำเข้าทันทีเมื่อ custom ใช้งานอยู่แล้ว
  • fallback เป็น claw เมื่อธีมกำหนดเองที่ใช้งานอยู่ถูกล้าง

เป้าหมายการตรวจสอบด้วยตนเอง:

  • นำเข้าธีม tweakcn ที่รู้จักจาก Settings
  • สลับระหว่าง light, dark และ system
  • สลับระหว่าง custom และกลุ่มในตัว
  • โหลดหน้าใหม่และยืนยันว่าธีมกำหนดเองที่นำเข้ายังคงอยู่เฉพาะภายในเครื่อง

หมายเหตุการ rollout

ฟีเจอร์นี้ตั้งใจให้มีขนาดเล็ก หากผู้ใช้ขอหลายธีมที่นำเข้า การเปลี่ยนชื่อ การส่งออก หรือการซิงก์ข้ามอุปกรณ์ในภายหลัง ให้ถือว่าเป็นการออกแบบต่อยอด อย่าสร้าง abstraction ของคลังธีมไว้ล่วงหน้าในการใช้งานนี้