jac-desktop 0.2.0__tar.gz

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.
Files changed (38) hide show
  1. jac_desktop-0.2.0/PKG-INFO +85 -0
  2. jac_desktop-0.2.0/README.md +64 -0
  3. jac_desktop-0.2.0/_jac_build.py +19 -0
  4. jac_desktop-0.2.0/jac.toml +46 -0
  5. jac_desktop-0.2.0/jac_desktop/_precompiled/cpython-312/config_loader.jir +0 -0
  6. jac_desktop-0.2.0/jac_desktop/_precompiled/cpython-312/native/oauth_broker.jir +0 -0
  7. jac_desktop-0.2.0/jac_desktop/_precompiled/cpython-312/native/webview/webview.na.jir +0 -0
  8. jac_desktop-0.2.0/jac_desktop/_precompiled/cpython-312/plugin.jir +0 -0
  9. jac_desktop-0.2.0/jac_desktop/_precompiled/cpython-312/plugin_config.jir +0 -0
  10. jac_desktop-0.2.0/jac_desktop/_precompiled/cpython-312/targets/native_desktop_target.jir +0 -0
  11. jac_desktop-0.2.0/jac_desktop/_precompiled/cpython-313/config_loader.jir +0 -0
  12. jac_desktop-0.2.0/jac_desktop/_precompiled/cpython-313/native/oauth_broker.jir +0 -0
  13. jac_desktop-0.2.0/jac_desktop/_precompiled/cpython-313/native/webview/webview.na.jir +0 -0
  14. jac_desktop-0.2.0/jac_desktop/_precompiled/cpython-313/plugin.jir +0 -0
  15. jac_desktop-0.2.0/jac_desktop/_precompiled/cpython-313/plugin_config.jir +0 -0
  16. jac_desktop-0.2.0/jac_desktop/_precompiled/cpython-313/targets/native_desktop_target.jir +0 -0
  17. jac_desktop-0.2.0/jac_desktop/_precompiled/cpython-314/config_loader.jir +0 -0
  18. jac_desktop-0.2.0/jac_desktop/_precompiled/cpython-314/native/oauth_broker.jir +0 -0
  19. jac_desktop-0.2.0/jac_desktop/_precompiled/cpython-314/native/webview/webview.na.jir +0 -0
  20. jac_desktop-0.2.0/jac_desktop/_precompiled/cpython-314/plugin.jir +0 -0
  21. jac_desktop-0.2.0/jac_desktop/_precompiled/cpython-314/plugin_config.jir +0 -0
  22. jac_desktop-0.2.0/jac_desktop/_precompiled/cpython-314/targets/native_desktop_target.jir +0 -0
  23. jac_desktop-0.2.0/jac_desktop/config_loader.jac +81 -0
  24. jac_desktop-0.2.0/jac_desktop/impl/config_loader.impl.jac +108 -0
  25. jac_desktop-0.2.0/jac_desktop/impl/plugin.impl.jac +8 -0
  26. jac_desktop-0.2.0/jac_desktop/native/oauth_broker.jac +248 -0
  27. jac_desktop-0.2.0/jac_desktop/native/webview/README.md +46 -0
  28. jac_desktop-0.2.0/jac_desktop/native/webview/build_libwebview.sh +69 -0
  29. jac_desktop-0.2.0/jac_desktop/native/webview/install_webkit_deps.sh +62 -0
  30. jac_desktop-0.2.0/jac_desktop/native/webview/webview.na.jac +139 -0
  31. jac_desktop-0.2.0/jac_desktop/plugin.jac +12 -0
  32. jac_desktop-0.2.0/jac_desktop/plugin_config.jac +164 -0
  33. jac_desktop-0.2.0/jac_desktop/targets/impl/native_desktop_target.impl.jac +258 -0
  34. jac_desktop-0.2.0/jac_desktop/targets/native_desktop_target.jac +43 -0
  35. jac_desktop-0.2.0/jac_desktop/tests/test_binding.jac +101 -0
  36. jac_desktop-0.2.0/jac_desktop/tests/test_native_target.jac +43 -0
  37. jac_desktop-0.2.0/jac_desktop/tests/test_oauth_broker.jac +187 -0
  38. jac_desktop-0.2.0/pyproject.toml +24 -0
