mirror of
https://github.com/anomalyco/opencode.git
synced 2026-07-23 11:16:13 +00:00
Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c251ead206 |
@@ -413,6 +413,10 @@
|
||||
"drizzle-kit": "catalog:",
|
||||
},
|
||||
},
|
||||
"packages/css": {
|
||||
"name": "@opencode-ai/css",
|
||||
"version": "1.18.4",
|
||||
},
|
||||
"packages/desktop": {
|
||||
"name": "@opencode-ai/desktop",
|
||||
"version": "1.18.4",
|
||||
@@ -2085,6 +2089,8 @@
|
||||
|
||||
"@opencode-ai/core": ["@opencode-ai/core@workspace:packages/core"],
|
||||
|
||||
"@opencode-ai/css": ["@opencode-ai/css@workspace:packages/css"],
|
||||
|
||||
"@opencode-ai/desktop": ["@opencode-ai/desktop@workspace:packages/desktop"],
|
||||
|
||||
"@opencode-ai/docs": ["@opencode-ai/docs@workspace:packages/docs"],
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2025 opencode
|
||||
|
||||
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:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
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 NONINFRINGEMENT. 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.
|
||||
@@ -0,0 +1,156 @@
|
||||
# @opencode-ai/css
|
||||
|
||||
OpenCode's design system as plain CSS. It has no JavaScript, framework, build tool, or runtime dependency.
|
||||
|
||||
Use semantic HTML and `data-*` attributes. Native browser states such as `:hover`, `:focus-visible`, and `:disabled` work automatically; your JavaScript can represent application state with attributes such as `data-expanded`, `data-checked`, and `data-invalid`.
|
||||
|
||||
## Install
|
||||
|
||||
```sh
|
||||
npm install @opencode-ai/css
|
||||
```
|
||||
|
||||
Load the complete design system from JavaScript:
|
||||
|
||||
```js
|
||||
import "@opencode-ai/css"
|
||||
```
|
||||
|
||||
Or from CSS:
|
||||
|
||||
```css
|
||||
@import "@opencode-ai/css";
|
||||
```
|
||||
|
||||
For a static page with no package tooling:
|
||||
|
||||
```html
|
||||
<link rel="stylesheet" href="https://unpkg.com/@opencode-ai/css/index.css" />
|
||||
```
|
||||
|
||||
Add `data-oc-root` to the element that should receive the default page background, text color, and font. Set `data-color-scheme` to `light` or `dark` on that element or an ancestor.
|
||||
|
||||
```html
|
||||
<body data-oc-root data-color-scheme="dark">
|
||||
<button data-component="button" data-variant="neutral" data-size="normal">Deploy</button>
|
||||
</body>
|
||||
```
|
||||
|
||||
## Selective imports
|
||||
|
||||
The default export contains every component. Small pages can load only what they use:
|
||||
|
||||
```css
|
||||
@import "@opencode-ai/css/tokens.css";
|
||||
@import "@opencode-ai/css/base.css";
|
||||
@import "@opencode-ai/css/components/button.css";
|
||||
@import "@opencode-ai/css/components/badge.css";
|
||||
@import "@opencode-ai/css/components/table.css";
|
||||
```
|
||||
|
||||
`tokens.css` defines the color ramps and semantic theme variables. `base.css` is intentionally small and scoped: it only applies the full-page defaults to `[data-oc-root]` and normalization to design-system elements.
|
||||
|
||||
## Common components
|
||||
|
||||
### Button
|
||||
|
||||
```html
|
||||
<button data-component="button" data-variant="contrast" data-size="normal">Create release</button>
|
||||
<button data-component="button" data-variant="danger" data-size="small">Delete</button>
|
||||
<button data-component="button" data-variant="ghost" data-size="small" disabled>Disabled</button>
|
||||
```
|
||||
|
||||
Variants: `neutral`, `contrast`, `outline`, `ghost`, `ghost-muted`, `danger`, `warning`, and `loading`. Sizes: `small`, `normal`, and `large`.
|
||||
|
||||
### Badge
|
||||
|
||||
```html
|
||||
<span data-component="badge">Inactive</span> <span data-component="badge" data-variant="accent">Active</span>
|
||||
```
|
||||
|
||||
### Internal-tool page
|
||||
|
||||
```html
|
||||
<main data-component="page">
|
||||
<header data-slot="page-header">
|
||||
<div data-slot="page-heading">
|
||||
<p data-slot="page-eyebrow">Release control</p>
|
||||
<h1 data-slot="page-title">Artifacts</h1>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<section data-component="surface" data-padding="none">
|
||||
<header data-slot="surface-header">
|
||||
<h2 data-slot="surface-title">Published builds</h2>
|
||||
<p data-slot="surface-description">Newest first.</p>
|
||||
</header>
|
||||
<div data-slot="surface-content">
|
||||
<div data-component="table-container">
|
||||
<table data-component="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Status</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td data-numeric>cli@1.0.0</td>
|
||||
<td><span data-component="badge" data-variant="accent">Active</span></td>
|
||||
<td data-align="end">
|
||||
<button data-component="button" data-variant="neutral" data-size="small">Activate</button>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
```
|
||||
|
||||
### JavaScript-owned state
|
||||
|
||||
The package styles state; it does not implement behavior. Keep semantic HTML and ARIA as the source of truth, then mirror state to a `data-*` attribute when a component needs it.
|
||||
|
||||
```js
|
||||
const trigger = document.querySelector('[data-component="button"]')
|
||||
trigger.addEventListener("click", () => {
|
||||
const expanded = trigger.getAttribute("aria-expanded") !== "true"
|
||||
trigger.setAttribute("aria-expanded", String(expanded))
|
||||
trigger.toggleAttribute("data-expanded", expanded)
|
||||
})
|
||||
```
|
||||
|
||||
## Theme customization
|
||||
|
||||
Override semantic tokens at your app root instead of editing component CSS:
|
||||
|
||||
```css
|
||||
[data-oc-root] {
|
||||
--oc-font-family-sans: Inter, system-ui, sans-serif;
|
||||
--oc-page-max-width: 1440px;
|
||||
--oc-bg-accent: #7152f4;
|
||||
--oc-border-focus: #8271f8;
|
||||
}
|
||||
```
|
||||
|
||||
The low-level color ramps live in `colors.css`; light and dark semantic mappings live in `theme.css`.
|
||||
|
||||
## Component inventory
|
||||
|
||||
The first release is intentionally focused on simple internal tools:
|
||||
|
||||
- Layout: page, surface, and table
|
||||
- Actions and status: button and badge
|
||||
- Forms: field, input, select, textarea, and native checkbox/radio styling
|
||||
- Structure: divider
|
||||
|
||||
Each stylesheet is available as `@opencode-ai/css/components/<name>.css`. This small interface avoids publishing framework-specific DOM contracts for menus, dialogs, comboboxes, and other interactive widgets.
|
||||
|
||||
## Scope
|
||||
|
||||
This package is styling only. It does not create DOM, manage focus, provide keyboard navigation, position overlays, or add ARIA attributes. Use native HTML for simple controls and a small behavior layer of your choice for menus, dialogs, comboboxes, and other interactive widgets.
|
||||
|
||||
`@opencode-ai/ui` does not depend on this package yet. The CSS is intentionally isolated while its public interface settles.
|
||||
@@ -0,0 +1,32 @@
|
||||
@layer base {
|
||||
:root {
|
||||
--font-weight-regular: 440;
|
||||
--line-height-normal: 1.5;
|
||||
--letter-spacing-normal: normal;
|
||||
--oc-page-max-width: 1200px;
|
||||
--oc-page-padding-block: 56px;
|
||||
}
|
||||
|
||||
:where([data-component], [data-component] *, [data-slot]) {
|
||||
box-sizing: border-box;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
text-rendering: geometricPrecision;
|
||||
}
|
||||
|
||||
:where([data-component]) {
|
||||
font-family: var(--oc-font-family-sans);
|
||||
}
|
||||
}
|
||||
|
||||
@layer base {
|
||||
[data-oc-root] {
|
||||
min-height: 100%;
|
||||
margin: 0;
|
||||
background: var(--oc-bg-deep);
|
||||
color: var(--oc-text-base);
|
||||
font-family: var(--oc-font-family-sans);
|
||||
font-size: 13px;
|
||||
line-height: 1.5;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,172 @@
|
||||
@layer theme {
|
||||
:root {
|
||||
/* ── Grey ── */
|
||||
--oc-grey-50: #ffffffff;
|
||||
--oc-grey-100: #fafafaff;
|
||||
--oc-grey-200: #f2f2f2ff;
|
||||
--oc-grey-300: #eeeeeeff;
|
||||
--oc-grey-400: #dbdbdbff;
|
||||
--oc-grey-500: #aeaeaeff;
|
||||
--oc-grey-600: #808080ff;
|
||||
--oc-grey-700: #5c5c5cff;
|
||||
--oc-grey-800: #3a3a3aff;
|
||||
--oc-grey-900: #2e2e2eff;
|
||||
--oc-grey-1000: #242424ff;
|
||||
--oc-grey-1100: #161616ff;
|
||||
--oc-grey-1200: #080808ff;
|
||||
|
||||
/* ── Alpha Dark ── */
|
||||
--oc-alpha-dark-100: #000000ff;
|
||||
--oc-alpha-dark-90: #000000e5;
|
||||
--oc-alpha-dark-80: #000000cc;
|
||||
--oc-alpha-dark-70: #000000b2;
|
||||
--oc-alpha-dark-60: #00000099;
|
||||
--oc-alpha-dark-50: #00000080;
|
||||
--oc-alpha-dark-40: #00000066;
|
||||
--oc-alpha-dark-30: #0000004d;
|
||||
--oc-alpha-dark-24: #0000003d;
|
||||
--oc-alpha-dark-20: #00000033;
|
||||
--oc-alpha-dark-16: #00000029;
|
||||
--oc-alpha-dark-14: #00000024;
|
||||
--oc-alpha-dark-12: #0000001f;
|
||||
--oc-alpha-dark-10: #0000001a;
|
||||
--oc-alpha-dark-8: #00000014;
|
||||
--oc-alpha-dark-6: #0000000f;
|
||||
--oc-alpha-dark-4: #0000000a;
|
||||
--oc-alpha-dark-2: #00000005;
|
||||
--oc-alpha-dark-0: #00000000;
|
||||
|
||||
/* ── Alpha Light ── */
|
||||
--oc-alpha-light-100: #ffffffff;
|
||||
--oc-alpha-light-90: #ffffffe5;
|
||||
--oc-alpha-light-80: #ffffffcc;
|
||||
--oc-alpha-light-70: #ffffffb2;
|
||||
--oc-alpha-light-60: #ffffff99;
|
||||
--oc-alpha-light-50: #ffffff80;
|
||||
--oc-alpha-light-40: #ffffff66;
|
||||
--oc-alpha-light-30: #ffffff4d;
|
||||
--oc-alpha-light-24: #ffffff3d;
|
||||
--oc-alpha-light-20: #ffffff33;
|
||||
--oc-alpha-light-16: #ffffff29;
|
||||
--oc-alpha-light-14: #ffffff24;
|
||||
--oc-alpha-light-12: #ffffff1f;
|
||||
--oc-alpha-light-10: #ffffff1a;
|
||||
--oc-alpha-light-8: #ffffff14;
|
||||
--oc-alpha-light-6: #ffffff0f;
|
||||
--oc-alpha-light-4: #ffffff0a;
|
||||
--oc-alpha-light-2: #ffffff05;
|
||||
--oc-alpha-light-0: #ffffff00;
|
||||
|
||||
/* ── Red ── */
|
||||
--oc-red-100: #fcecebff;
|
||||
--oc-red-200: #f6d5d3ff;
|
||||
--oc-red-300: #f2bbb7ff;
|
||||
--oc-red-400: #f29b96ff;
|
||||
--oc-red-500: #f17471ff;
|
||||
--oc-red-600: #f1484fff;
|
||||
--oc-red-700: #d92e3cff;
|
||||
--oc-red-800: #b82d35ff;
|
||||
--oc-red-900: #97252bff;
|
||||
--oc-red-1000: #7a1f23ff;
|
||||
--oc-red-1100: #5f1a1cff;
|
||||
--oc-red-1200: #461516ff;
|
||||
|
||||
/* ── Orange ── */
|
||||
--oc-orange-100: #fdf2edff;
|
||||
--oc-orange-200: #ffe7dcff;
|
||||
--oc-orange-300: #ffd8c6ff;
|
||||
--oc-orange-400: #ffc1a4ff;
|
||||
--oc-orange-500: #ffa478ff;
|
||||
--oc-orange-600: #ff8648ff;
|
||||
--oc-orange-700: #ee7330ff;
|
||||
--oc-orange-800: #d16427ff;
|
||||
--oc-orange-900: #b35624ff;
|
||||
--oc-orange-1000: #954c27ff;
|
||||
--oc-orange-1100: #723d22ff;
|
||||
--oc-orange-1200: #5a2c14ff;
|
||||
|
||||
/* ── Yellow ── */
|
||||
--oc-yellow-100: #fefaecff;
|
||||
--oc-yellow-200: #fcefd0ff;
|
||||
--oc-yellow-300: #f7e5b5ff;
|
||||
--oc-yellow-400: #f3da9bff;
|
||||
--oc-yellow-500: #f2cf76ff;
|
||||
--oc-yellow-600: #f6c251ff;
|
||||
--oc-yellow-700: #e7af36ff;
|
||||
--oc-yellow-800: #cb9f34ff;
|
||||
--oc-yellow-900: #ac8833ff;
|
||||
--oc-yellow-1000: #8e7231ff;
|
||||
--oc-yellow-1100: #68552bff;
|
||||
--oc-yellow-1200: #4b4025ff;
|
||||
|
||||
/* ── Green ── */
|
||||
--oc-green-100: #e7f9eaff;
|
||||
--oc-green-200: #d0f0d5ff;
|
||||
--oc-green-300: #b8e9c1ff;
|
||||
--oc-green-400: #96e3a6ff;
|
||||
--oc-green-500: #6bd586ff;
|
||||
--oc-green-600: #49c970ff;
|
||||
--oc-green-700: #2eaf5aff;
|
||||
--oc-green-800: #198b43ff;
|
||||
--oc-green-900: #1d783cff;
|
||||
--oc-green-1000: #196130ff;
|
||||
--oc-green-1100: #164c26ff;
|
||||
--oc-green-1200: #14361dff;
|
||||
|
||||
/* ── Cyan ── */
|
||||
--oc-cyan-100: #e2f7fbff;
|
||||
--oc-cyan-200: #c4edf4ff;
|
||||
--oc-cyan-300: #a3e4efff;
|
||||
--oc-cyan-400: #65d9ebff;
|
||||
--oc-cyan-500: #00c5dfff;
|
||||
--oc-cyan-600: #00abcfff;
|
||||
--oc-cyan-700: #0096b8ff;
|
||||
--oc-cyan-800: #007d9bff;
|
||||
--oc-cyan-900: #006c85ff;
|
||||
--oc-cyan-1000: #005a6eff;
|
||||
--oc-cyan-1100: #004756ff;
|
||||
--oc-cyan-1200: #00353fff;
|
||||
|
||||
/* ── Blue ── */
|
||||
--oc-blue-100: #ecf1feff;
|
||||
--oc-blue-200: #d7e2fcff;
|
||||
--oc-blue-300: #c3d4fdff;
|
||||
--oc-blue-400: #a2bcffff;
|
||||
--oc-blue-500: #7698fdff;
|
||||
--oc-blue-600: #3b5cf6ff;
|
||||
--oc-blue-700: #3250dfff;
|
||||
--oc-blue-800: #2c47c8ff;
|
||||
--oc-blue-900: #263fa9ff;
|
||||
--oc-blue-1000: #22388fff;
|
||||
--oc-blue-1100: #1c2e70ff;
|
||||
--oc-blue-1200: #1b2852ff;
|
||||
|
||||
/* ── Purple ── */
|
||||
--oc-purple-100: #ebecfeff;
|
||||
--oc-purple-200: #d5d5fcff;
|
||||
--oc-purple-300: #b9b8f5ff;
|
||||
--oc-purple-400: #9e99f7ff;
|
||||
--oc-purple-500: #8271f8ff;
|
||||
--oc-purple-600: #7152f4ff;
|
||||
--oc-purple-700: #623be2ff;
|
||||
--oc-purple-800: #5230c2ff;
|
||||
--oc-purple-900: #442aa1ff;
|
||||
--oc-purple-1000: #361f83ff;
|
||||
--oc-purple-1100: #2b1b6aff;
|
||||
--oc-purple-1200: #221358ff;
|
||||
|
||||
/* ── Pink ── */
|
||||
--oc-pink-100: #fdecf3ff;
|
||||
--oc-pink-200: #f7d5e4ff;
|
||||
--oc-pink-300: #fabcd8ff;
|
||||
--oc-pink-400: #f799c6ff;
|
||||
--oc-pink-500: #f26cb2ff;
|
||||
--oc-pink-600: #f64aabff;
|
||||
--oc-pink-700: #e4429eff;
|
||||
--oc-pink-800: #c83d8bff;
|
||||
--oc-pink-900: #aa3576ff;
|
||||
--oc-pink-1000: #8c2d61ff;
|
||||
--oc-pink-1100: #6f284fff;
|
||||
--oc-pink-1200: #5c1d3fff;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
@import "./components/badge.css" layer(components);
|
||||
@import "./components/button.css" layer(components);
|
||||
@import "./components/check.css" layer(components);
|
||||
@import "./components/divider.css" layer(components);
|
||||
@import "./components/field.css" layer(components);
|
||||
@import "./components/input.css" layer(components);
|
||||
@import "./components/page.css" layer(components);
|
||||
@import "./components/surface.css" layer(components);
|
||||
@import "./components/table.css" layer(components);
|
||||
@@ -0,0 +1,33 @@
|
||||
[data-component="badge"] {
|
||||
box-sizing: border-box;
|
||||
display: inline-flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 4px;
|
||||
height: 16px;
|
||||
padding: 0 4px;
|
||||
user-select: none;
|
||||
|
||||
border-radius: 2px;
|
||||
border: 0.5px solid var(--oc-border-base);
|
||||
background: var(--oc-bg-layer-02);
|
||||
|
||||
font-style: normal;
|
||||
font-weight: 530;
|
||||
font-size: 11px;
|
||||
line-height: 1;
|
||||
letter-spacing: 0.05px;
|
||||
color: var(--oc-text-muted);
|
||||
font-variant-numeric: tabular-nums;
|
||||
}
|
||||
|
||||
[data-component="badge"][data-high-contrast] {
|
||||
border-color: var(--oc-border-strong);
|
||||
}
|
||||
|
||||
[data-component="badge"][data-variant="accent"] {
|
||||
border: 0;
|
||||
background: var(--oc-bg-accent);
|
||||
color: var(--oc-text-contrast);
|
||||
}
|
||||
@@ -0,0 +1,246 @@
|
||||
[data-component="button"] {
|
||||
position: relative;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 6px;
|
||||
border-radius: 6px;
|
||||
font-style: normal;
|
||||
font-weight: 530;
|
||||
font-size: 13px;
|
||||
line-height: 20px;
|
||||
color: var(--oc-text-base);
|
||||
text-shadow: none;
|
||||
font-variant-numeric: tabular-nums;
|
||||
letter-spacing: -0.04px;
|
||||
user-select: none;
|
||||
cursor: pointer;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
[data-component="button"]:focus {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
[data-component="button"]:is(:focus-visible, [data-state="focus"]):not(:disabled):not([data-variant="loading"]) {
|
||||
outline: 2px solid var(--oc-border-focus);
|
||||
outline-offset: 2.5px;
|
||||
}
|
||||
|
||||
[data-component="button"][data-size="normal"] {
|
||||
height: 28px;
|
||||
padding: 0 11px;
|
||||
}
|
||||
|
||||
[data-component="button"][data-size="small"] {
|
||||
height: 24px;
|
||||
padding: 0 9px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
[data-component="button"][data-size="large"] {
|
||||
height: 32px;
|
||||
padding: 0 15px;
|
||||
}
|
||||
|
||||
[data-component="button"][data-icon][data-size="small"] {
|
||||
padding-left: 9px;
|
||||
}
|
||||
|
||||
[data-component="button"][data-icon][data-size="normal"] {
|
||||
padding-left: 11px;
|
||||
}
|
||||
|
||||
[data-component="button"][data-icon][data-size="large"] {
|
||||
padding-left: 15px;
|
||||
}
|
||||
|
||||
[data-component="button"] [data-slot="icon-svg"] {
|
||||
color: currentColor;
|
||||
}
|
||||
|
||||
/* Neutral */
|
||||
[data-component="button"][data-variant="neutral"] {
|
||||
background-color: var(--oc-bg-button-neutral);
|
||||
color: var(--oc-text-base);
|
||||
box-shadow: var(--oc-elevation-button-neutral);
|
||||
}
|
||||
|
||||
[data-component="button"][data-variant="neutral"]:is(:hover, [data-state="hover"]):not(:disabled) {
|
||||
background-image:
|
||||
linear-gradient(90deg, var(--oc-overlay-hover) 0%, var(--oc-overlay-hover) 100%),
|
||||
linear-gradient(90deg, var(--oc-bg-button-neutral) 0%, var(--oc-bg-button-neutral) 100%);
|
||||
}
|
||||
|
||||
[data-component="button"][data-variant="neutral"]:is(:active, [data-state="pressed"]):not(:disabled) {
|
||||
background-image:
|
||||
linear-gradient(90deg, var(--oc-overlay-pressed) 0%, var(--oc-overlay-pressed) 100%),
|
||||
linear-gradient(90deg, var(--oc-bg-button-neutral) 0%, var(--oc-bg-button-neutral) 100%);
|
||||
}
|
||||
|
||||
[data-component="button"][data-variant="neutral"]:is(:disabled, [data-state="disabled"]) {
|
||||
opacity: 0.5;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
/* Danger */
|
||||
[data-component="button"][data-variant="danger"] {
|
||||
background-color: var(--oc-bg-button-neutral);
|
||||
color: var(--oc-state-fg-danger);
|
||||
box-shadow: var(--oc-elevation-button-neutral);
|
||||
}
|
||||
|
||||
[data-component="button"][data-variant="danger"]:is(:hover, [data-state="hover"]):not(:disabled) {
|
||||
background-image:
|
||||
linear-gradient(90deg, var(--oc-overlay-hover) 0%, var(--oc-overlay-hover) 100%),
|
||||
linear-gradient(90deg, var(--oc-bg-button-neutral) 0%, var(--oc-bg-button-neutral) 100%);
|
||||
}
|
||||
|
||||
[data-component="button"][data-variant="danger"]:is(:active, [data-state="pressed"]):not(:disabled) {
|
||||
background-image:
|
||||
linear-gradient(90deg, var(--oc-overlay-pressed) 0%, var(--oc-overlay-pressed) 100%),
|
||||
linear-gradient(90deg, var(--oc-bg-button-neutral) 0%, var(--oc-bg-button-neutral) 100%);
|
||||
}
|
||||
|
||||
[data-component="button"][data-variant="danger"]:is(:disabled, [data-state="disabled"]) {
|
||||
opacity: 0.5;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
[data-component="button"][data-variant="warning"] {
|
||||
background-color: var(--oc-bg-button-neutral);
|
||||
color: var(--oc-state-fg-warning);
|
||||
box-shadow: var(--oc-elevation-button-neutral);
|
||||
}
|
||||
|
||||
[data-component="button"][data-variant="warning"]:is(:hover, [data-state="hover"]):not(:disabled) {
|
||||
background-image:
|
||||
linear-gradient(90deg, var(--oc-overlay-hover) 0%, var(--oc-overlay-hover) 100%),
|
||||
linear-gradient(90deg, var(--oc-bg-button-neutral) 0%, var(--oc-bg-button-neutral) 100%);
|
||||
}
|
||||
|
||||
[data-component="button"][data-variant="warning"]:is(:active, [data-state="pressed"]):not(:disabled) {
|
||||
background-image:
|
||||
linear-gradient(90deg, var(--oc-overlay-pressed) 0%, var(--oc-overlay-pressed) 100%),
|
||||
linear-gradient(90deg, var(--oc-bg-button-neutral) 0%, var(--oc-bg-button-neutral) 100%);
|
||||
}
|
||||
|
||||
[data-component="button"][data-variant="warning"]:is(:disabled, [data-state="disabled"]) {
|
||||
opacity: 0.5;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
/* Outline */
|
||||
[data-component="button"][data-variant="outline"] {
|
||||
background-color: transparent;
|
||||
color: var(--oc-text-base);
|
||||
box-shadow: inset 0 0 0 1px var(--oc-border-muted);
|
||||
}
|
||||
|
||||
[data-component="button"][data-variant="outline"]:is(:hover, [data-state="hover"]):not(:disabled) {
|
||||
background-color: var(--oc-overlay-hover);
|
||||
}
|
||||
|
||||
[data-component="button"][data-variant="outline"]:is(:active, [data-state="pressed"]):not(:disabled) {
|
||||
background-color: var(--oc-overlay-pressed);
|
||||
}
|
||||
|
||||
[data-component="button"][data-variant="outline"]:is(:disabled, [data-state="disabled"]) {
|
||||
opacity: 0.5;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
/* Contrast */
|
||||
[data-component="button"][data-variant="contrast"] {
|
||||
background-image:
|
||||
linear-gradient(180deg, var(--oc-alpha-light-20) 0%, var(--oc-alpha-light-0) 100%),
|
||||
linear-gradient(90deg, var(--oc-bg-contrast) 0%, var(--oc-bg-contrast) 100%);
|
||||
color: var(--oc-text-contrast);
|
||||
text-shadow: 0 0.5px 0.5px rgba(0, 0, 0, 0.3);
|
||||
box-shadow: var(--oc-elevation-button-contrast);
|
||||
}
|
||||
|
||||
[data-component="button"][data-variant="contrast"]:is(:hover, [data-state="hover"]):not(:disabled) {
|
||||
background-image:
|
||||
linear-gradient(90deg, var(--oc-overlay-contrast-hover) 0%, var(--oc-overlay-contrast-hover) 100%),
|
||||
linear-gradient(180deg, var(--oc-alpha-light-20) 0%, var(--oc-alpha-light-0) 100%),
|
||||
linear-gradient(90deg, var(--oc-bg-contrast) 0%, var(--oc-bg-contrast) 100%);
|
||||
}
|
||||
|
||||
[data-component="button"][data-variant="contrast"]:is(:active, [data-state="pressed"]):not(:disabled) {
|
||||
background-image:
|
||||
linear-gradient(90deg, var(--oc-overlay-contrast-pressed) 0%, var(--oc-overlay-contrast-pressed) 100%),
|
||||
linear-gradient(180deg, var(--oc-alpha-light-20) 0%, var(--oc-alpha-light-0) 100%),
|
||||
linear-gradient(90deg, var(--oc-bg-contrast) 0%, var(--oc-bg-contrast) 100%);
|
||||
}
|
||||
|
||||
[data-component="button"][data-variant="contrast"]:is(:disabled, [data-state="disabled"]) {
|
||||
opacity: 0.4;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
/* Loading */
|
||||
[data-component="button"][data-variant="loading"] {
|
||||
background: #f2f2f2;
|
||||
color: var(--oc-text-base);
|
||||
box-shadow: inset 0 0 0 0.5px rgba(0, 0, 0, 0.08);
|
||||
cursor: default;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
/* Ghost */
|
||||
[data-component="button"][data-variant="ghost"] {
|
||||
background-color: transparent;
|
||||
color: var(--oc-text-base);
|
||||
}
|
||||
|
||||
[data-component="button"][data-variant="ghost"]:is(:hover, [data-state="hover"]):not(:disabled):not([data-expanded]) {
|
||||
background-color: var(--oc-overlay-hover);
|
||||
}
|
||||
|
||||
[data-component="button"][data-variant="ghost"]:is(:active, [data-state="pressed"]):not(:disabled) {
|
||||
background-color: var(--oc-overlay-pressed);
|
||||
}
|
||||
|
||||
[data-component="button"][data-variant="ghost"]:where([data-expanded]):not(:disabled) {
|
||||
background-color: var(--oc-overlay-pressed);
|
||||
}
|
||||
|
||||
[data-component="button"][data-variant="ghost"]:is(:disabled, [data-state="disabled"]) {
|
||||
opacity: 0.5;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
/* Ghost muted */
|
||||
[data-component="button"][data-variant="ghost-muted"] {
|
||||
background-color: transparent;
|
||||
color: var(--oc-text-muted);
|
||||
}
|
||||
|
||||
[data-component="button"][data-variant="ghost-muted"] [data-slot="icon-svg"] {
|
||||
color: var(--oc-icon-muted);
|
||||
}
|
||||
|
||||
[data-component="button"][data-variant="ghost-muted"]:is(:hover, [data-state="hover"]):not(:disabled):not(
|
||||
[data-expanded]
|
||||
) {
|
||||
background-color: var(--oc-overlay-hover);
|
||||
}
|
||||
|
||||
[data-component="button"][data-variant="ghost-muted"]:is(:active, [data-state="pressed"]):not(:disabled) {
|
||||
background-color: var(--oc-overlay-pressed);
|
||||
}
|
||||
|
||||
[data-component="button"][data-variant="ghost-muted"]:where([data-expanded]):not(:disabled) {
|
||||
background-color: var(--oc-overlay-pressed);
|
||||
color: var(--oc-text-base);
|
||||
}
|
||||
|
||||
[data-component="button"][data-variant="ghost-muted"]:where([data-expanded]):not(:disabled) [data-slot="icon-svg"] {
|
||||
color: var(--oc-icon-base);
|
||||
}
|
||||
|
||||
[data-component="button"][data-variant="ghost-muted"]:is(:disabled, [data-state="disabled"]) {
|
||||
opacity: 0.5;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
[data-component="check"] {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
color: var(--oc-text-base);
|
||||
font-size: 13px;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
[data-component="check"] :is(input[type="checkbox"], input[type="radio"]) {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
margin: 0;
|
||||
accent-color: var(--oc-bg-accent);
|
||||
}
|
||||
|
||||
[data-component="check"]:has(input:disabled) {
|
||||
opacity: 0.5;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
[data-component="divider"] {
|
||||
box-sizing: border-box;
|
||||
width: 100%;
|
||||
height: 1px;
|
||||
transform: scaleY(0.5);
|
||||
border: none;
|
||||
background: var(--oc-border-strong);
|
||||
flex: none;
|
||||
align-self: stretch;
|
||||
flex-grow: 0;
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
[data-component="field"] {
|
||||
display: grid;
|
||||
gap: 6px;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
[data-component="field"] [data-slot="field-label"] {
|
||||
color: var(--oc-text-base);
|
||||
font-size: 13px;
|
||||
font-weight: 530;
|
||||
line-height: 1.25;
|
||||
}
|
||||
|
||||
[data-component="field"] [data-slot="field-description"] {
|
||||
margin: 0;
|
||||
color: var(--oc-text-muted);
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
[data-component="field"] [data-slot="field-error"] {
|
||||
margin: 0;
|
||||
color: var(--oc-state-fg-danger);
|
||||
font-size: 11px;
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
[data-component="input"],
|
||||
[data-component="select"],
|
||||
[data-component="textarea"] {
|
||||
width: 100%;
|
||||
min-width: 0;
|
||||
border: 0;
|
||||
border-radius: 6px;
|
||||
outline: 1px solid transparent;
|
||||
background: linear-gradient(180deg, var(--oc-alpha-light-2) 0%, var(--oc-alpha-light-0) 100%), var(--oc-bg-base);
|
||||
box-shadow: var(--oc-elevation-button-neutral);
|
||||
color: var(--oc-text-base);
|
||||
font: inherit;
|
||||
font-size: 13px;
|
||||
transition:
|
||||
outline-color 85ms ease-out,
|
||||
background 85ms ease-out;
|
||||
}
|
||||
|
||||
[data-component="input"],
|
||||
[data-component="select"] {
|
||||
height: 32px;
|
||||
padding: 0 10px;
|
||||
}
|
||||
|
||||
[data-component="textarea"] {
|
||||
min-height: 88px;
|
||||
padding: 8px 10px;
|
||||
line-height: 1.5;
|
||||
resize: vertical;
|
||||
}
|
||||
|
||||
:is([data-component="input"], [data-component="select"], [data-component="textarea"])::placeholder {
|
||||
color: var(--oc-text-faint);
|
||||
}
|
||||
|
||||
:is([data-component="input"], [data-component="select"], [data-component="textarea"]):hover:not(:disabled) {
|
||||
background: linear-gradient(0deg, var(--oc-overlay-hover), var(--oc-overlay-hover)), var(--oc-bg-base);
|
||||
}
|
||||
|
||||
:is([data-component="input"], [data-component="select"], [data-component="textarea"]):focus-visible {
|
||||
outline: 2px solid var(--oc-border-focus);
|
||||
outline-offset: 1px;
|
||||
}
|
||||
|
||||
:is([data-component="input"], [data-component="select"], [data-component="textarea"]):is(:disabled, [data-disabled]) {
|
||||
opacity: 0.5;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
:is([data-component="input"], [data-component="select"], [data-component="textarea"]):is(
|
||||
[aria-invalid="true"],
|
||||
[data-invalid]
|
||||
) {
|
||||
outline-color: var(--oc-state-fg-danger);
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
[data-component="page"] {
|
||||
width: min(100% - 32px, var(--oc-page-max-width, 1200px));
|
||||
margin-inline: auto;
|
||||
padding-block: var(--oc-page-padding-block, 56px);
|
||||
}
|
||||
|
||||
[data-component="page"] [data-slot="page-header"] {
|
||||
display: flex;
|
||||
align-items: flex-end;
|
||||
justify-content: space-between;
|
||||
gap: 24px;
|
||||
margin-bottom: 32px;
|
||||
}
|
||||
|
||||
[data-component="page"] [data-slot="page-heading"] {
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
[data-component="page"] [data-slot="page-eyebrow"] {
|
||||
margin: 0 0 4px;
|
||||
color: var(--oc-text-muted);
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
[data-component="page"] [data-slot="page-title"] {
|
||||
margin: 0;
|
||||
color: var(--oc-text-base);
|
||||
font-size: clamp(36px, 6vw, 64px);
|
||||
font-weight: 440;
|
||||
line-height: 0.95;
|
||||
letter-spacing: -0.045em;
|
||||
}
|
||||
|
||||
[data-component="page"] [data-slot="page-description"] {
|
||||
max-width: 64ch;
|
||||
margin: 12px 0 0;
|
||||
color: var(--oc-text-muted);
|
||||
}
|
||||
|
||||
[data-component="page"] [data-slot="page-actions"] {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
gap: 8px;
|
||||
flex: none;
|
||||
}
|
||||
|
||||
[data-component="page"] [data-slot="page-content"] {
|
||||
display: grid;
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
@media (max-width: 640px) {
|
||||
[data-component="page"] {
|
||||
width: min(100% - 24px, var(--oc-page-max-width, 1200px));
|
||||
padding-block: 32px;
|
||||
}
|
||||
|
||||
[data-component="page"] [data-slot="page-header"] {
|
||||
align-items: flex-start;
|
||||
flex-direction: column;
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
[data-component="surface"] {
|
||||
overflow: hidden;
|
||||
border: 1px solid var(--oc-border-muted);
|
||||
border-radius: 12px;
|
||||
background: var(--oc-bg-layer-01);
|
||||
box-shadow: var(--oc-elevation-raised);
|
||||
}
|
||||
|
||||
[data-component="surface"][data-variant="flat"] {
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
[data-component="surface"] [data-slot="surface-header"] {
|
||||
padding: 24px 24px 16px;
|
||||
}
|
||||
|
||||
[data-component="surface"] [data-slot="surface-title"] {
|
||||
margin: 0;
|
||||
color: var(--oc-text-base);
|
||||
font-size: 16px;
|
||||
font-weight: 530;
|
||||
line-height: 1.25;
|
||||
letter-spacing: -0.01em;
|
||||
}
|
||||
|
||||
[data-component="surface"] [data-slot="surface-description"] {
|
||||
margin: 6px 0 0;
|
||||
color: var(--oc-text-muted);
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
[data-component="surface"] [data-slot="surface-content"] {
|
||||
padding: 0 24px 24px;
|
||||
}
|
||||
|
||||
[data-component="surface"][data-padding="none"] [data-slot="surface-content"] {
|
||||
padding: 0;
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
[data-component="table-container"] {
|
||||
width: 100%;
|
||||
overflow-x: auto;
|
||||
}
|
||||
|
||||
[data-component="table"] {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
color: var(--oc-text-base);
|
||||
font-size: 13px;
|
||||
font-variant-numeric: tabular-nums;
|
||||
}
|
||||
|
||||
[data-component="table"] :is(th, td) {
|
||||
height: 48px;
|
||||
padding: 0 16px;
|
||||
border-bottom: 1px solid var(--oc-border-muted);
|
||||
text-align: left;
|
||||
vertical-align: middle;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
[data-component="table"] th {
|
||||
height: 40px;
|
||||
color: var(--oc-text-muted);
|
||||
font-size: 11px;
|
||||
font-weight: 530;
|
||||
letter-spacing: 0.08em;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
[data-component="table"] tbody tr:last-child td {
|
||||
border-bottom: 0;
|
||||
}
|
||||
|
||||
[data-component="table"] tbody tr:is(:hover, [data-state="hover"]) td {
|
||||
background: var(--oc-overlay-hover);
|
||||
}
|
||||
|
||||
[data-component="table"] [data-align="end"] {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
[data-component="table"] [data-numeric] {
|
||||
font-family: ui-monospace, "SFMono-Regular", Consolas, monospace;
|
||||
}
|
||||
|
||||
[data-component="table"] [data-slot="table-actions"] {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
gap: 8px;
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
<!doctype html>
|
||||
<html lang="en" data-color-scheme="dark">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<link rel="icon" href="data:," />
|
||||
<title>OpenCode CSS internal tool</title>
|
||||
<link rel="stylesheet" href="../index.css" />
|
||||
<style>
|
||||
:root {
|
||||
--oc-font-family-sans: Inter, ui-sans-serif, system-ui, sans-serif;
|
||||
}
|
||||
[data-slot="release-meta"] {
|
||||
color: var(--oc-text-muted);
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body data-oc-root>
|
||||
<main data-component="page">
|
||||
<header data-slot="page-header">
|
||||
<div data-slot="page-heading">
|
||||
<p data-slot="page-eyebrow">Release control</p>
|
||||
<h1 data-slot="page-title">Artifacts</h1>
|
||||
</div>
|
||||
<span data-component="badge">dax@anoma.ly</span>
|
||||
</header>
|
||||
|
||||
<section data-component="surface" data-padding="none">
|
||||
<header data-slot="surface-header">
|
||||
<h2 data-slot="surface-title">Published builds</h2>
|
||||
<p data-slot="surface-description">
|
||||
Every build received from the trusted publishing workflow, newest first.
|
||||
</p>
|
||||
</header>
|
||||
<div data-slot="surface-content">
|
||||
<div data-component="table-container">
|
||||
<table data-component="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Channel</th>
|
||||
<th>Name</th>
|
||||
<th>Distribution</th>
|
||||
<th>Version</th>
|
||||
<th>Created</th>
|
||||
<th>Status</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>next</td>
|
||||
<td data-numeric>cli</td>
|
||||
<td>npm</td>
|
||||
<td data-numeric>0.0.0-next-16020</td>
|
||||
<td data-slot="release-meta">2026-07-22T13:23:42.642Z</td>
|
||||
<td><span data-component="badge" data-variant="accent">Active</span></td>
|
||||
<td></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>next</td>
|
||||
<td data-numeric>cli</td>
|
||||
<td>npm</td>
|
||||
<td data-numeric>0.0.0-next-16019</td>
|
||||
<td data-slot="release-meta">2026-07-22T10:33:24.519Z</td>
|
||||
<td><span data-component="badge">Inactive</span></td>
|
||||
<td data-align="end">
|
||||
<button data-component="button" data-variant="neutral" data-size="normal">Activate</button>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>next</td>
|
||||
<td data-numeric>cli</td>
|
||||
<td>npm</td>
|
||||
<td data-numeric>0.0.0-next-16018</td>
|
||||
<td data-slot="release-meta">2026-07-22T10:03:19.411Z</td>
|
||||
<td><span data-component="badge">Inactive</span></td>
|
||||
<td data-align="end">
|
||||
<button data-component="button" data-variant="neutral" data-size="normal">Activate</button>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>next</td>
|
||||
<td data-numeric>cli</td>
|
||||
<td>npm</td>
|
||||
<td data-numeric>0.0.0-next-16017</td>
|
||||
<td data-slot="release-meta">2026-07-22T09:28:50.782Z</td>
|
||||
<td><span data-component="badge">Inactive</span></td>
|
||||
<td data-align="end">
|
||||
<button data-component="button" data-variant="neutral" data-size="normal">Activate</button>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,3 @@
|
||||
@import "./tokens.css";
|
||||
@import "./base.css";
|
||||
@import "./components.css";
|
||||
@@ -0,0 +1,45 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/package.json",
|
||||
"name": "@opencode-ai/css",
|
||||
"version": "1.18.4",
|
||||
"description": "OpenCode's framework-agnostic CSS design system",
|
||||
"type": "module",
|
||||
"license": "MIT",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/anomalyco/opencode.git",
|
||||
"directory": "packages/css"
|
||||
},
|
||||
"homepage": "https://github.com/anomalyco/opencode/tree/v2/packages/css",
|
||||
"bugs": "https://github.com/anomalyco/opencode/issues",
|
||||
"keywords": [
|
||||
"css",
|
||||
"design-system",
|
||||
"opencode"
|
||||
],
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
},
|
||||
"style": "./index.css",
|
||||
"sideEffects": [
|
||||
"**/*.css"
|
||||
],
|
||||
"files": [
|
||||
"*.css",
|
||||
"components",
|
||||
"README.md",
|
||||
"LICENSE"
|
||||
],
|
||||
"exports": {
|
||||
".": "./index.css",
|
||||
"./index.css": "./index.css",
|
||||
"./base.css": "./base.css",
|
||||
"./tokens.css": "./tokens.css",
|
||||
"./components.css": "./components.css",
|
||||
"./components/*.css": "./components/*.css",
|
||||
"./package.json": "./package.json"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "bun test"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
#!/usr/bin/env bun
|
||||
|
||||
import { Script } from "@opencode-ai/script"
|
||||
import { $ } from "bun"
|
||||
import { rm } from "node:fs/promises"
|
||||
import { fileURLToPath } from "node:url"
|
||||
|
||||
process.chdir(fileURLToPath(new URL("..", import.meta.url)))
|
||||
|
||||
const pkg = (await Bun.file("package.json").json()) as { name: string; version: string }
|
||||
const tarball = `${pkg.name.replace("@", "").replace("/", "-")}-${pkg.version}.tgz`
|
||||
|
||||
if ((await $`npm view ${pkg.name}@${pkg.version} version`.nothrow()).exitCode === 0) {
|
||||
console.log(`already published ${pkg.name}@${pkg.version}`)
|
||||
process.exit(0)
|
||||
}
|
||||
|
||||
try {
|
||||
await $`bun run test`
|
||||
await $`bun pm pack`
|
||||
await $`npm publish ${tarball} --access public --tag ${Script.channel}`
|
||||
} finally {
|
||||
await rm(tarball, { force: true })
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
import { describe, expect, test } from "bun:test"
|
||||
import path from "node:path"
|
||||
|
||||
const root = path.join(import.meta.dir, "..")
|
||||
const manifest = await Bun.file(path.join(root, "package.json")).json()
|
||||
|
||||
function imports(file: string) {
|
||||
return [...file.matchAll(/@import\s+["'](.+?)["']/g)].map((match) => match[1])
|
||||
}
|
||||
|
||||
describe("package", () => {
|
||||
test("all exports resolve to files", async () => {
|
||||
const targets = Object.values(manifest.exports) as string[]
|
||||
const concrete = targets.filter((target) => !target.includes("*"))
|
||||
expect(await Promise.all(concrete.map((target) => Bun.file(path.join(root, target)).exists()))).toEqual(
|
||||
concrete.map(() => true),
|
||||
)
|
||||
})
|
||||
|
||||
test("all local CSS imports resolve", async () => {
|
||||
const files = [...new Bun.Glob("**/*.css").scanSync({ cwd: root, absolute: true })]
|
||||
const missing = (
|
||||
await Promise.all(
|
||||
files.map(async (file) =>
|
||||
Promise.all(
|
||||
imports(await Bun.file(file).text())
|
||||
.filter((target) => target.startsWith("."))
|
||||
.map(async (target) =>
|
||||
(await Bun.file(path.resolve(path.dirname(file), target)).exists()) ? undefined : target,
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
)
|
||||
.flat()
|
||||
.filter(Boolean)
|
||||
expect(missing).toEqual([])
|
||||
})
|
||||
|
||||
test("component index includes every component once", async () => {
|
||||
const expected = [...new Bun.Glob("components/*.css").scanSync({ cwd: root })].sort()
|
||||
const actual = imports(await Bun.file(path.join(root, "components.css")).text())
|
||||
.map((target) => target.replace(/^\.\//, ""))
|
||||
.sort()
|
||||
expect(actual).toEqual(expected)
|
||||
})
|
||||
|
||||
test("public CSS does not expose v2 names", async () => {
|
||||
const files = [...new Bun.Glob("**/*.css").scanSync({ cwd: root, absolute: true })]
|
||||
const stale = (
|
||||
await Promise.all(files.map(async (file) => ((await Bun.file(file).text()).includes("-v2") ? file : undefined)))
|
||||
).filter(Boolean)
|
||||
expect(stale).toEqual([])
|
||||
})
|
||||
|
||||
test("all referenced tokens are declared", async () => {
|
||||
const files = [...new Bun.Glob("**/*.css").scanSync({ cwd: root, absolute: true })]
|
||||
const css = (await Promise.all(files.map((file) => Bun.file(file).text()))).join("\n")
|
||||
const declared = new Set([...css.matchAll(/(--[\w-]+)\s*:/g)].map((match) => match[1]))
|
||||
const referenced = new Set([...css.matchAll(/var\((--[\w-]+)/g)].map((match) => match[1]))
|
||||
expect([...referenced].filter((token) => !declared.has(token)).sort()).toEqual([])
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,107 @@
|
||||
@layer theme {
|
||||
:root {
|
||||
--oc-font-family-sans: "Inter", ui-sans-serif, system-ui, sans-serif;
|
||||
}
|
||||
|
||||
:root,
|
||||
[data-color-scheme="light"] {
|
||||
color-scheme: light;
|
||||
|
||||
--oc-bg-base: var(--oc-grey-50);
|
||||
--oc-bg-deep: var(--oc-grey-100);
|
||||
--oc-bg-layer-01: var(--oc-grey-100);
|
||||
--oc-bg-layer-02: var(--oc-grey-200);
|
||||
--oc-bg-layer-03: var(--oc-grey-300);
|
||||
--oc-bg-contrast: var(--oc-grey-1000);
|
||||
--oc-bg-button-neutral: var(--oc-grey-50);
|
||||
--oc-bg-accent: var(--oc-blue-600);
|
||||
|
||||
--oc-text-base: var(--oc-grey-1100);
|
||||
--oc-text-muted: var(--oc-grey-700);
|
||||
--oc-text-faint: var(--oc-grey-600);
|
||||
--oc-text-contrast: var(--oc-grey-50);
|
||||
--oc-icon-base: var(--oc-grey-800);
|
||||
--oc-icon-muted: var(--oc-grey-600);
|
||||
|
||||
--oc-border-muted: var(--oc-alpha-dark-8);
|
||||
--oc-border-base: var(--oc-alpha-dark-10);
|
||||
--oc-border-strong: var(--oc-alpha-dark-20);
|
||||
--oc-border-focus: var(--oc-blue-500);
|
||||
|
||||
--oc-overlay-hover: var(--oc-alpha-dark-4);
|
||||
--oc-overlay-pressed: var(--oc-alpha-dark-8);
|
||||
--oc-overlay-contrast-hover: var(--oc-alpha-light-12);
|
||||
--oc-overlay-contrast-pressed: var(--oc-alpha-light-24);
|
||||
|
||||
--oc-state-bg-success: var(--oc-green-100);
|
||||
--oc-state-fg-success: var(--oc-green-800);
|
||||
--oc-state-border-success: var(--oc-green-300);
|
||||
--oc-state-bg-warning: var(--oc-yellow-100);
|
||||
--oc-state-fg-warning: var(--oc-yellow-800);
|
||||
--oc-state-border-warning: var(--oc-yellow-300);
|
||||
--oc-state-bg-danger: var(--oc-red-100);
|
||||
--oc-state-fg-danger: var(--oc-red-800);
|
||||
--oc-state-border-danger: var(--oc-red-300);
|
||||
--oc-state-bg-info: var(--oc-blue-100);
|
||||
--oc-state-fg-info: var(--oc-blue-800);
|
||||
--oc-state-border-info: var(--oc-blue-300);
|
||||
|
||||
--oc-elevation-raised:
|
||||
0 2px 4px var(--oc-alpha-dark-4), 0 1px 2px -1px var(--oc-alpha-dark-8), 0 0 0 0.5px var(--oc-alpha-dark-12);
|
||||
--oc-elevation-button-neutral: 0 1px 1.5px var(--oc-alpha-dark-10), 0 0 0 0.5px var(--oc-alpha-dark-14);
|
||||
--oc-elevation-button-contrast:
|
||||
0 1px 1.5px var(--oc-alpha-dark-20), 0 0 0 0.5px var(--oc-grey-800), inset 0 1px 2px var(--oc-alpha-light-14),
|
||||
inset 0 -1px 2px var(--oc-alpha-dark-6);
|
||||
}
|
||||
|
||||
[data-color-scheme="dark"] {
|
||||
color-scheme: dark;
|
||||
|
||||
--oc-bg-base: var(--oc-grey-1100);
|
||||
--oc-bg-deep: var(--oc-grey-1200);
|
||||
--oc-bg-layer-01: var(--oc-grey-1000);
|
||||
--oc-bg-layer-02: var(--oc-grey-900);
|
||||
--oc-bg-layer-03: var(--oc-grey-800);
|
||||
--oc-bg-contrast: var(--oc-grey-700);
|
||||
--oc-bg-button-neutral: var(--oc-alpha-light-6);
|
||||
--oc-bg-accent: var(--oc-blue-600);
|
||||
|
||||
--oc-text-base: var(--oc-grey-100);
|
||||
--oc-text-muted: var(--oc-grey-500);
|
||||
--oc-text-faint: var(--oc-grey-600);
|
||||
--oc-text-contrast: var(--oc-grey-50);
|
||||
--oc-icon-base: var(--oc-grey-400);
|
||||
--oc-icon-muted: var(--oc-grey-600);
|
||||
|
||||
--oc-border-muted: var(--oc-alpha-light-8);
|
||||
--oc-border-base: var(--oc-alpha-light-10);
|
||||
--oc-border-strong: var(--oc-alpha-light-20);
|
||||
--oc-border-focus: var(--oc-blue-500);
|
||||
|
||||
--oc-overlay-hover: var(--oc-alpha-light-6);
|
||||
--oc-overlay-pressed: var(--oc-alpha-light-10);
|
||||
--oc-overlay-contrast-hover: var(--oc-alpha-dark-24);
|
||||
--oc-overlay-contrast-pressed: var(--oc-alpha-dark-40);
|
||||
|
||||
--oc-state-bg-success: var(--oc-green-1200);
|
||||
--oc-state-fg-success: var(--oc-green-500);
|
||||
--oc-state-border-success: var(--oc-green-900);
|
||||
--oc-state-bg-warning: var(--oc-yellow-1200);
|
||||
--oc-state-fg-warning: var(--oc-yellow-500);
|
||||
--oc-state-border-warning: var(--oc-yellow-900);
|
||||
--oc-state-bg-danger: var(--oc-red-1200);
|
||||
--oc-state-fg-danger: var(--oc-red-500);
|
||||
--oc-state-border-danger: var(--oc-red-900);
|
||||
--oc-state-bg-info: var(--oc-blue-1200);
|
||||
--oc-state-fg-info: var(--oc-blue-500);
|
||||
--oc-state-border-info: var(--oc-blue-900);
|
||||
|
||||
--oc-elevation-raised:
|
||||
0 2px 4px var(--oc-alpha-dark-30), 0 1px 2px var(--oc-alpha-dark-30), 0 0 0 0.5px var(--oc-alpha-light-16),
|
||||
0 -0.5px 0 var(--oc-alpha-light-6);
|
||||
--oc-elevation-button-neutral:
|
||||
0 1px 2px var(--oc-alpha-dark-40), 0 0 0 0.5px var(--oc-alpha-light-20), 0 -0.5px 0 var(--oc-alpha-light-10);
|
||||
--oc-elevation-button-contrast:
|
||||
0 1px 2px var(--oc-alpha-dark-40), 0 0 0 0.5px var(--oc-alpha-light-40), 0 -0.5px 0 var(--oc-alpha-light-30);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
@import "./colors.css";
|
||||
@import "./theme.css";
|
||||
@@ -63,6 +63,9 @@ await $`bun ./packages/plugin/script/publish.ts`
|
||||
console.log("\n=== ui ===\n")
|
||||
await $`bun ./packages/ui/script/publish.ts`
|
||||
|
||||
console.log("\n=== css ===\n")
|
||||
await $`bun ./packages/css/script/publish.ts`
|
||||
|
||||
if (Script.release) {
|
||||
await $`bun ./packages/desktop/scripts/finalize-latest-json.ts`
|
||||
await $`bun ./packages/desktop/scripts/finalize-latest-yml.ts`
|
||||
|
||||
Reference in New Issue
Block a user