@tishlang/tish-desktop-ui-theme 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,13 @@
1
+ Pay It Forward License (PIF)
2
+
3
+ Copyright (c) 2026-present The Tish Project Authors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
6
+
7
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
8
+
9
+ This license includes a perpetual, worldwide, non-exclusive, royalty-free, irrevocable (except as stated below) grant for patent claims necessarily infringed by the use of the Software or any contributions submitted to it; terminating automatically when an entity initiates patent litigation (including a cross-claim or counterclaim) alleging that the Software or any portion thereof infringes a patent.
10
+
11
+ The software is provided "as is," without warranty of any kind, express or implied, including but not limited to the warranties of merchantability, fitness for a particular purpose, and non-infringement. In no event shall the authors or copyright holders be liable for any claim, damages, or other liability, whether in an action of contract, tort, or otherwise, arising from, out of, or in connection with the software or the use or other dealings in the software.
12
+
13
+ Recipients of the Software should pay it forward by contributing to the open-source community through actions including but not limited to providing code, documentation, tutorials, guides, support, feedback, or promoting open-source projects through advocacy or related efforts that advance innovation and community growth.
package/README.md ADDED
@@ -0,0 +1,26 @@
1
+ # `@tishlang/tish-desktop-ui-theme`
2
+
3
+ Shared design tokens + small **lattish** primitives for Tish Desktop examples and apps.
4
+
5
+ ## Contents
6
+
7
+ | Path | Role |
8
+ |------|------|
9
+ | `src/theme.css` | shadcn-style CSS variables (`--background`, `--primary`, …); dark default |
10
+ | `src/theme-utilities.css` | Maps `bg-background` / `text-muted-foreground` / … → `var(--*)` (required — tish-tailwind has no `@theme`) |
11
+ | `src/fonts.css` | Distinct UI font stack |
12
+ | `src/ui/*.tish` | `Button`, `Card`, `Section`, `Input`, `Badge`, `Separator`, `ScrollArea`, `Toolbar` |
13
+ | `src/index.tish` | Re-exports |
14
+
15
+ ## Usage
16
+
17
+ 1. Depend on the package (`file:` or published).
18
+ 2. Alias `@tishlang/tish-desktop-ui-theme` in Vite to `src/index.tish`.
19
+ 3. In `build-css.tish`, concatenate `fonts.css` + `theme.css` + `theme-utilities.css` with the tish-tailwind emit, and include theme `src/ui/*.tish` in `sourceFiles` so scanned utilities are emitted.
20
+ 4. Import primitives in UI Tish:
21
+
22
+ ```tish
23
+ import { Button, Section } from "@tishlang/tish-desktop-ui-theme"
24
+ ```
25
+
26
+ `examples/basic` intentionally stays unthemed. Prefer this package for `file-browser`, `native-chrome`, and new apps.
package/package.json ADDED
@@ -0,0 +1,37 @@
1
+ {
2
+ "name": "@tishlang/tish-desktop-ui-theme",
3
+ "version": "1.0.0",
4
+ "description": "Shared shadcn-like theme tokens and lattish UI primitives for Tish Desktop",
5
+ "license": "PIF",
6
+ "type": "module",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "git+https://github.com/tishlang/tish-desktop.git",
10
+ "directory": "packages/ui-theme"
11
+ },
12
+ "publishConfig": {
13
+ "access": "public"
14
+ },
15
+ "files": [
16
+ "src",
17
+ "README.md",
18
+ "LICENSE"
19
+ ],
20
+ "tish": {
21
+ "module": "./src/index.tish"
22
+ },
23
+ "exports": {
24
+ ".": {
25
+ "tish": "./src/index.tish"
26
+ },
27
+ "./theme.css": "./src/theme.css",
28
+ "./theme-utilities.css": "./src/theme-utilities.css",
29
+ "./fonts.css": "./src/fonts.css"
30
+ },
31
+ "keywords": [
32
+ "tish",
33
+ "desktop",
34
+ "ui",
35
+ "theme"
36
+ ]
37
+ }
package/src/fonts.css ADDED
@@ -0,0 +1,21 @@
1
+ @import url("https://fonts.googleapis.com/css2?family=DM+Sans:ital,opsz,wght@0,9..40,400;0,9..40,500;0,9..40,600;0,9..40,700;1,9..40,400;1,9..40,500&display=swap");
2
+
3
+ :root {
4
+ --font-sans: "DM Sans", ui-sans-serif, system-ui, sans-serif;
5
+ --font-mono: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
6
+ }
7
+
8
+ body {
9
+ font-family: var(--font-sans);
10
+ font-feature-settings: "ss01" on, "cv11" on;
11
+ -webkit-font-smoothing: antialiased;
12
+ -moz-osx-font-smoothing: grayscale;
13
+ }
14
+
15
+ .font-sans {
16
+ font-family: var(--font-sans);
17
+ }
18
+
19
+ .font-mono {
20
+ font-family: var(--font-mono);
21
+ }
package/src/index.tish ADDED
@@ -0,0 +1,8 @@
1
+ export { Button } from "./ui/Button.tish"
2
+ export { Card, CardHeader, CardBody, CardFooter } from "./ui/Card.tish"
3
+ export { Section } from "./ui/Section.tish"
4
+ export { Input } from "./ui/Input.tish"
5
+ export { Badge } from "./ui/Badge.tish"
6
+ export { Separator } from "./ui/Separator.tish"
7
+ export { ScrollArea } from "./ui/ScrollArea.tish"
8
+ export { Toolbar } from "./ui/Toolbar.tish"
@@ -0,0 +1,51 @@
1
+ /*
2
+ * Map shadcn-like utility class names to CSS variables.
3
+ * Required because tish-tailwind does not emit @theme / bg-background tokens.
4
+ */
5
+
6
+ .bg-background { background-color: hsl(var(--background)); }
7
+ .text-foreground { color: hsl(var(--foreground)); }
8
+
9
+ .bg-card { background-color: hsl(var(--card)); }
10
+ .text-card-foreground { color: hsl(var(--card-foreground)); }
11
+
12
+ .bg-popover { background-color: hsl(var(--popover)); }
13
+ .text-popover-foreground { color: hsl(var(--popover-foreground)); }
14
+
15
+ .bg-primary { background-color: hsl(var(--primary)); }
16
+ .text-primary { color: hsl(var(--primary)); }
17
+ .text-primary-foreground { color: hsl(var(--primary-foreground)); }
18
+
19
+ .bg-secondary { background-color: hsl(var(--secondary)); }
20
+ .text-secondary { color: hsl(var(--secondary)); }
21
+ .text-secondary-foreground { color: hsl(var(--secondary-foreground)); }
22
+
23
+ .bg-muted { background-color: hsl(var(--muted)); }
24
+ .text-muted { color: hsl(var(--muted)); }
25
+ .text-muted-foreground { color: hsl(var(--muted-foreground)); }
26
+
27
+ .bg-accent { background-color: hsl(var(--accent)); }
28
+ .text-accent { color: hsl(var(--accent)); }
29
+ .text-accent-foreground { color: hsl(var(--accent-foreground)); }
30
+
31
+ .bg-destructive { background-color: hsl(var(--destructive)); }
32
+ .text-destructive { color: hsl(var(--destructive)); }
33
+ .text-destructive-foreground { color: hsl(var(--destructive-foreground)); }
34
+
35
+ .border-border { border-color: hsl(var(--border)); }
36
+ .border-input { border-color: hsl(var(--input)); }
37
+ .ring-ring { --tw-ring-color: hsl(var(--ring)); }
38
+ .outline-ring { outline-color: hsl(var(--ring)); }
39
+
40
+ .bg-sidebar { background-color: hsl(var(--sidebar)); }
41
+ .text-sidebar-foreground { color: hsl(var(--sidebar-foreground)); }
42
+ .border-sidebar-border { border-color: hsl(var(--sidebar-border)); }
43
+ .bg-sidebar-accent { background-color: hsl(var(--sidebar-accent)); }
44
+ .text-sidebar-accent-foreground { color: hsl(var(--sidebar-accent-foreground)); }
45
+
46
+ .rounded-sm { border-radius: calc(var(--radius) - 4px); }
47
+ .rounded-md { border-radius: calc(var(--radius) - 2px); }
48
+ .rounded-lg { border-radius: var(--radius); }
49
+ .rounded-xl { border-radius: calc(var(--radius) + 4px); }
50
+
51
+ .ring-offset-background { --tw-ring-offset-color: hsl(var(--background)); }
package/src/theme.css ADDED
@@ -0,0 +1,122 @@
1
+ /*
2
+ * shadcn-style design tokens for Tish Desktop.
3
+ * Dark is the default on :root (zinc/slate neutrals + emerald accent).
4
+ * Opt into light with :root.light or html.light.
5
+ */
6
+
7
+ :root {
8
+ color-scheme: dark;
9
+
10
+ --background: 240 10% 3.9%;
11
+ --foreground: 0 0% 98%;
12
+
13
+ --card: 240 6% 7%;
14
+ --card-foreground: 0 0% 98%;
15
+
16
+ --popover: 240 6% 7%;
17
+ --popover-foreground: 0 0% 98%;
18
+
19
+ --primary: 160 84% 39%;
20
+ --primary-foreground: 0 0% 100%;
21
+
22
+ --secondary: 240 4% 16%;
23
+ --secondary-foreground: 0 0% 98%;
24
+
25
+ --muted: 240 4% 16%;
26
+ --muted-foreground: 240 5% 64.9%;
27
+
28
+ --accent: 240 4% 18%;
29
+ --accent-foreground: 0 0% 98%;
30
+
31
+ --destructive: 0 72% 51%;
32
+ --destructive-foreground: 0 0% 98%;
33
+
34
+ --border: 240 4% 18%;
35
+ --input: 240 4% 18%;
36
+ --ring: 160 84% 39%;
37
+
38
+ --sidebar: 240 8% 5%;
39
+ --sidebar-foreground: 0 0% 98%;
40
+ --sidebar-border: 240 4% 14%;
41
+ --sidebar-accent: 240 4% 14%;
42
+ --sidebar-accent-foreground: 0 0% 98%;
43
+
44
+ --radius: 0.5rem;
45
+ }
46
+
47
+ :root.light,
48
+ html.light {
49
+ color-scheme: light;
50
+
51
+ --background: 0 0% 100%;
52
+ --foreground: 240 10% 3.9%;
53
+
54
+ --card: 0 0% 100%;
55
+ --card-foreground: 240 10% 3.9%;
56
+
57
+ --popover: 0 0% 100%;
58
+ --popover-foreground: 240 10% 3.9%;
59
+
60
+ --primary: 160 84% 39%;
61
+ --primary-foreground: 0 0% 100%;
62
+
63
+ --secondary: 240 5% 96%;
64
+ --secondary-foreground: 240 6% 10%;
65
+
66
+ --muted: 240 5% 96%;
67
+ --muted-foreground: 240 4% 46%;
68
+
69
+ --accent: 240 5% 96%;
70
+ --accent-foreground: 240 6% 10%;
71
+
72
+ --destructive: 0 72% 51%;
73
+ --destructive-foreground: 0 0% 98%;
74
+
75
+ --border: 240 6% 90%;
76
+ --input: 240 6% 90%;
77
+ --ring: 160 84% 39%;
78
+
79
+ --sidebar: 240 5% 96%;
80
+ --sidebar-foreground: 240 10% 3.9%;
81
+ --sidebar-border: 240 6% 90%;
82
+ --sidebar-accent: 240 5% 92%;
83
+ --sidebar-accent-foreground: 240 6% 10%;
84
+ }
85
+
86
+ .dark {
87
+ color-scheme: dark;
88
+
89
+ --background: 240 10% 3.9%;
90
+ --foreground: 0 0% 98%;
91
+ --card: 240 6% 7%;
92
+ --card-foreground: 0 0% 98%;
93
+ --popover: 240 6% 7%;
94
+ --popover-foreground: 0 0% 98%;
95
+ --primary: 160 84% 39%;
96
+ --primary-foreground: 0 0% 100%;
97
+ --secondary: 240 4% 16%;
98
+ --secondary-foreground: 0 0% 98%;
99
+ --muted: 240 4% 16%;
100
+ --muted-foreground: 240 5% 64.9%;
101
+ --accent: 240 4% 18%;
102
+ --accent-foreground: 0 0% 98%;
103
+ --destructive: 0 72% 51%;
104
+ --destructive-foreground: 0 0% 98%;
105
+ --border: 240 4% 18%;
106
+ --input: 240 4% 18%;
107
+ --ring: 160 84% 39%;
108
+ --sidebar: 240 8% 5%;
109
+ --sidebar-foreground: 0 0% 98%;
110
+ --sidebar-border: 240 4% 14%;
111
+ --sidebar-accent: 240 4% 14%;
112
+ --sidebar-accent-foreground: 0 0% 98%;
113
+ }
114
+
115
+ * {
116
+ border-color: hsl(var(--border));
117
+ }
118
+
119
+ body {
120
+ background-color: hsl(var(--background));
121
+ color: hsl(var(--foreground));
122
+ }
@@ -0,0 +1,21 @@
1
+ /** @jsxImportSource lattish */
2
+
3
+ fn cx(base, extra) {
4
+ if (extra === null || extra === undefined || extra === "") return base
5
+ return base + " " + extra
6
+ }
7
+
8
+ fn toneClass(tone) {
9
+ if (tone === "primary") return "bg-primary text-primary-foreground border-transparent"
10
+ if (tone === "destructive") return "bg-destructive text-destructive-foreground border-transparent"
11
+ if (tone === "outline") return "bg-transparent text-foreground border-border"
12
+ if (tone === "accent") return "bg-accent text-accent-foreground border-transparent"
13
+ return "bg-muted text-muted-foreground border-transparent"
14
+ }
15
+
16
+ export fn Badge(props) {
17
+ let tone = props.tone !== null && props.tone !== undefined ? props.tone : "muted"
18
+ return <span class={cx("inline-flex items-center rounded-md border px-2 py-0.5 text-xs font-medium", cx(toneClass(tone), props.class))}>
19
+ {props.children}
20
+ </span>
21
+ }
@@ -0,0 +1,49 @@
1
+ /** @jsxImportSource lattish */
2
+
3
+ fn cx(base, extra) {
4
+ if (extra === null || extra === undefined || extra === "") return base
5
+ return base + " " + extra
6
+ }
7
+
8
+ fn variantClass(variant) {
9
+ if (variant === "secondary") {
10
+ return "bg-secondary text-secondary-foreground border border-border hover:bg-accent"
11
+ }
12
+ if (variant === "outline") {
13
+ return "bg-background text-foreground border border-border hover:bg-accent hover:text-accent-foreground"
14
+ }
15
+ if (variant === "ghost") {
16
+ return "bg-transparent text-foreground hover:bg-accent hover:text-accent-foreground"
17
+ }
18
+ if (variant === "destructive") {
19
+ return "bg-destructive text-destructive-foreground border border-transparent hover:opacity-90"
20
+ }
21
+ if (variant === "link") {
22
+ return "bg-transparent text-primary underline-offset-4 hover:underline px-0"
23
+ }
24
+ return "bg-primary text-primary-foreground border border-transparent hover:opacity-90"
25
+ }
26
+
27
+ fn sizeClass(size) {
28
+ if (size === "sm") return "h-8 px-3 text-xs"
29
+ if (size === "lg") return "h-11 px-5 text-base"
30
+ if (size === "icon") return "h-9 w-9 p-0"
31
+ return "h-9 px-4 text-sm"
32
+ }
33
+
34
+ export fn Button(props) {
35
+ let variant = props.variant !== null && props.variant !== undefined ? props.variant : "default"
36
+ let size = props.size !== null && props.size !== undefined ? props.size : "md"
37
+ let className = cx(
38
+ "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-lg font-medium transition-opacity disabled:pointer-events-none disabled:opacity-50",
39
+ cx(variantClass(variant), cx(sizeClass(size), props.class))
40
+ )
41
+ return <button
42
+ type={props.type !== null && props.type !== undefined ? props.type : "button"}
43
+ class={className}
44
+ disabled={props.disabled}
45
+ onClick={props.onClick}
46
+ >
47
+ {props.children}
48
+ </button>
49
+ }
@@ -0,0 +1,30 @@
1
+ /** @jsxImportSource lattish */
2
+
3
+ fn cx(base, extra) {
4
+ if (extra === null || extra === undefined || extra === "") return base
5
+ return base + " " + extra
6
+ }
7
+
8
+ export fn Card(props) {
9
+ return <div class={cx("rounded-lg border border-border bg-card text-card-foreground", props.class)}>
10
+ {props.children}
11
+ </div>
12
+ }
13
+
14
+ export fn CardHeader(props) {
15
+ return <div class={cx("flex flex-col gap-1.5 p-4 border-b border-border", props.class)}>
16
+ {props.children}
17
+ </div>
18
+ }
19
+
20
+ export fn CardBody(props) {
21
+ return <div class={cx("p-4", props.class)}>
22
+ {props.children}
23
+ </div>
24
+ }
25
+
26
+ export fn CardFooter(props) {
27
+ return <div class={cx("flex items-center gap-2 p-4 border-t border-border text-muted-foreground", props.class)}>
28
+ {props.children}
29
+ </div>
30
+ }
@@ -0,0 +1,23 @@
1
+ /** @jsxImportSource lattish */
2
+
3
+ fn cx(base, extra) {
4
+ if (extra === null || extra === undefined || extra === "") return base
5
+ return base + " " + extra
6
+ }
7
+
8
+ export fn Input(props) {
9
+ let className = cx(
10
+ "flex h-9 w-full rounded-lg border border-input bg-background px-3 py-1 text-sm text-foreground placeholder:text-muted-foreground outline-none focus-visible:ring-2 ring-ring disabled:cursor-not-allowed disabled:opacity-50",
11
+ props.class
12
+ )
13
+ return <input
14
+ type={props.type !== null && props.type !== undefined ? props.type : "text"}
15
+ class={className}
16
+ value={props.value}
17
+ placeholder={props.placeholder}
18
+ disabled={props.disabled}
19
+ onInput={props.onInput}
20
+ onChange={props.onChange}
21
+ onKeyDown={props.onKeyDown}
22
+ />
23
+ }
@@ -0,0 +1,12 @@
1
+ /** @jsxImportSource lattish */
2
+
3
+ fn cx(base, extra) {
4
+ if (extra === null || extra === undefined || extra === "") return base
5
+ return base + " " + extra
6
+ }
7
+
8
+ export fn ScrollArea(props) {
9
+ return <div class={cx("relative overflow-auto", props.class)}>
10
+ {props.children}
11
+ </div>
12
+ }
@@ -0,0 +1,16 @@
1
+ /** @jsxImportSource lattish */
2
+
3
+ fn cx(base, extra) {
4
+ if (extra === null || extra === undefined || extra === "") return base
5
+ return base + " " + extra
6
+ }
7
+
8
+ export fn Section(props) {
9
+ let hint = props.hint
10
+ let hasHint = hint !== null && hint !== undefined && hint !== ""
11
+ return <section class={cx("mb-8", props.class)}>
12
+ {props.title !== null && props.title !== undefined ? <h2 class="text-lg font-semibold mb-1 text-foreground">{props.title}</h2> : null}
13
+ {hasHint ? <p class="text-sm text-muted-foreground mb-3">{hint}</p> : null}
14
+ <div class="flex flex-wrap gap-2">{props.children}</div>
15
+ </section>
16
+ }
@@ -0,0 +1,14 @@
1
+ /** @jsxImportSource lattish */
2
+
3
+ fn cx(base, extra) {
4
+ if (extra === null || extra === undefined || extra === "") return base
5
+ return base + " " + extra
6
+ }
7
+
8
+ export fn Separator(props) {
9
+ let vertical = props.orientation === "vertical"
10
+ let className = vertical
11
+ ? "shrink-0 bg-border w-px h-full"
12
+ : "shrink-0 bg-border h-px w-full"
13
+ return <div role="separator" class={cx(className, props.class)} />
14
+ }
@@ -0,0 +1,18 @@
1
+ /** @jsxImportSource lattish */
2
+
3
+ fn cx(base, extra) {
4
+ if (extra === null || extra === undefined || extra === "") return base
5
+ return base + " " + extra
6
+ }
7
+
8
+ export fn Toolbar(props) {
9
+ return <div
10
+ class={cx(
11
+ "flex items-center gap-2 h-12 px-4 border-b border-border bg-background select-none",
12
+ props.class
13
+ )}
14
+ onMouseDown={props.onMouseDown}
15
+ >
16
+ {props.children}
17
+ </div>
18
+ }