@@ -0,0 +1,85 @@
1
+ Metadata-Version: 2.4
2
+ Name: jac-desktop
3
+ Version: 0.2.0
4
+ Summary: Jac-native desktop target: one nacompile'd binary + the OS webview.
5
+ Author-email: Jason Mars <jason@mars.ninja>
6
+ Maintainer-email: Jason Mars <jason@mars.ninja>
7
+ License-Expression: MIT
8
+ Keywords: jac,jaclang,jaseci,desktop,webview,native
9
+ Requires-Python: >=3.12
10
+ Project-URL: Repository, https://github.com/jaseci-labs/jaseci
11
+ Project-URL: Homepage, https://jaseci.org
12
+ Project-URL: Documentation, https://jac-lang.org
13
+ Project-URL: Source, https://github.com/jaseci-labs/jaseci/tree/main/jac-desktop
14
+ Project-URL: Issues, https://github.com/jaseci-labs/jaseci/issues
15
+ Project-URL: Changelog, https://github.com/jaseci-labs/jaseci/blob/main/docs/docs/community/release_notes/jac-desktop.md
16
+ Requires-Dist: jaclang>=0.16.1
17
+ Requires-Dist: jac-client>=0.3.22
18
+ Provides-Extra: dev
19
+ Requires-Dist: pytest==8.3.5; extra == "dev"
20
+ Description-Content-Type: text/markdown
21
+
22
+ # jac-desktop
23
+
24
+ Jac-native desktop target for [Jac](https://jac-lang.org): a desktop app is **one
25
+ `jac nacompile`d binary + the OS's own web engine** - no Rust toolchain, no
26
+ PyInstaller, no separate process.
27
+
28
+ ## What you get
29
+
30
+ - A `desktop` build target registered with `jac-client`'s target registry, so
31
+ `jac build --client desktop` and `jac start --client desktop` work once this
32
+ package is installed.
33
+ - The build pipeline:
34
+ 1. builds your `cl` codespace with the standard Vite pipeline (via `WebTarget`),
35
+ 2. compiles a native host (`na`) that embeds CPython to serve that bundle on a
36
+ loopback port and renders it in the OS-native webview (WebKitGTK on Linux /
37
+ WKWebView on macOS / WebView2 on Windows),
38
+ 3. produces a single self-contained binary under `.jac/client/desktop/`.
39
+
40
+ The native webview binding + build tooling live under
41
+ [`jac_desktop/native/webview/`](jac_desktop/native/webview/) (see its README for
42
+ the phase-by-phase design and the dependency-free test suite).
43
+
44
+ ## Install
45
+
46
+ ```sh
47
+ pip install jac-client jac-desktop
48
+ ```
49
+
50
+ Building a desktop app needs the OS web engine + a C toolchain so the native host
51
+ can link `libwebview.so` (built on first use). On Debian/Ubuntu:
52
+
53
+ ```sh
54
+ sudo ./jac_desktop/native/webview/install_webkit_deps.sh
55
+ # (build-essential, pkg-config, libgtk-3-dev, libwebkit2gtk-4.1-dev)
56
+ ```
57
+
58
+ ## Project flow
59
+
60
+ ```sh
61
+ jac create --use fullstack my-app # or any project with a cl codespace
62
+ cd my-app
63
+ jac build --client desktop # -> .jac/client/desktop/<app> (single binary)
64
+ jac start --client desktop # build + launch the native window
65
+ ```
66
+
67
+ Window geometry + app identity come from `[plugins.desktop]` in `jac.toml`:
68
+
69
+ ```toml
70
+ [plugins.desktop]
71
+ name = "my-app"
72
+
73
+ [plugins.desktop.window]
74
+ title = "My App"
75
+ width = 1000
76
+ height = 700
77
+ ```
78
+
79
+ ## Status
80
+
81
+ `jac build --client desktop` produces a working, self-contained native desktop
82
+ binary that renders your `cl` UI. The host embeds CPython (it serves the bundle
83
+ and is where `sv` runs in-process). Remaining: wiring the `sv` codespace/walkers
84
+ onto the embedded interpreter, HMR dev mode, and per-OS packaging/signing - see
85
+ [issue #6436](https://github.com/jaseci-labs/jaseci/issues/6436).
@@ -0,0 +1,64 @@
1
+ # jac-desktop
2
+
3
+ Jac-native desktop target for [Jac](https://jac-lang.org): a desktop app is **one
4
+ `jac nacompile`d binary + the OS's own web engine** - no Rust toolchain, no
5
+ PyInstaller, no separate process.
6
+
7
+ ## What you get
8
+
9
+ - A `desktop` build target registered with `jac-client`'s target registry, so
10
+ `jac build --client desktop` and `jac start --client desktop` work once this
11
+ package is installed.
12
+ - The build pipeline:
13
+ 1. builds your `cl` codespace with the standard Vite pipeline (via `WebTarget`),
14
+ 2. compiles a native host (`na`) that embeds CPython to serve that bundle on a
15
+ loopback port and renders it in the OS-native webview (WebKitGTK on Linux /
16
+ WKWebView on macOS / WebView2 on Windows),
17
+ 3. produces a single self-contained binary under `.jac/client/desktop/`.
18
+
19
+ The native webview binding + build tooling live under
20
+ [`jac_desktop/native/webview/`](jac_desktop/native/webview/) (see its README for
21
+ the phase-by-phase design and the dependency-free test suite).
22
+
23
+ ## Install
24
+
25
+ ```sh
26
+ pip install jac-client jac-desktop
27
+ ```
28
+
29
+ Building a desktop app needs the OS web engine + a C toolchain so the native host
30
+ can link `libwebview.so` (built on first use). On Debian/Ubuntu:
31
+
32
+ ```sh
33
+ sudo ./jac_desktop/native/webview/install_webkit_deps.sh
34
+ # (build-essential, pkg-config, libgtk-3-dev, libwebkit2gtk-4.1-dev)
35
+ ```
36
+
37
+ ## Project flow
38
+
39
+ ```sh
40
+ jac create --use fullstack my-app # or any project with a cl codespace
41
+ cd my-app
42
+ jac build --client desktop # -> .jac/client/desktop/<app> (single binary)
43
+ jac start --client desktop # build + launch the native window
44
+ ```
45
+
46
+ Window geometry + app identity come from `[plugins.desktop]` in `jac.toml`:
47
+
48
+ ```toml
49
+ [plugins.desktop]
50
+ name = "my-app"
51
+
52
+ [plugins.desktop.window]
53
+ title = "My App"
54
+ width = 1000
55
+ height = 700
56
+ ```
57
+
58
+ ## Status
59
+
60
+ `jac build --client desktop` produces a working, self-contained native desktop
61
+ binary that renders your `cl` UI. The host embeds CPython (it serves the bundle
62
+ and is where `sv` runs in-process). Remaining: wiring the `sv` codespace/walkers
63
+ onto the embedded interpreter, HMR dev mode, and per-OS packaging/signing - see
64
+ [issue #6436](https://github.com/jaseci-labs/jaseci/issues/6436).
@@ -0,0 +1,19 @@
1
+ """Bundled PEP 517 build backend — generated by jac bundle, do not edit."""
2
+
3
+
4
+ def get_requires_for_build_wheel(config_settings=None):
5
+ return []
6
+
7
+
8
+ def build_wheel(wheel_directory, config_settings=None, metadata_directory=None):
9
+ from pathlib import Path
10
+ from jaclang.project.config import get_config
11
+ from jaclang.publish.builder import WheelBuilder
12
+
13
+ proj_root = Path.cwd()
14
+ config = get_config(start_path=proj_root, force_discover=True)
15
+ if config is None:
16
+ raise RuntimeError(f"No jac.toml found in {proj_root}.")
17
+ return WheelBuilder(config=config, project_root=proj_root).build_to(
18
+ Path(wheel_directory)
19
+ ).name
@@ -0,0 +1,46 @@
1
+ [project]
2
+ name = "jac-desktop"
3
+ version = "0.2.0"
4
+ description = "Jac-native desktop target: one nacompile'd binary + the OS webview."
5
+ authors = [{ name = "Jason Mars", email = "jason@mars.ninja" }]
6
+ maintainers = [{ name = "Jason Mars", email = "jason@mars.ninja" }]
7
+ license = "MIT"
8
+ readme = "README.md"
9
+ keywords = ["jac", "jaclang", "jaseci", "desktop", "webview", "native"]
10
+ requires-python = ">=3.12"
11
+ entry-point = "jac_desktop/plugin.jac"
12
+
13
+ [project.urls]
14
+ repository = "https://github.com/jaseci-labs/jaseci"
15
+ homepage = "https://jaseci.org"
16
+ documentation = "https://jac-lang.org"
17
+ source = "https://github.com/jaseci-labs/jaseci/tree/main/jac-desktop"
18
+ issues = "https://github.com/jaseci-labs/jaseci/issues"
19
+ changelog = "https://github.com/jaseci-labs/jaseci/blob/main/docs/docs/community/release_notes/jac-desktop.md"
20
+
21
+ [project.include]
22
+ packages = ["jac_desktop*"]
23
+
24
+ [project.include.data]
25
+ # Ship the Jac-native webview binding + build tooling so the desktop target can
26
+ # compile a host on the user's machine (libwebview.so is built on first use).
27
+ # Explicit globs so tests/ and build caches don't end up in the wheel.
28
+ jac_desktop = [
29
+ "native/webview/webview.na.jac",
30
+ "native/webview/build_libwebview.sh",
31
+ "native/webview/install_webkit_deps.sh",
32
+ "native/webview/README.md",
33
+ ]
34
+
35
+ [dependencies]
36
+ jaclang = ">=0.16.1"
37
+ jac-client = ">=0.3.22"
38
+
39
+ [optional-dependencies.dev]
40
+ pytest = "==8.3.5"
41
+
42
+ [entrypoints.jac]
43
+ desktop_plugin_config = "jac_desktop.plugin_config:JacDesktopPluginConfig"
44
+
45
+ [entrypoints.jac_client]
46
+ desktop = "jac_desktop.plugin:JacDesktopPlugin"
@@ -0,0 +1,81 @@
1
+ """Configuration loader for jac-desktop.
2
+
3
+ Loads from jac.toml under [plugins.desktop], inheriting PluginConfigBase like
4
+ jac-scale and jac-client. Provides the app identity + window geometry the native
5
+ desktop target bakes into the generated host.
6
+ """
7
+ import from pathlib { Path }
8
+ import from jaclang.project.plugin_config { PluginConfigBase }
9
+
10
+ """Desktop app identity (name, identifier, version)."""
11
+ obj DesktopIdentity {
12
+ has name: str,
13
+ identifier: str,
14
+ version: str;
15
+ }
16
+
17
+ """Main window geometry from [plugins.desktop.window]."""
18
+ obj WindowConfig {
19
+ has title: str,
20
+ width: int = 1200,
21
+ height: int = 800,
22
+ min_width: int = 800,
23
+ min_height: int = 600,
24
+ resizable: bool = True;
25
+
26
+ static def from_dict(
27
+ data: dict[str, any], title_fallback: str = ""
28
+ ) -> WindowConfig {
29
+ title = str(data.get("title", title_fallback) or title_fallback);
30
+ return WindowConfig(
31
+ title=title,
32
+ width=int(data.get("width", 1200)),
33
+ height=int(data.get("height", 800)),
34
+ min_width=int(data.get("min_width", 800)),
35
+ min_height=int(data.get("min_height", 600)),
36
+ resizable=bool(data.get("resizable", True))
37
+ );
38
+ }
39
+ }
40
+
41
+ """Default [plugins.desktop] values before merging user jac.toml."""
42
+ obj DesktopDefaultConfig {
43
+ has name: str,
44
+ identifier: str,
45
+ version: str,
46
+ window: WindowConfig;
47
+
48
+ def to_dict -> dict[str, any] {
49
+ return {
50
+ 'name': self.name,
51
+ 'identifier': self.identifier,
52
+ 'version': self.version,
53
+ 'window': {
54
+ 'title': self.window.title,
55
+ 'width': self.window.width,
56
+ 'height': self.window.height,
57
+ 'min_width': self.window.min_width,
58
+ 'min_height': self.window.min_height,
59
+ 'resizable': self.window.resizable,
60
+ 'fullscreen': False
61
+ }
62
+ };
63
+ }
64
+ }
65
+
66
+ """Desktop plugin configuration loader."""
67
+ class JacDesktopConfig(PluginConfigBase) {
68
+ override def get_plugin_name(self: JacDesktopConfig) -> str;
69
+ override def get_default_config(self: JacDesktopConfig) -> dict[str, any];
70
+ def get_identity(self: JacDesktopConfig) -> DesktopIdentity;
71
+ def get_window_config(self: JacDesktopConfig) -> WindowConfig;
72
+ def validate(self: JacDesktopConfig) -> None;
73
+ }
74
+
75
+ glob _desktop_config_instance: JacDesktopConfig | None = None;
76
+
77
+ """Get or create the desktop config singleton."""
78
+ def get_desktop_config(project_dir: Path | None = None) -> JacDesktopConfig;
79
+
80
+ """Reset cached desktop config (for tests)."""
81
+ def reset_desktop_config -> None;
@@ -0,0 +1,108 @@
1
+ """Implementation of jac-desktop configuration loader."""
2
+ import from pathlib { Path }
3
+ import from jaclang.cli.console { console }
4
+
5
+ """Plugin section name for [plugins.desktop]."""
6
+ impl JacDesktopConfig.get_plugin_name(self: JacDesktopConfig) -> str {
7
+ return "desktop";
8
+ }
9
+
10
+ """Default [plugins.desktop] configuration."""
11
+ impl JacDesktopConfig.get_default_config(self: JacDesktopConfig) -> dict[str, any] {
12
+ project_name = "my-jac-app";
13
+ project_version = "1.0.0";
14
+ try {
15
+ jac_config = self.get_jac_config();
16
+ if jac_config and jac_config.project {
17
+ project_name = jac_config.project.name or project_name;
18
+ project_version = jac_config.project.version or project_version;
19
+ }
20
+ } except Exception {
21
+ console.warning("Failed to load JacConfig, using fallback defaults");
22
+ }
23
+ identifier = _generate_identifier(project_name);
24
+ return DesktopDefaultConfig(
25
+ name=project_name,
26
+ identifier=identifier,
27
+ version=project_version,
28
+ window=WindowConfig(title=project_name)
29
+ ).to_dict();
30
+ }
31
+
32
+ impl JacDesktopConfig.get_identity(self: JacDesktopConfig) -> DesktopIdentity {
33
+ config = self.load();
34
+ return DesktopIdentity(
35
+ name=str(config.get('name', '')),
36
+ identifier=str(config.get('identifier', '')),
37
+ version=str(config.get('version', '1.0.0'))
38
+ );
39
+ }
40
+
41
+ impl JacDesktopConfig.get_window_config(self: JacDesktopConfig) -> WindowConfig {
42
+ config = self.load();
43
+ identity = self.get_identity();
44
+ window_data: dict[str, any] = {};
45
+ raw_window = config.get('window', {});
46
+ if isinstance(raw_window, dict) {
47
+ window_data = raw_window;
48
+ }
49
+ return WindowConfig.from_dict(window_data, title_fallback=identity.name);
50
+ }
51
+
52
+ impl JacDesktopConfig.validate(self: JacDesktopConfig) -> None {
53
+ config = self.load();
54
+ name = str(config.get('name', ''));
55
+ if not name {
56
+ raise ValueError(
57
+ "Desktop configuration error: 'name' is required in [plugins.desktop]"
58
+ );
59
+ }
60
+ window = config.get('window', {});
61
+ width = window.get('width', 1200);
62
+ height = window.get('height', 800);
63
+ min_width = window.get('min_width', 800);
64
+ min_height = window.get('min_height', 600);
65
+ if min_width > width {
66
+ raise ValueError(
67
+ f"Desktop configuration error: window.min_width ({min_width}) "
68
+ f"cannot be greater than window.width ({width})"
69
+ );
70
+ }
71
+ if min_height > height {
72
+ raise ValueError(
73
+ f"Desktop configuration error: window.min_height ({min_height}) "
74
+ f"cannot be greater than window.height ({height})"
75
+ );
76
+ }
77
+ }
78
+
79
+ impl get_desktop_config(project_dir: Path | None = None) -> JacDesktopConfig {
80
+ # Module-level singleton mirrors jac-client/jac-scale; not thread-safe.
81
+ global _desktop_config_instance;
82
+ if (
83
+ _desktop_config_instance is not None
84
+ and project_dir != _desktop_config_instance.project_dir
85
+ ) {
86
+ _desktop_config_instance = None;
87
+ }
88
+ if _desktop_config_instance is None {
89
+ _desktop_config_instance = JacDesktopConfig(project_dir=project_dir);
90
+ }
91
+ return _desktop_config_instance;
92
+ }
93
+
94
+ impl reset_desktop_config -> None {
95
+ global _desktop_config_instance;
96
+ _desktop_config_instance = None;
97
+ }
98
+
99
+ def _generate_identifier(name: str) -> str {
100
+ import re;
101
+ cleaned = re.sub(r'[^a-zA-Z0-9.\-]', '', name.lower());
102
+ cleaned = re.sub(r'[.\-]+', '.', cleaned);
103
+ cleaned = cleaned.strip('.');
104
+ if not cleaned or not '.' in cleaned {
105
+ cleaned = f"com.{cleaned}" if cleaned else "com.myapp";
106
+ }
107
+ return cleaned;
108
+ }
@@ -0,0 +1,8 @@
1
+ """Implementation of jac-desktop runtime plugin hooks."""
2
+
3
+ import from jac_client.plugin.src.targets.registry { ClientTarget }
4
+ import from jac_desktop.targets.native_desktop_target { NativeDesktopTarget }
5
+
6
+ impl JacDesktopPlugin.get_client_targets -> list[ClientTarget] {
7
+ return [NativeDesktopTarget()];
8
+ }