@gtkx/css 0.9.3 → 0.10.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/README.md +55 -67
- package/dist/css.d.ts +61 -16
- package/dist/css.js +58 -16
- package/dist/style-sheet.js +0 -3
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -1,47 +1,43 @@
|
|
|
1
1
|
<p align="center">
|
|
2
|
-
<img src="https://raw.githubusercontent.com/eugeniodepalo/gtkx/
|
|
2
|
+
<img src="https://raw.githubusercontent.com/eugeniodepalo/gtkx/main/logo.svg" alt="GTKX" width="80" height="80">
|
|
3
3
|
</p>
|
|
4
4
|
|
|
5
5
|
<h1 align="center">GTKX</h1>
|
|
6
6
|
|
|
7
7
|
<p align="center">
|
|
8
|
-
<strong>Build native GTK4 desktop
|
|
8
|
+
<strong>Build native GTK4 desktop applications with React and TypeScript.</strong>
|
|
9
9
|
</p>
|
|
10
10
|
|
|
11
11
|
<p align="center">
|
|
12
|
-
<a href="https://
|
|
13
|
-
<a href="
|
|
14
|
-
<a href="
|
|
15
|
-
<a href="
|
|
12
|
+
<a href="https://www.npmjs.com/package/@gtkx/react"><img src="https://img.shields.io/npm/v/@gtkx/react.svg" alt="npm version"></a>
|
|
13
|
+
<a href="https://github.com/eugeniodepalo/gtkx/actions"><img src="https://img.shields.io/github/actions/workflow/status/eugeniodepalo/gtkx/ci.yml" alt="CI"></a>
|
|
14
|
+
<a href="https://github.com/eugeniodepalo/gtkx/blob/main/LICENSE"><img src="https://img.shields.io/badge/license-MPL--2.0-blue.svg" alt="License"></a>
|
|
15
|
+
<a href="https://github.com/eugeniodepalo/gtkx/discussions"><img src="https://img.shields.io/badge/discussions-GitHub-blue" alt="GitHub Discussions"></a>
|
|
16
16
|
</p>
|
|
17
17
|
|
|
18
18
|
---
|
|
19
19
|
|
|
20
|
-
GTKX lets you
|
|
21
|
-
|
|
22
|
-
## Features
|
|
23
|
-
|
|
24
|
-
- **React** — Hooks, state, props, and components you already know
|
|
25
|
-
- **HMR** — Edit code and see changes instantly via Vite
|
|
26
|
-
- **Native** — Direct FFI bindings to GTK4 via Rust and libffi
|
|
27
|
-
- **CLI** — `npx @gtkx/cli@latest create` scaffolds a ready-to-go project
|
|
28
|
-
- **CSS-in-JS** — Emotion-style `css` template literals for GTK styling
|
|
29
|
-
- **Testing** — Testing Library-style `screen`, `userEvent`, and queries
|
|
20
|
+
GTKX lets you write Linux desktop applications using React. Your components render as native GTK4 widgets through a Rust FFI bridge—no webviews, no Electron, just native performance with the developer experience you already know.
|
|
30
21
|
|
|
31
22
|
## Quick Start
|
|
32
23
|
|
|
33
24
|
```bash
|
|
34
|
-
npx @gtkx/cli
|
|
25
|
+
npx @gtkx/cli create my-app
|
|
35
26
|
cd my-app
|
|
36
27
|
npm run dev
|
|
37
28
|
```
|
|
38
29
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
### Example
|
|
30
|
+
## Example
|
|
42
31
|
|
|
43
32
|
```tsx
|
|
44
|
-
import {
|
|
33
|
+
import {
|
|
34
|
+
GtkApplicationWindow,
|
|
35
|
+
GtkBox,
|
|
36
|
+
GtkButton,
|
|
37
|
+
GtkLabel,
|
|
38
|
+
quit,
|
|
39
|
+
render,
|
|
40
|
+
} from "@gtkx/react";
|
|
45
41
|
import * as Gtk from "@gtkx/ffi/gtk";
|
|
46
42
|
import { useState } from "react";
|
|
47
43
|
|
|
@@ -49,67 +45,59 @@ const App = () => {
|
|
|
49
45
|
const [count, setCount] = useState(0);
|
|
50
46
|
|
|
51
47
|
return (
|
|
52
|
-
<
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
48
|
+
<GtkApplicationWindow
|
|
49
|
+
title="Counter"
|
|
50
|
+
defaultWidth={300}
|
|
51
|
+
defaultHeight={200}
|
|
52
|
+
onCloseRequest={quit}
|
|
53
|
+
>
|
|
54
|
+
<GtkBox
|
|
55
|
+
orientation={Gtk.Orientation.VERTICAL}
|
|
56
|
+
spacing={20}
|
|
57
|
+
valign={Gtk.Align.CENTER}
|
|
58
|
+
>
|
|
59
|
+
<GtkLabel label={`Count: ${count}`} cssClasses={["title-1"]} />
|
|
60
|
+
<GtkButton label="Increment" onClicked={() => setCount((c) => c + 1)} />
|
|
61
|
+
</GtkBox>
|
|
62
|
+
</GtkApplicationWindow>
|
|
58
63
|
);
|
|
59
64
|
};
|
|
60
65
|
|
|
61
|
-
render(<App />, "
|
|
66
|
+
render(<App />, "com.example.counter");
|
|
62
67
|
```
|
|
63
68
|
|
|
64
|
-
##
|
|
65
|
-
|
|
66
|
-
```tsx
|
|
67
|
-
import { css } from "@gtkx/css";
|
|
68
|
-
import { Button } from "@gtkx/react";
|
|
69
|
-
|
|
70
|
-
const primary = css`
|
|
71
|
-
padding: 16px 32px;
|
|
72
|
-
border-radius: 24px;
|
|
73
|
-
background: linear-gradient(135deg, #3584e4, #9141ac);
|
|
74
|
-
color: white;
|
|
75
|
-
`;
|
|
76
|
-
|
|
77
|
-
<Button label="Click me" cssClasses={[primary]} />;
|
|
78
|
-
```
|
|
79
|
-
|
|
80
|
-
## Testing
|
|
81
|
-
|
|
82
|
-
```tsx
|
|
83
|
-
import { cleanup, render, screen, userEvent } from "@gtkx/testing";
|
|
84
|
-
import * as Gtk from "@gtkx/ffi/gtk";
|
|
85
|
-
|
|
86
|
-
afterEach(() => cleanup());
|
|
69
|
+
## Features
|
|
87
70
|
|
|
88
|
-
|
|
89
|
-
|
|
71
|
+
- **React 19** — Hooks, concurrent features, and the component model you know
|
|
72
|
+
- **Native GTK4 widgets** — Real native controls, not web components in a webview
|
|
73
|
+
- **Adwaita support** — Modern GNOME styling with Libadwaita components
|
|
74
|
+
- **Hot Module Replacement** — Fast refresh during development
|
|
75
|
+
- **TypeScript first** — Full type safety with auto-generated bindings
|
|
76
|
+
- **CSS-in-JS styling** — Familiar styling patterns adapted for GTK
|
|
77
|
+
- **Testing utilities** — Component testing similar to Testing Library
|
|
90
78
|
|
|
91
|
-
|
|
92
|
-
name: "Increment",
|
|
93
|
-
});
|
|
79
|
+
## Examples
|
|
94
80
|
|
|
95
|
-
|
|
81
|
+
Explore complete applications in the [`examples/`](./examples) directory:
|
|
96
82
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
83
|
+
- **[gtk-demo](./examples/gtk-demo)** — Full replica of the official GTK demo app
|
|
84
|
+
- **[hello-world](./examples/hello-world)** — Minimal application showing a counter
|
|
85
|
+
- **[todo](./examples/todo)** — Full-featured todo application with Adwaita styling and testing
|
|
86
|
+
- **[deploying](./examples/deploying)** — Example of packaging and distributing a GTKX app
|
|
100
87
|
|
|
101
|
-
##
|
|
88
|
+
## Documentation
|
|
102
89
|
|
|
103
|
-
|
|
104
|
-
- GTK4 Runtime (`gtk4` on Fedora, `libgtk-4-1` on Ubuntu)
|
|
90
|
+
Visit [https://eugeniodepalo.github.io/gtkx](https://eugeniodepalo.github.io/gtkx/) for the full documentation.
|
|
105
91
|
|
|
106
92
|
## Contributing
|
|
107
93
|
|
|
108
|
-
|
|
94
|
+
Contributions are welcome! Please see the [contributing guidelines](./CONTRIBUTING.md) and check out the [good first issues](https://github.com/eugeniodepalo/gtkx/labels/good%20first%20issue).
|
|
95
|
+
|
|
96
|
+
## Community
|
|
109
97
|
|
|
110
|
-
- [
|
|
111
|
-
- [
|
|
98
|
+
- [GitHub Discussions](https://github.com/eugeniodepalo/gtkx/discussions) — Questions, ideas, and general discussion
|
|
99
|
+
- [Issue Tracker](https://github.com/eugeniodepalo/gtkx/issues) — Bug reports and feature requests
|
|
112
100
|
|
|
113
101
|
## License
|
|
114
102
|
|
|
115
|
-
[MPL-2.0](LICENSE)
|
|
103
|
+
[MPL-2.0](./LICENSE)
|
package/dist/css.d.ts
CHANGED
|
@@ -1,48 +1,93 @@
|
|
|
1
1
|
import type { CSSInterpolation } from "@emotion/serialize";
|
|
2
|
+
/**
|
|
3
|
+
* Branded type for CSS class names generated by {@link css}.
|
|
4
|
+
*/
|
|
2
5
|
type CSSClassName = string & {
|
|
3
6
|
__brand: "css";
|
|
4
7
|
};
|
|
5
8
|
/**
|
|
6
|
-
* Creates a CSS class from
|
|
7
|
-
*
|
|
9
|
+
* Creates a CSS class from style definitions.
|
|
10
|
+
*
|
|
11
|
+
* Uses Emotion's CSS-in-JS system adapted for GTK CSS. Supports nested selectors
|
|
12
|
+
* using `&` for the parent selector reference.
|
|
13
|
+
*
|
|
14
|
+
* @param args - CSS style definitions (objects, template literals, or interpolations)
|
|
15
|
+
* @returns A unique class name to use with `cssClasses` prop
|
|
8
16
|
*
|
|
9
17
|
* @example
|
|
10
18
|
* ```tsx
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
19
|
+
* import { css } from "@gtkx/css";
|
|
20
|
+
*
|
|
21
|
+
* const buttonStyle = css({
|
|
22
|
+
* padding: "8px 16px",
|
|
23
|
+
* borderRadius: "4px",
|
|
24
|
+
* "&:hover": {
|
|
25
|
+
* backgroundColor: "@accent_bg_color",
|
|
26
|
+
* },
|
|
27
|
+
* });
|
|
16
28
|
*
|
|
17
|
-
* <
|
|
29
|
+
* <GtkButton cssClasses={[buttonStyle]} label="Styled Button" />
|
|
30
|
+
* ```
|
|
31
|
+
*
|
|
32
|
+
* @example
|
|
33
|
+
* ```tsx
|
|
34
|
+
* // Template literal syntax
|
|
35
|
+
* const labelStyle = css`
|
|
36
|
+
* font-size: 14px;
|
|
37
|
+
* color: @theme_text_color;
|
|
38
|
+
* `;
|
|
18
39
|
* ```
|
|
40
|
+
*
|
|
41
|
+
* @see {@link cx} for combining class names
|
|
42
|
+
* @see {@link injectGlobal} for global styles
|
|
19
43
|
*/
|
|
20
44
|
export declare const css: (...args: CSSInterpolation[]) => CSSClassName;
|
|
21
45
|
/**
|
|
22
|
-
*
|
|
46
|
+
* Combines multiple class names into a single space-separated string.
|
|
47
|
+
*
|
|
48
|
+
* Filters out falsy values, allowing conditional class application.
|
|
49
|
+
*
|
|
50
|
+
* @param classNames - Class names, booleans, undefined, or null values
|
|
51
|
+
* @returns Space-separated string of valid class names
|
|
23
52
|
*
|
|
24
53
|
* @example
|
|
25
54
|
* ```tsx
|
|
26
|
-
*
|
|
27
|
-
*
|
|
55
|
+
* import { css, cx } from "@gtkx/css";
|
|
56
|
+
*
|
|
57
|
+
* const base = css({ padding: "8px" });
|
|
58
|
+
* const active = css({ backgroundColor: "@accent_bg_color" });
|
|
59
|
+
*
|
|
60
|
+
* <GtkButton
|
|
61
|
+
* cssClasses={[cx(base, isActive && active, "custom-class")]}
|
|
62
|
+
* label="Button"
|
|
63
|
+
* />
|
|
28
64
|
* ```
|
|
29
65
|
*/
|
|
30
66
|
export declare const cx: (...classNames: (string | boolean | undefined | null)[]) => string;
|
|
31
67
|
/**
|
|
32
|
-
* Injects global CSS styles.
|
|
33
|
-
*
|
|
68
|
+
* Injects global CSS styles without a wrapping class.
|
|
69
|
+
*
|
|
70
|
+
* Use for application-wide styles, CSS variables, or styling native GTK widgets.
|
|
71
|
+
*
|
|
72
|
+
* @param args - CSS style definitions
|
|
34
73
|
*
|
|
35
74
|
* @example
|
|
36
75
|
* ```tsx
|
|
76
|
+
* import { injectGlobal } from "@gtkx/css";
|
|
77
|
+
*
|
|
37
78
|
* injectGlobal`
|
|
38
79
|
* window {
|
|
39
|
-
* background: @theme_bg_color;
|
|
80
|
+
* background-color: @theme_bg_color;
|
|
40
81
|
* }
|
|
41
|
-
*
|
|
42
|
-
*
|
|
82
|
+
*
|
|
83
|
+
* .title-1 {
|
|
84
|
+
* font-size: 24px;
|
|
85
|
+
* font-weight: bold;
|
|
43
86
|
* }
|
|
44
87
|
* `;
|
|
45
88
|
* ```
|
|
89
|
+
*
|
|
90
|
+
* @see {@link css} for scoped class-based styles
|
|
46
91
|
*/
|
|
47
92
|
export declare const injectGlobal: (...args: CSSInterpolation[]) => void;
|
|
48
93
|
export {};
|
package/dist/css.js
CHANGED
|
@@ -51,19 +51,40 @@ function expandNestedRules(styles, className) {
|
|
|
51
51
|
return allRules.join("\n");
|
|
52
52
|
}
|
|
53
53
|
/**
|
|
54
|
-
* Creates a CSS class from
|
|
55
|
-
*
|
|
54
|
+
* Creates a CSS class from style definitions.
|
|
55
|
+
*
|
|
56
|
+
* Uses Emotion's CSS-in-JS system adapted for GTK CSS. Supports nested selectors
|
|
57
|
+
* using `&` for the parent selector reference.
|
|
58
|
+
*
|
|
59
|
+
* @param args - CSS style definitions (objects, template literals, or interpolations)
|
|
60
|
+
* @returns A unique class name to use with `cssClasses` prop
|
|
56
61
|
*
|
|
57
62
|
* @example
|
|
58
63
|
* ```tsx
|
|
59
|
-
*
|
|
60
|
-
*
|
|
61
|
-
*
|
|
62
|
-
*
|
|
63
|
-
*
|
|
64
|
+
* import { css } from "@gtkx/css";
|
|
65
|
+
*
|
|
66
|
+
* const buttonStyle = css({
|
|
67
|
+
* padding: "8px 16px",
|
|
68
|
+
* borderRadius: "4px",
|
|
69
|
+
* "&:hover": {
|
|
70
|
+
* backgroundColor: "@accent_bg_color",
|
|
71
|
+
* },
|
|
72
|
+
* });
|
|
73
|
+
*
|
|
74
|
+
* <GtkButton cssClasses={[buttonStyle]} label="Styled Button" />
|
|
75
|
+
* ```
|
|
64
76
|
*
|
|
65
|
-
*
|
|
77
|
+
* @example
|
|
78
|
+
* ```tsx
|
|
79
|
+
* // Template literal syntax
|
|
80
|
+
* const labelStyle = css`
|
|
81
|
+
* font-size: 14px;
|
|
82
|
+
* color: @theme_text_color;
|
|
83
|
+
* `;
|
|
66
84
|
* ```
|
|
85
|
+
*
|
|
86
|
+
* @see {@link cx} for combining class names
|
|
87
|
+
* @see {@link injectGlobal} for global styles
|
|
67
88
|
*/
|
|
68
89
|
export const css = (...args) => {
|
|
69
90
|
const cache = getGtkCache();
|
|
@@ -78,30 +99,51 @@ export const css = (...args) => {
|
|
|
78
99
|
return className;
|
|
79
100
|
};
|
|
80
101
|
/**
|
|
81
|
-
*
|
|
102
|
+
* Combines multiple class names into a single space-separated string.
|
|
103
|
+
*
|
|
104
|
+
* Filters out falsy values, allowing conditional class application.
|
|
105
|
+
*
|
|
106
|
+
* @param classNames - Class names, booleans, undefined, or null values
|
|
107
|
+
* @returns Space-separated string of valid class names
|
|
82
108
|
*
|
|
83
109
|
* @example
|
|
84
110
|
* ```tsx
|
|
85
|
-
*
|
|
86
|
-
*
|
|
111
|
+
* import { css, cx } from "@gtkx/css";
|
|
112
|
+
*
|
|
113
|
+
* const base = css({ padding: "8px" });
|
|
114
|
+
* const active = css({ backgroundColor: "@accent_bg_color" });
|
|
115
|
+
*
|
|
116
|
+
* <GtkButton
|
|
117
|
+
* cssClasses={[cx(base, isActive && active, "custom-class")]}
|
|
118
|
+
* label="Button"
|
|
119
|
+
* />
|
|
87
120
|
* ```
|
|
88
121
|
*/
|
|
89
122
|
export const cx = (...classNames) => classNames.filter((cn) => typeof cn === "string" && cn.length > 0).join(" ");
|
|
90
123
|
/**
|
|
91
|
-
* Injects global CSS styles.
|
|
92
|
-
*
|
|
124
|
+
* Injects global CSS styles without a wrapping class.
|
|
125
|
+
*
|
|
126
|
+
* Use for application-wide styles, CSS variables, or styling native GTK widgets.
|
|
127
|
+
*
|
|
128
|
+
* @param args - CSS style definitions
|
|
93
129
|
*
|
|
94
130
|
* @example
|
|
95
131
|
* ```tsx
|
|
132
|
+
* import { injectGlobal } from "@gtkx/css";
|
|
133
|
+
*
|
|
96
134
|
* injectGlobal`
|
|
97
135
|
* window {
|
|
98
|
-
* background: @theme_bg_color;
|
|
136
|
+
* background-color: @theme_bg_color;
|
|
99
137
|
* }
|
|
100
|
-
*
|
|
101
|
-
*
|
|
138
|
+
*
|
|
139
|
+
* .title-1 {
|
|
140
|
+
* font-size: 24px;
|
|
141
|
+
* font-weight: bold;
|
|
102
142
|
* }
|
|
103
143
|
* `;
|
|
104
144
|
* ```
|
|
145
|
+
*
|
|
146
|
+
* @see {@link css} for scoped class-based styles
|
|
105
147
|
*/
|
|
106
148
|
export const injectGlobal = (...args) => {
|
|
107
149
|
const cache = getGtkCache();
|
package/dist/style-sheet.js
CHANGED
|
@@ -13,12 +13,9 @@ const flushPendingStyles = () => {
|
|
|
13
13
|
};
|
|
14
14
|
const resetGtkState = () => {
|
|
15
15
|
isGtkReady = false;
|
|
16
|
-
// Re-register the start listener for the next start() call
|
|
17
16
|
events.once("start", flushPendingStyles);
|
|
18
17
|
};
|
|
19
|
-
// Initial registration
|
|
20
18
|
events.once("start", flushPendingStyles);
|
|
21
|
-
// Handle stop to reset state and prepare for potential restart
|
|
22
19
|
events.on("stop", resetGtkState);
|
|
23
20
|
export class StyleSheet {
|
|
24
21
|
key;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gtkx/css",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.10.0",
|
|
4
4
|
"description": "Emotion-style CSS-in-JS for GTKX applications",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"gtk",
|
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
"dependencies": {
|
|
36
36
|
"@emotion/cache": "^11.14.0",
|
|
37
37
|
"@emotion/serialize": "^1.3.3",
|
|
38
|
-
"@gtkx/ffi": "0.
|
|
38
|
+
"@gtkx/ffi": "0.10.0"
|
|
39
39
|
},
|
|
40
40
|
"scripts": {
|
|
41
41
|
"build": "tsc -b && cp ../../README.md .",
|