├── public └── icon.png ├── src ├── app │ ├── favicon.ico │ ├── fonts │ │ ├── JetBrainsMono.woff2 │ │ └── HarmonyOS_Sans.woff2 │ ├── font.tsx │ ├── [lang] │ │ ├── page.tsx │ │ └── layout.tsx │ ├── layout.tsx │ ├── api │ │ └── dpaste │ │ │ └── route.ts │ └── globals.css ├── cangjie.d.ts ├── examples │ ├── hello-world.cj │ ├── hello-world.en.cj │ ├── cffi.cj │ ├── cffi.en.cj │ ├── lambda.en.cj │ ├── lambda.cj │ ├── extend.cj │ ├── extend.en.cj │ ├── function-btree.en.cj │ ├── function-btree.cj │ ├── struct-btree.cj │ ├── struct-btree.en.cj │ ├── for.cj │ ├── for.en.cj │ ├── option-parse.cj │ ├── option-parse.en.cj │ ├── prop-btree.cj │ ├── prop-btree.en.cj │ ├── variables.cj │ ├── thread.en.cj │ ├── generic-btree.cj │ ├── generic-btree.en.cj │ ├── thread.cj │ ├── class-btree.cj │ ├── class-btree.en.cj │ ├── enum-btree.en.cj │ ├── enum-btree.cj │ ├── enum-expr.cj │ ├── enum-expr.en.cj │ ├── array.cj │ ├── array.en.cj │ ├── variables.en.cj │ ├── functional.cj │ ├── functional.en.cj │ └── cube.cj ├── components │ ├── EnglishWrapper.tsx │ ├── ChineseWrapper.tsx │ ├── Wrapper.tsx │ ├── ui │ │ ├── sonner.tsx │ │ ├── label.tsx │ │ ├── switch.tsx │ │ ├── popover.tsx │ │ ├── tabs.tsx │ │ ├── resizable.tsx │ │ ├── button.tsx │ │ ├── dialog.tsx │ │ └── command.tsx │ ├── LabelContainer.tsx │ ├── TrackingScript.tsx │ ├── LinguiClientProvider.tsx │ ├── ShareDialog.tsx │ ├── DesktopHeader.tsx │ ├── MobileHeader.tsx │ ├── ExamplesDropdown.tsx │ ├── CodeRunner.tsx │ ├── ShareButton.tsx │ ├── LanguageSelector.tsx │ ├── EditorWrapper.tsx │ └── Playground.tsx ├── lib │ ├── utils.ts │ ├── file.ts │ ├── i18n.ts │ ├── events.ts │ ├── language-configuration.json │ └── monaco.ts ├── service │ ├── share.test.ts │ ├── run.ts │ └── share.ts ├── hooks │ └── useLanguage.ts ├── locales │ ├── zh │ │ ├── messages.mjs │ │ └── messages.po │ └── en │ │ ├── messages.mjs │ │ └── messages.po ├── middleware.ts ├── umami.d.ts ├── const.ts └── antlr │ └── CangjieLexer.g4 ├── server ├── .gitignore ├── README.md ├── utils.go ├── cjplay.service ├── cjpm.toml ├── msg.go ├── cmd │ ├── agent │ │ ├── cjc.go │ │ ├── utils.go │ │ └── main.go │ └── server │ │ ├── main.go │ │ ├── handler.go │ │ ├── client.go │ │ └── websocket.go ├── Dockerfile └── go.mod ├── .github ├── dependabot.yml └── workflows │ └── test.yml ├── postcss.config.mjs ├── vitest.config.mts ├── lingui.config.ts ├── components.json ├── README.md ├── .gitignore ├── eslint.config.mjs ├── tsconfig.json ├── next.config.ts ├── package.json └── LICENSE /public/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zxilly/playground-cj/HEAD/public/icon.png -------------------------------------------------------------------------------- /src/app/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zxilly/playground-cj/HEAD/src/app/favicon.ico -------------------------------------------------------------------------------- /src/cangjie.d.ts: -------------------------------------------------------------------------------- 1 | declare module '*.cj' { 2 | const value: any 3 | export default value 4 | } 5 | -------------------------------------------------------------------------------- /src/app/fonts/JetBrainsMono.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zxilly/playground-cj/HEAD/src/app/fonts/JetBrainsMono.woff2 -------------------------------------------------------------------------------- /server/.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | *.iml 3 | out 4 | gen 5 | *.exe 6 | .vscode 7 | 8 | *.tar.gz 9 | *.zip 10 | 11 | server -------------------------------------------------------------------------------- /src/app/fonts/HarmonyOS_Sans.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zxilly/playground-cj/HEAD/src/app/fonts/HarmonyOS_Sans.woff2 -------------------------------------------------------------------------------- /src/examples/hello-world.cj: -------------------------------------------------------------------------------- 1 | package playground 2 | 3 | main(): Int64 { 4 | println("你好,仓颉!") 5 | return 0 6 | } 7 | -------------------------------------------------------------------------------- /src/examples/hello-world.en.cj: -------------------------------------------------------------------------------- 1 | package playground 2 | 3 | main(): Int64 { 4 | println("Hello, Cangjie!") 5 | return 0 6 | } -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: github-actions 4 | directory: / 5 | schedule: 6 | interval: weekly 7 | -------------------------------------------------------------------------------- /postcss.config.mjs: -------------------------------------------------------------------------------- 1 | /** @type {import('postcss-load-config').Config} */ 2 | const config = { 3 | plugins: ['@tailwindcss/postcss'], 4 | } 5 | 6 | export default config 7 | -------------------------------------------------------------------------------- /server/README.md: -------------------------------------------------------------------------------- 1 | # 安装 2 | 3 | ```bash 4 | useradd --groups docker --shell /usr/bin/bash --create-home --home /opt/cjplay cjplay 5 | vi /etc/systemd/system/cjplay.service 6 | ``` 7 | -------------------------------------------------------------------------------- /src/components/EnglishWrapper.tsx: -------------------------------------------------------------------------------- 1 | 'use client' 2 | 3 | import Playground from '@/components/Playground' 4 | 5 | interface WrapperProps { 6 | defaultCode?: string 7 | } 8 | 9 | export default function EnglishWrapper({ defaultCode }: WrapperProps) { 10 | return 11 | } 12 | -------------------------------------------------------------------------------- /vitest.config.mts: -------------------------------------------------------------------------------- 1 | import react from '@vitejs/plugin-react' 2 | import { defineConfig } from 'vitest/config' 3 | 4 | export default defineConfig({ 5 | plugins: [react()], 6 | test: { 7 | environment: 'jsdom', 8 | }, 9 | resolve: { 10 | alias: { 11 | '@': '/src', 12 | }, 13 | }, 14 | }) 15 | -------------------------------------------------------------------------------- /server/utils.go: -------------------------------------------------------------------------------- 1 | package server 2 | 3 | import "math/rand" 4 | 5 | func RandomString(n int) string { 6 | var letters = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ") 7 | b := make([]rune, n) 8 | for i := range b { 9 | b[i] = letters[rand.Intn(len(letters))] 10 | } 11 | return string(b) 12 | } 13 | -------------------------------------------------------------------------------- /src/examples/cffi.cj: -------------------------------------------------------------------------------- 1 | package playground 2 | 3 | // 调用 C 标准库中的 rand 和 printf 函数 4 | foreign func rand(): Int32 5 | foreign func printf(fmt: CString, ...): Int32 6 | 7 | unsafe main() { 8 | let text = LibC.mallocCString("random number: %d\n") 9 | for (_ in 0..5) { 10 | printf(text, rand()) 11 | } 12 | LibC.free(text) 13 | } 14 | -------------------------------------------------------------------------------- /src/lib/utils.ts: -------------------------------------------------------------------------------- 1 | import { clsx } from 'clsx' 2 | import type { ClassValue } from 'clsx' 3 | import { twMerge } from 'tailwind-merge' 4 | 5 | export function cn(...inputs: ClassValue[]) { 6 | return twMerge(clsx(inputs)) 7 | } 8 | 9 | export function isDarkMode() { 10 | return window.matchMedia('(prefers-color-scheme: dark)').matches 11 | } 12 | -------------------------------------------------------------------------------- /src/examples/cffi.en.cj: -------------------------------------------------------------------------------- 1 | package playground 2 | 3 | // Call C standard library rand and printf functions 4 | foreign func rand(): Int32 5 | foreign func printf(fmt: CString, ...): Int32 6 | 7 | unsafe main() { 8 | let text = LibC.mallocCString("random number: %d\n") 9 | for (_ in 0..5) { 10 | printf(text, rand()) 11 | } 12 | LibC.free(text) 13 | } -------------------------------------------------------------------------------- /src/components/ChineseWrapper.tsx: -------------------------------------------------------------------------------- 1 | 'use client' 2 | 3 | import '@codingame/monaco-vscode-language-pack-zh-hans' 4 | import Playground from '@/components/Playground' 5 | 6 | interface WrapperProps { 7 | defaultCode?: string 8 | } 9 | 10 | export default function ChineseWrapper({ defaultCode }: WrapperProps) { 11 | return 12 | } 13 | -------------------------------------------------------------------------------- /server/cjplay.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=Cangjie Playground 3 | After=network.target 4 | 5 | [Service] 6 | Type=simple 7 | User=cjplay 8 | WorkingDirectory=/opt/cjplay 9 | ExecStart=/opt/cjplay/server 127.0.0.1:1313 10 | Restart=on-failure 11 | StandardOutput=file:/opt/cjplay/cjplay.log 12 | StandardError=file:/opt/cjplay/cjplay.log 13 | 14 | [Install] 15 | WantedBy=multi-user.target -------------------------------------------------------------------------------- /src/examples/lambda.en.cj: -------------------------------------------------------------------------------- 1 | package playground 2 | 3 | func iter(n: Int64, x0: Float64, f: (Float64) -> Float64) { 4 | var x = x0 5 | for (_ in 0..n) { 6 | print("${x}, ") 7 | x = f(x) 8 | } 9 | println("${x}") 10 | } 11 | 12 | main() { 13 | iter(5, 0.8, { x: Float64 => 1.0 / (1.0 - x)}) 14 | iter(10, 0.8, { x: Float64 => 15 | 4.0 * x * (1.0 - x) 16 | }) 17 | } -------------------------------------------------------------------------------- /src/examples/lambda.cj: -------------------------------------------------------------------------------- 1 | package playground 2 | 3 | func iter(n: Int64, x0: Float64, f: (Float64) -> Float64) { 4 | var x = x0 5 | for (_ in 0..n) { 6 | print("${x}, ") 7 | x = f(x) 8 | } 9 | println("${x}") 10 | } 11 | 12 | main() { 13 | iter(5, 0.8, { x: Float64 => 1.0 / (1.0 - x)}) 14 | iter(10, 0.8, { x: Float64 => 15 | 4.0 * x * (1.0 - x) 16 | }) 17 | } 18 | -------------------------------------------------------------------------------- /src/app/font.tsx: -------------------------------------------------------------------------------- 1 | import localFont from 'next/font/local' 2 | 3 | export const harmonyFont = localFont({ 4 | src: './fonts/HarmonyOS_Sans.woff2', 5 | preload: true, 6 | }) 7 | 8 | export const jetbrainsFont = localFont({ 9 | src: './fonts/JetBrainsMono.woff2', 10 | preload: true, 11 | }) 12 | 13 | export const fontFamily = `${jetbrainsFont.style.fontFamily}, ${harmonyFont.style.fontFamily}, sans-serif` 14 | -------------------------------------------------------------------------------- /src/examples/extend.cj: -------------------------------------------------------------------------------- 1 | package playground 2 | 3 | extend String { 4 | operator func >>(n: Int64): String { 5 | if (n <= 0) { 6 | return this.clone() 7 | } 8 | let size = this.size 9 | let offset = size - n % size 10 | this[offset..size] + this[0..offset] 11 | } 12 | } 13 | 14 | main() { 15 | let text = "Cangjie2024" 16 | println(text >> 2 >> 2) 17 | } 18 | -------------------------------------------------------------------------------- /src/examples/extend.en.cj: -------------------------------------------------------------------------------- 1 | package playground 2 | 3 | extend String { 4 | operator func >>(n: Int64): String { 5 | if (n <= 0) { 6 | return this.clone() 7 | } 8 | let size = this.size 9 | let offset = size - n % size 10 | this[offset..size] + this[0..offset] 11 | } 12 | } 13 | 14 | main() { 15 | let text = "Cangjie2024" 16 | println(text >> 2 >> 2) 17 | } -------------------------------------------------------------------------------- /lingui.config.ts: -------------------------------------------------------------------------------- 1 | import type { LinguiConfig } from '@lingui/conf' 2 | 3 | const config: LinguiConfig = { 4 | locales: ['zh', 'en'], 5 | sourceLocale: 'zh', 6 | catalogs: [ 7 | { 8 | path: '/src/locales/{locale}/messages', 9 | include: ['src'], 10 | exclude: ['**/node_modules/**'], 11 | }, 12 | ], 13 | format: 'po', 14 | compileNamespace: 'es', 15 | } 16 | 17 | export default config 18 | -------------------------------------------------------------------------------- /server/cjpm.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | cjc-version = "0.60.5" 3 | name = "playground" 4 | description = "nothing here" 5 | version = "1.0.0" 6 | target-dir = "" 7 | src-dir = "" 8 | output-type = "executable" 9 | compile-option = "-ldl" 10 | override-compile-option = "" 11 | link-option = "" 12 | package-configuration = {} 13 | 14 | [target.x86_64-unknown-linux-gnu.bin-dependencies] 15 | path-option = [ "/linux_x86_64_llvm/dynamic/stdx" ] 16 | -------------------------------------------------------------------------------- /src/examples/function-btree.en.cj: -------------------------------------------------------------------------------- 1 | package playground 2 | 3 | func node(value: Rune, 4 | left!: () -> Unit = {=>}, right!: () -> Unit = {=>}) { 5 | return { => 6 | left() 7 | print(value) 8 | right() 9 | } 10 | } 11 | 12 | main() { 13 | let tree = node(r'A', 14 | left: node(r'B', left: node(r'C', right: node(r'D'))), 15 | right: node(r'E', left: node(r'F'), right: node(r'G'))) 16 | tree() 17 | } -------------------------------------------------------------------------------- /src/examples/function-btree.cj: -------------------------------------------------------------------------------- 1 | package playground 2 | 3 | func node(value: Rune, 4 | left!: () -> Unit = {=>}, right!: () -> Unit = {=>}) { 5 | return { => 6 | left() 7 | print(value) 8 | right() 9 | } 10 | } 11 | 12 | main() { 13 | let tree = node(r'A', 14 | left: node(r'B', left: node(r'C', right: node(r'D'))), 15 | right: node(r'E', left: node(r'F'), right: node(r'G'))) 16 | tree() 17 | } 18 | -------------------------------------------------------------------------------- /src/components/Wrapper.tsx: -------------------------------------------------------------------------------- 1 | 'use client' 2 | 3 | import dynamic from 'next/dynamic' 4 | 5 | const ChineseWrapper = dynamic(() => import('@/components/ChineseWrapper'), { ssr: false }) 6 | const EnglishWrapper = dynamic(() => import('@/components/EnglishWrapper'), { ssr: false }) 7 | 8 | export default function Wrapper({ lang, defaultCode }: { lang: string, defaultCode?: string }) { 9 | const WrapperComponent = lang === 'zh' ? ChineseWrapper : EnglishWrapper 10 | return 11 | } 12 | -------------------------------------------------------------------------------- /components.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://ui.shadcn.com/schema.json", 3 | "style": "new-york", 4 | "rsc": true, 5 | "tsx": true, 6 | "tailwind": { 7 | "config": "", 8 | "css": "src/app/globals.css", 9 | "baseColor": "gray", 10 | "cssVariables": true, 11 | "prefix": "" 12 | }, 13 | "aliases": { 14 | "components": "@/components", 15 | "utils": "@/lib/utils", 16 | "ui": "@/components/ui", 17 | "lib": "@/lib", 18 | "hooks": "@/hooks" 19 | }, 20 | "iconLibrary": "lucide" 21 | } 22 | -------------------------------------------------------------------------------- /server/msg.go: -------------------------------------------------------------------------------- 1 | package server 2 | 3 | type ForwardMessage struct { 4 | Data []byte `json:"data"` 5 | } 6 | 7 | type FormatMessage struct { 8 | Formatted string `json:"formatted"` 9 | FormatterOutput string `json:"formatter_output"` 10 | FormatterCode int `json:"formatter_code"` 11 | } 12 | 13 | type RunMessage struct { 14 | CompilerOutput string `json:"compiler_output"` 15 | CompilerCode int `json:"compiler_code"` 16 | BinOutput string `json:"bin_output"` 17 | BinCode int `json:"bin_code"` 18 | } 19 | -------------------------------------------------------------------------------- /src/lib/file.ts: -------------------------------------------------------------------------------- 1 | export function saveAsFile(content: string, fileName: string = 'main.cj'): void { 2 | const blob = new Blob([content], { type: 'text/plain;charset=utf-8' }) 3 | 4 | const url = URL.createObjectURL(blob) 5 | 6 | const a = document.createElement('a') 7 | a.href = url 8 | a.download = fileName 9 | 10 | const clickEvent = new MouseEvent('click', { 11 | view: window, 12 | bubbles: true, 13 | cancelable: true, 14 | }) 15 | a.dispatchEvent(clickEvent) 16 | 17 | URL.revokeObjectURL(url) 18 | } 19 | -------------------------------------------------------------------------------- /src/examples/struct-btree.cj: -------------------------------------------------------------------------------- 1 | package playground 2 | 3 | struct Node { 4 | public Node(var value: Rune, 5 | let left!: ?Node = None, 6 | let right!: ?Node = None) {} 7 | 8 | public func traverse(): Unit { 9 | left?.traverse() 10 | print(value) 11 | right?.traverse() 12 | } 13 | } 14 | 15 | main() { 16 | var tree = Node(r'A', 17 | left: Node(r'B', left: Node(r'C', right: Node(r'D'))), 18 | right: Node(r'E', left: Node(r'F'), right: Node(r'G'))) 19 | tree.traverse() 20 | } 21 | -------------------------------------------------------------------------------- /src/examples/struct-btree.en.cj: -------------------------------------------------------------------------------- 1 | package playground 2 | 3 | struct Node { 4 | public Node(var value: Rune, 5 | let left!: ?Node = None, 6 | let right!: ?Node = None) {} 7 | 8 | public func traverse(): Unit { 9 | left?.traverse() 10 | print(value) 11 | right?.traverse() 12 | } 13 | } 14 | 15 | main() { 16 | var tree = Node(r'A', 17 | left: Node(r'B', left: Node(r'C', right: Node(r'D'))), 18 | right: Node(r'E', left: Node(r'F'), right: Node(r'G'))) 19 | tree.traverse() 20 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 仓颉 Playground 2 | 3 | ![](https://img.shields.io/badge/next.js-000000) 4 | [![Runs All Unit tests](https://github.com/Zxilly/playground-cj/actions/workflows/test.yml/badge.svg)](https://github.com/Zxilly/playground-cj/actions/workflows/test.yml) 5 | 6 | ## Demo 7 | 8 | [https://playground.cj.zxilly.dev](https://playground.cj.zxilly.dev) 9 | 10 | ## 开发 11 | 12 | ```bash 13 | pnpm dev 14 | ``` 15 | 16 | ## TODO 17 | 18 | - [x] 分享链接 19 | - [x] 格式化 20 | - [x] 代码高亮 21 | - [ ] 允许导入流行第三方包 22 | - [x] 彩色输出支持 23 | - [ ] 用户友好的错误处理 24 | - [ ] 用户友好的运行状态显示 25 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | .yarn/install-state.gz 8 | 9 | # testing 10 | /coverage 11 | 12 | # next.js 13 | /.next/ 14 | /out/ 15 | 16 | # production 17 | /build 18 | 19 | # misc 20 | .DS_Store 21 | *.pem 22 | 23 | # debug 24 | npm-debug.log* 25 | yarn-debug.log* 26 | yarn-error.log* 27 | 28 | # local env files 29 | .env*.local 30 | 31 | # vercel 32 | .vercel 33 | 34 | # typescript 35 | *.tsbuildinfo 36 | next-env.d.ts 37 | 38 | .idea/ 39 | .vscode/ 40 | 41 | .env 42 | .claude -------------------------------------------------------------------------------- /src/examples/for.cj: -------------------------------------------------------------------------------- 1 | package playground 2 | 3 | main() { 4 | let metaArray = [r'甲', r'乙', r'丙', r'丁', r'戊', 5 | r'己', r'庚', r'辛', r'壬', r'癸'] 6 | let noumenonArray = [r'寅', r'卯', r'辰', r'巳', r'午', r'未', 7 | r'申', r'酉', r'戌', r'亥', r'子', r'丑'] 8 | let year = 2024 9 | // 年份对应的天干索引 10 | let metaOfYear = ((year % 10) + 10 - 4) % 10 11 | // 此年首月对应的天干索引 12 | var index = (2 * metaOfYear + 3) % 10 - 1 13 | println("农历 2024 年各月干支:") 14 | for (noumenon in noumenonArray) { 15 | print("${metaArray[index]}${noumenon} ") 16 | index = (index + 1) % 10 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/service/share.test.ts: -------------------------------------------------------------------------------- 1 | import { generateDataShareUrl, loadDataShareCode } from '@/service/share' 2 | import { expect, it } from 'vitest' 3 | 4 | it.each([ 5 | ['print(\'Hello, world!\')', 'http://localhost:3000/#data=A4JwlgdgLgFA5ACQKYBsUHsA0ACA7ukFAEwEI4BKIA'], 6 | ['print(\'中文代码\')', 'http://localhost:3000/#data=A4JwlgdgLgFA5IWjlDhpoY7lCAHnAlEA'], 7 | ])('test share serialization with code: %s', async (code, expectedUrl) => { 8 | const url = generateDataShareUrl(code) 9 | 10 | expect(url).toBe(expectedUrl) 11 | 12 | window.location.href = url 13 | const newCode = loadDataShareCode() 14 | expect(newCode).toBe(code) 15 | }) 16 | -------------------------------------------------------------------------------- /eslint.config.mjs: -------------------------------------------------------------------------------- 1 | import antfu from '@antfu/eslint-config' 2 | import pluginLingui from 'eslint-plugin-lingui' 3 | 4 | export default antfu({ 5 | react: true, 6 | rules: { 7 | 'react-dom/no-dangerously-set-innerhtml': 'off', 8 | 'no-template-curly-in-string': 'off', 9 | 'prefer-promise-reject-errors': 'off', 10 | 'node/prefer-global/process': 'off', 11 | 'antfu/no-top-level-await': 'off', 12 | 'perfectionist/sort-imports': 'off', 13 | }, 14 | }, { 15 | ignores: [ 16 | 'src/components/ui/*.*', 17 | 'tailwind.config.ts', 18 | '**/*.json', 19 | '**/*.mjs', 20 | ], 21 | }, pluginLingui.configs['flat/recommended']) 22 | -------------------------------------------------------------------------------- /src/app/[lang]/page.tsx: -------------------------------------------------------------------------------- 1 | import Wrapper from '@/components/Wrapper' 2 | import { getShareCode } from '@/service/share' 3 | 4 | interface PageProps { 5 | params: Promise<{ lang: string }> 6 | searchParams: Promise<{ [_: string]: string | string[] | undefined }> 7 | } 8 | 9 | export default async function LangPage({ params, searchParams }: PageProps) { 10 | const { lang } = await params 11 | const sp = await searchParams 12 | 13 | let code: string | undefined 14 | if (sp.hash) { 15 | code = await getShareCode(sp.hash as string) 16 | } 17 | return ( 18 |
19 | 20 |
21 | ) 22 | } 23 | -------------------------------------------------------------------------------- /server/cmd/agent/cjc.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | type Position struct { 4 | Line int `json:"line"` 5 | Column int `json:"column"` 6 | } 7 | 8 | type Import struct { 9 | File string `json:"file"` 10 | Begin Position `json:"begin"` 11 | End Position `json:"end"` 12 | } 13 | 14 | type Dependency struct { 15 | Package string `json:"package"` 16 | IsStd bool `json:"isStd"` 17 | Imports []Import `json:"imports"` 18 | } 19 | 20 | type CjcDepRoot struct { 21 | Package string `json:"package"` 22 | IsMacro bool `json:"isMacro"` 23 | AccessLevel string `json:"accessLevel"` 24 | Dependencies []Dependency `json:"dependencies"` 25 | } 26 | -------------------------------------------------------------------------------- /src/components/ui/sonner.tsx: -------------------------------------------------------------------------------- 1 | "use client" 2 | 3 | import { useTheme } from "next-themes" 4 | import { Toaster as Sonner, ToasterProps } from "sonner" 5 | 6 | const Toaster = ({ ...props }: ToasterProps) => { 7 | const { theme = "system" } = useTheme() 8 | 9 | return ( 10 | 22 | ) 23 | } 24 | 25 | export { Toaster } 26 | -------------------------------------------------------------------------------- /src/components/ui/label.tsx: -------------------------------------------------------------------------------- 1 | "use client" 2 | 3 | import * as React from "react" 4 | import * as LabelPrimitive from "@radix-ui/react-label" 5 | 6 | import { cn } from "@/lib/utils" 7 | 8 | function Label({ 9 | className, 10 | ...props 11 | }: React.ComponentProps) { 12 | return ( 13 | 21 | ) 22 | } 23 | 24 | export { Label } 25 | -------------------------------------------------------------------------------- /src/examples/for.en.cj: -------------------------------------------------------------------------------- 1 | package playground 2 | 3 | main() { 4 | let metaArray = [r'A', r'B', r'C', r'D', r'E', 5 | r'F', r'G', r'H', r'I', r'J'] 6 | let noumenonArray = [r'K', r'L', r'M', r'N', r'O', r'P', 7 | r'Q', r'R', r'S', r'T', r'U', r'V'] 8 | let year = 2024 9 | // Index of the heavenly stem corresponding to the year 10 | let metaOfYear = ((year % 10) + 10 - 4) % 10 11 | // Index of the heavenly stem corresponding to the first month of this year 12 | var index = (2 * metaOfYear + 3) % 10 - 1 13 | println("Lunar 2024 year month stems and branches:") 14 | for (noumenon in noumenonArray) { 15 | print("${metaArray[index]}${noumenon} ") 16 | index = (index + 1) % 10 17 | } 18 | } -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: Runs All Unit tests 2 | 3 | on: 4 | push: 5 | branches: [master] 6 | pull_request: 7 | branches: [master] 8 | 9 | jobs: 10 | build: 11 | runs-on: ubuntu-latest 12 | 13 | steps: 14 | - uses: actions/checkout@v6 15 | 16 | - uses: pnpm/action-setup@v4 17 | with: 18 | standalone: 'true' 19 | 20 | - name: Use Node.js 21 | uses: actions/setup-node@v6 22 | with: 23 | node-version: 20.x 24 | cache: pnpm 25 | 26 | - name: Install dependencies 27 | run: pnpm install --frozen-lockfile 28 | 29 | - name: Run Lint 30 | run: pnpm run lint 31 | 32 | - name: Execute Unit tests 33 | run: pnpm run test 34 | -------------------------------------------------------------------------------- /src/examples/option-parse.cj: -------------------------------------------------------------------------------- 1 | package playground 2 | 3 | func parseInt(text: String): Option { 4 | if (text.isEmpty() || text == "-") { 5 | return None 6 | } 7 | var sign = if (text[0] == 45u8) { 1 } else { 0 } 8 | var sum = 0 9 | for (i in sign..text.size) { 10 | if (text[i] > 57u8 || text[i] < 48u8) { 11 | return None 12 | } 13 | let digit = Int64(text[i] - 48u8) 14 | sum = 10 * sum + digit 15 | } 16 | return if (sign == 1) { -sum } else { sum } 17 | } 18 | 19 | main() { 20 | let number = parseInt("-123456") 21 | println(number.getOrThrow()) 22 | let result = parseInt("123-456") 23 | if (result.isNone()) { 24 | println("parse failed") 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/examples/option-parse.en.cj: -------------------------------------------------------------------------------- 1 | package playground 2 | 3 | func parseInt(text: String): Option { 4 | if (text.isEmpty() || text == "-") { 5 | return None 6 | } 7 | var sign = if (text[0] == 45u8) { 1 } else { 0 } 8 | var sum = 0 9 | for (i in sign..text.size) { 10 | if (text[i] > 57u8 || text[i] < 48u8) { 11 | return None 12 | } 13 | let digit = Int64(text[i] - 48u8) 14 | sum = 10 * sum + digit 15 | } 16 | return if (sign == 1) { -sum } else { sum } 17 | } 18 | 19 | main() { 20 | let number = parseInt("-123456") 21 | println(number.getOrThrow()) 22 | let result = parseInt("123-456") 23 | if (result.isNone()) { 24 | println("parse failed") 25 | } 26 | } -------------------------------------------------------------------------------- /src/examples/prop-btree.cj: -------------------------------------------------------------------------------- 1 | package playground 2 | 3 | class Node { 4 | private var value: Int64 = 0 5 | public Node(private var name: Rune, 6 | private let left!: ?Node = None, 7 | private let right!: ?Node = None) {} 8 | 9 | public mut prop param: Int64 { 10 | set(number) { 11 | value = number 12 | println("${name}: ${value}") 13 | left?.param = number / 2 14 | right?.param = number / 2 15 | } 16 | get() { value } 17 | } 18 | } 19 | 20 | main() { 21 | var tree = Node(r'A', 22 | left: Node(r'B', left: Node(r'C', right: Node(r'D'))), 23 | right: Node(r'E', left: Node(r'F'), right: Node(r'G'))) 24 | println(tree.param) 25 | tree.param = 128 26 | } 27 | -------------------------------------------------------------------------------- /src/examples/prop-btree.en.cj: -------------------------------------------------------------------------------- 1 | package playground 2 | 3 | class Node { 4 | private var value: Int64 = 0 5 | public Node(private var name: Rune, 6 | private let left!: ?Node = None, 7 | private let right!: ?Node = None) {} 8 | 9 | public mut prop param: Int64 { 10 | set(number) { 11 | value = number 12 | println("${name}: ${value}") 13 | left?.param = number / 2 14 | right?.param = number / 2 15 | } 16 | get() { value } 17 | } 18 | } 19 | 20 | main() { 21 | var tree = Node(r'A', 22 | left: Node(r'B', left: Node(r'C', right: Node(r'D'))), 23 | right: Node(r'E', left: Node(r'F'), right: Node(r'G'))) 24 | println(tree.param) 25 | tree.param = 128 26 | } -------------------------------------------------------------------------------- /src/components/LabelContainer.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | interface EmbeddedLabelContainerProps { 4 | title: string 5 | content: React.ReactNode 6 | className?: string 7 | } 8 | 9 | const LabelContainer: React.FC = ({ 10 | title, 11 | content, 12 | className = '', 13 | }) => { 14 | return ( 15 |
16 |
17 | {title} 18 |
19 |
20 | {content} 21 |
22 |
23 | ) 24 | } 25 | 26 | export default LabelContainer 27 | -------------------------------------------------------------------------------- /src/components/TrackingScript.tsx: -------------------------------------------------------------------------------- 1 | import Script from 'next/script' 2 | import React, { Fragment } from 'react' 3 | 4 | function TrackingScript() { 5 | return ( 6 | <> 7 | {process.env.NODE_ENV === 'production' && ( 8 | 9 |