Fix nix run .#clan-app
This commit is contained in:
@@ -5,17 +5,21 @@ from clan_cli.profiler import profile
|
|||||||
|
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
from clan_cli.custom_logger import setup_logging
|
|
||||||
from clan_app.deps.webview.webview import Webview
|
|
||||||
from pathlib import Path
|
|
||||||
import os
|
|
||||||
import argparse
|
import argparse
|
||||||
from urllib.parse import quote
|
import os
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
from clan_cli.custom_logger import setup_logging
|
||||||
|
|
||||||
|
from clan_app.deps.webview.webview import Webview
|
||||||
|
|
||||||
|
|
||||||
@profile
|
@profile
|
||||||
def main(argv: list[str] = sys.argv) -> int:
|
def main(argv: list[str] = sys.argv) -> int:
|
||||||
parser = argparse.ArgumentParser(description="Clan App")
|
parser = argparse.ArgumentParser(description="Clan App")
|
||||||
parser.add_argument("--content-uri", type=str, help="The URI of the content to display")
|
parser.add_argument(
|
||||||
|
"--content-uri", type=str, help="The URI of the content to display"
|
||||||
|
)
|
||||||
parser.add_argument("--debug", action="store_true", help="Enable debug mode")
|
parser.add_argument("--debug", action="store_true", help="Enable debug mode")
|
||||||
args = parser.parse_args(argv[1:])
|
args = parser.parse_args(argv[1:])
|
||||||
|
|
||||||
@@ -36,16 +40,6 @@ def main(argv: list[str] = sys.argv) -> int:
|
|||||||
site_index: Path = Path(os.getenv("WEBUI_PATH", ".")).resolve() / "index.html"
|
site_index: Path = Path(os.getenv("WEBUI_PATH", ".")).resolve() / "index.html"
|
||||||
content_uri = f"file://{site_index}"
|
content_uri = f"file://{site_index}"
|
||||||
|
|
||||||
html = """
|
|
||||||
<html>
|
|
||||||
<body>
|
|
||||||
<h1>Hello from Python Webview!</h1>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
"""
|
|
||||||
|
|
||||||
webview = Webview()
|
webview = Webview()
|
||||||
webview.navigate(f"data:text/html,{quote(html)}")
|
webview.navigate(content_uri)
|
||||||
#webview.navigate(content_uri)
|
|
||||||
webview.run()
|
webview.run()
|
||||||
|
|
||||||
|
|||||||
@@ -1,19 +1,20 @@
|
|||||||
import ctypes
|
import ctypes
|
||||||
import sys
|
import ctypes.util
|
||||||
import os
|
import os
|
||||||
import platform
|
import platform
|
||||||
import urllib.request
|
from ctypes import CFUNCTYPE, c_char_p, c_int, c_void_p
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from ctypes import c_int, c_char_p, c_void_p, CFUNCTYPE
|
|
||||||
import ctypes.util
|
|
||||||
|
|
||||||
def _encode_c_string(s: str) -> bytes:
|
def _encode_c_string(s: str) -> bytes:
|
||||||
return s.encode("utf-8")
|
return s.encode("utf-8")
|
||||||
|
|
||||||
|
|
||||||
def _get_webview_version():
|
def _get_webview_version():
|
||||||
"""Get webview version from environment variable or use default"""
|
"""Get webview version from environment variable or use default"""
|
||||||
return os.getenv("WEBVIEW_VERSION", "0.8.1")
|
return os.getenv("WEBVIEW_VERSION", "0.8.1")
|
||||||
|
|
||||||
|
|
||||||
def _get_lib_names():
|
def _get_lib_names():
|
||||||
"""Get platform-specific library names."""
|
"""Get platform-specific library names."""
|
||||||
system = platform.system().lower()
|
system = platform.system().lower()
|
||||||
@@ -22,22 +23,25 @@ def _get_lib_names():
|
|||||||
if system == "windows":
|
if system == "windows":
|
||||||
if machine == "amd64" or machine == "x86_64":
|
if machine == "amd64" or machine == "x86_64":
|
||||||
return ["webview.dll", "WebView2Loader.dll"]
|
return ["webview.dll", "WebView2Loader.dll"]
|
||||||
elif machine == "arm64":
|
if machine == "arm64":
|
||||||
raise Exception("arm64 is not supported on Windows")
|
msg = "arm64 is not supported on Windows"
|
||||||
elif system == "darwin":
|
raise Exception(msg)
|
||||||
|
return None
|
||||||
|
if system == "darwin":
|
||||||
if machine == "arm64":
|
if machine == "arm64":
|
||||||
return ["libwebview.aarch64.dylib"]
|
return ["libwebview.aarch64.dylib"]
|
||||||
else:
|
|
||||||
return ["libwebview.x86_64.dylib"]
|
return ["libwebview.x86_64.dylib"]
|
||||||
else: # linux
|
# linux
|
||||||
return ["libwebview.so"]
|
return ["libwebview.so"]
|
||||||
|
|
||||||
|
|
||||||
def _be_sure_libraries():
|
def _be_sure_libraries():
|
||||||
"""Ensure libraries exist and return paths."""
|
"""Ensure libraries exist and return paths."""
|
||||||
|
|
||||||
lib_dir = os.environ.get("WEBVIEW_LIB_DIR")
|
lib_dir = os.environ.get("WEBVIEW_LIB_DIR")
|
||||||
if not lib_dir:
|
if not lib_dir:
|
||||||
raise RuntimeError("WEBVIEW_LIB_DIR environment variable is not set")
|
msg = "WEBVIEW_LIB_DIR environment variable is not set"
|
||||||
|
raise RuntimeError(msg)
|
||||||
lib_dir = Path(lib_dir)
|
lib_dir = Path(lib_dir)
|
||||||
lib_names = _get_lib_names()
|
lib_names = _get_lib_names()
|
||||||
lib_paths = [lib_dir / lib_name for lib_name in lib_names]
|
lib_paths = [lib_dir / lib_name for lib_name in lib_names]
|
||||||
@@ -46,10 +50,11 @@ def _be_sure_libraries():
|
|||||||
missing_libs = [path for path in lib_paths if not path.exists()]
|
missing_libs = [path for path in lib_paths if not path.exists()]
|
||||||
if not missing_libs:
|
if not missing_libs:
|
||||||
return lib_paths
|
return lib_paths
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
class _WebviewLibrary:
|
class _WebviewLibrary:
|
||||||
def __init__(self):
|
def __init__(self) -> None:
|
||||||
lib_names = _get_lib_names()
|
lib_names = _get_lib_names()
|
||||||
try:
|
try:
|
||||||
library_path = ctypes.util.find_library(lib_names[0])
|
library_path = ctypes.util.find_library(lib_names[0])
|
||||||
@@ -99,4 +104,5 @@ class _WebviewLibrary:
|
|||||||
|
|
||||||
self.CFUNCTYPE = CFUNCTYPE
|
self.CFUNCTYPE = CFUNCTYPE
|
||||||
|
|
||||||
|
|
||||||
_webview_lib = _WebviewLibrary()
|
_webview_lib = _WebviewLibrary()
|
||||||
|
|||||||
@@ -1,8 +1,11 @@
|
|||||||
from enum import IntEnum
|
|
||||||
from typing import Optional, Callable, Any
|
|
||||||
import json
|
|
||||||
import ctypes
|
import ctypes
|
||||||
from ._webview_ffi import _webview_lib, _encode_c_string
|
import json
|
||||||
|
from collections.abc import Callable
|
||||||
|
from enum import IntEnum
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
|
from ._webview_ffi import _encode_c_string, _webview_lib
|
||||||
|
|
||||||
|
|
||||||
class SizeHint(IntEnum):
|
class SizeHint(IntEnum):
|
||||||
NONE = 0
|
NONE = 0
|
||||||
@@ -10,14 +13,18 @@ class SizeHint(IntEnum):
|
|||||||
MAX = 2
|
MAX = 2
|
||||||
FIXED = 3
|
FIXED = 3
|
||||||
|
|
||||||
|
|
||||||
class Size:
|
class Size:
|
||||||
def __init__(self, width: int, height: int, hint: SizeHint):
|
def __init__(self, width: int, height: int, hint: SizeHint) -> None:
|
||||||
self.width = width
|
self.width = width
|
||||||
self.height = height
|
self.height = height
|
||||||
self.hint = hint
|
self.hint = hint
|
||||||
|
|
||||||
|
|
||||||
class Webview:
|
class Webview:
|
||||||
def __init__(self, debug: bool = False, size: Optional[Size] = None, window: Optional[int] = None):
|
def __init__(
|
||||||
|
self, debug: bool = False, size: Size | None = None, window: int | None = None
|
||||||
|
) -> None:
|
||||||
self._handle = _webview_lib.webview_create(int(debug), window)
|
self._handle = _webview_lib.webview_create(int(debug), window)
|
||||||
self._callbacks = {}
|
self._callbacks = {}
|
||||||
|
|
||||||
@@ -29,8 +36,10 @@ class Webview:
|
|||||||
return self._size
|
return self._size
|
||||||
|
|
||||||
@size.setter
|
@size.setter
|
||||||
def size(self, value: Size):
|
def size(self, value: Size) -> None:
|
||||||
_webview_lib.webview_set_size(self._handle, value.width, value.height, value.hint)
|
_webview_lib.webview_set_size(
|
||||||
|
self._handle, value.width, value.height, value.hint
|
||||||
|
)
|
||||||
self._size = value
|
self._size = value
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@@ -38,26 +47,26 @@ class Webview:
|
|||||||
return self._title
|
return self._title
|
||||||
|
|
||||||
@title.setter
|
@title.setter
|
||||||
def title(self, value: str):
|
def title(self, value: str) -> None:
|
||||||
_webview_lib.webview_set_title(self._handle, _encode_c_string(value))
|
_webview_lib.webview_set_title(self._handle, _encode_c_string(value))
|
||||||
self._title = value
|
self._title = value
|
||||||
|
|
||||||
def destroy(self):
|
def destroy(self) -> None:
|
||||||
for name in list(self._callbacks.keys()):
|
for name in list(self._callbacks.keys()):
|
||||||
self.unbind(name)
|
self.unbind(name)
|
||||||
_webview_lib.webview_terminate(self._handle)
|
_webview_lib.webview_terminate(self._handle)
|
||||||
_webview_lib.webview_destroy(self._handle)
|
_webview_lib.webview_destroy(self._handle)
|
||||||
self._handle = None
|
self._handle = None
|
||||||
|
|
||||||
def navigate(self, url: str):
|
def navigate(self, url: str) -> None:
|
||||||
_webview_lib.webview_navigate(self._handle, _encode_c_string(url))
|
_webview_lib.webview_navigate(self._handle, _encode_c_string(url))
|
||||||
|
|
||||||
def run(self):
|
def run(self) -> None:
|
||||||
_webview_lib.webview_run(self._handle)
|
_webview_lib.webview_run(self._handle)
|
||||||
self.destroy()
|
self.destroy()
|
||||||
|
|
||||||
def bind(self, name: str, callback: Callable[..., Any]):
|
def bind(self, name: str, callback: Callable[..., Any]) -> None:
|
||||||
def wrapper(seq: bytes, req: bytes, arg: int):
|
def wrapper(seq: bytes, req: bytes, arg: int) -> None:
|
||||||
args = json.loads(req.decode())
|
args = json.loads(req.decode())
|
||||||
try:
|
try:
|
||||||
result = callback(*args)
|
result = callback(*args)
|
||||||
@@ -67,24 +76,31 @@ class Webview:
|
|||||||
success = False
|
success = False
|
||||||
self.return_(seq.decode(), 0 if success else 1, json.dumps(result))
|
self.return_(seq.decode(), 0 if success else 1, json.dumps(result))
|
||||||
|
|
||||||
c_callback = _webview_lib.CFUNCTYPE(None, ctypes.c_char_p, ctypes.c_char_p, ctypes.c_void_p)(wrapper)
|
c_callback = _webview_lib.CFUNCTYPE(
|
||||||
|
None, ctypes.c_char_p, ctypes.c_char_p, ctypes.c_void_p
|
||||||
|
)(wrapper)
|
||||||
self._callbacks[name] = c_callback
|
self._callbacks[name] = c_callback
|
||||||
_webview_lib.webview_bind(self._handle, _encode_c_string(name), c_callback, None)
|
_webview_lib.webview_bind(
|
||||||
|
self._handle, _encode_c_string(name), c_callback, None
|
||||||
|
)
|
||||||
|
|
||||||
def unbind(self, name: str):
|
def unbind(self, name: str) -> None:
|
||||||
if name in self._callbacks:
|
if name in self._callbacks:
|
||||||
_webview_lib.webview_unbind(self._handle, _encode_c_string(name))
|
_webview_lib.webview_unbind(self._handle, _encode_c_string(name))
|
||||||
del self._callbacks[name]
|
del self._callbacks[name]
|
||||||
|
|
||||||
def return_(self, seq: str, status: int, result: str):
|
def return_(self, seq: str, status: int, result: str) -> None:
|
||||||
_webview_lib.webview_return(self._handle, _encode_c_string(seq), status, _encode_c_string(result))
|
_webview_lib.webview_return(
|
||||||
|
self._handle, _encode_c_string(seq), status, _encode_c_string(result)
|
||||||
|
)
|
||||||
|
|
||||||
def eval(self, source: str):
|
def eval(self, source: str) -> None:
|
||||||
_webview_lib.webview_eval(self._handle, _encode_c_string(source))
|
_webview_lib.webview_eval(self._handle, _encode_c_string(source))
|
||||||
|
|
||||||
def init(self, source: str):
|
def init(self, source: str) -> None:
|
||||||
_webview_lib.webview_init(self._handle, _encode_c_string(source))
|
_webview_lib.webview_init(self._handle, _encode_c_string(source))
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
wv = Webview()
|
wv = Webview()
|
||||||
wv.title = "Hello, World!"
|
wv.title = "Hello, World!"
|
||||||
|
|||||||
@@ -19,6 +19,7 @@
|
|||||||
pytest-xdist, # Run tests in parallel on multiple cores
|
pytest-xdist, # Run tests in parallel on multiple cores
|
||||||
pytest-timeout, # Add timeouts to your tests
|
pytest-timeout, # Add timeouts to your tests
|
||||||
webview-ui,
|
webview-ui,
|
||||||
|
webview-lib,
|
||||||
fontconfig,
|
fontconfig,
|
||||||
}:
|
}:
|
||||||
let
|
let
|
||||||
@@ -48,7 +49,7 @@ let
|
|||||||
|
|
||||||
# Runtime binary dependencies required by the application
|
# Runtime binary dependencies required by the application
|
||||||
runtimeDependencies = [
|
runtimeDependencies = [
|
||||||
|
webview-lib
|
||||||
];
|
];
|
||||||
|
|
||||||
# Dependencies required for running tests
|
# Dependencies required for running tests
|
||||||
@@ -77,10 +78,9 @@ python3.pkgs.buildPythonApplication rec {
|
|||||||
dontWrapGApps = true;
|
dontWrapGApps = true;
|
||||||
preFixup = ''
|
preFixup = ''
|
||||||
makeWrapperArgs+=(
|
makeWrapperArgs+=(
|
||||||
# Use software rendering for webkit, mesa causes random crashes with css.
|
|
||||||
--set WEBKIT_DISABLE_COMPOSITING_MODE 1
|
|
||||||
--set FONTCONFIG_FILE ${fontconfig.out}/etc/fonts/fonts.conf
|
--set FONTCONFIG_FILE ${fontconfig.out}/etc/fonts/fonts.conf
|
||||||
--set WEBUI_PATH "$out/${python3.sitePackages}/clan_app/.webui"
|
--set WEBUI_PATH "$out/${python3.sitePackages}/clan_app/.webui"
|
||||||
|
--set WEBVIEW_LIB_DIR "${webview-lib}/lib"
|
||||||
# This prevents problems with mixed glibc versions that might occur when the
|
# This prevents problems with mixed glibc versions that might occur when the
|
||||||
# cli is called through a browser built against another glibc
|
# cli is called through a browser built against another glibc
|
||||||
--unset LD_LIBRARY_PATH
|
--unset LD_LIBRARY_PATH
|
||||||
|
|||||||
@@ -14,11 +14,11 @@
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
devShells.clan-app = pkgs.callPackage ./shell.nix {
|
devShells.clan-app = pkgs.callPackage ./shell.nix {
|
||||||
inherit (config.packages) clan-app webview-wrapper;
|
inherit (config.packages) clan-app webview-lib;
|
||||||
inherit self';
|
inherit self';
|
||||||
};
|
};
|
||||||
packages.clan-app = pkgs.python3.pkgs.callPackage ./default.nix {
|
packages.clan-app = pkgs.python3.pkgs.callPackage ./default.nix {
|
||||||
inherit (config.packages) clan-cli webview-ui;
|
inherit (config.packages) clan-cli webview-ui webview-lib;
|
||||||
};
|
};
|
||||||
|
|
||||||
checks = config.packages.clan-app.tests;
|
checks = config.packages.clan-app.tests;
|
||||||
|
|||||||
@@ -12,7 +12,7 @@
|
|||||||
python3,
|
python3,
|
||||||
gtk4,
|
gtk4,
|
||||||
libadwaita,
|
libadwaita,
|
||||||
webview-wrapper,
|
webview-lib,
|
||||||
clang,
|
clang,
|
||||||
self',
|
self',
|
||||||
}:
|
}:
|
||||||
@@ -39,8 +39,8 @@ mkShell {
|
|||||||
ruff
|
ruff
|
||||||
gtk4
|
gtk4
|
||||||
clang
|
clang
|
||||||
webview-wrapper.dev
|
webview-lib.dev
|
||||||
webview-wrapper
|
webview-lib
|
||||||
gtk4.dev # has the demo called 'gtk4-widget-factory'
|
gtk4.dev # has the demo called 'gtk4-widget-factory'
|
||||||
libadwaita.devdoc # has the demo called 'adwaita-1-demo'
|
libadwaita.devdoc # has the demo called 'adwaita-1-demo'
|
||||||
]
|
]
|
||||||
@@ -68,6 +68,6 @@ mkShell {
|
|||||||
export XDG_DATA_DIRS=${gtk4}/share/gsettings-schemas/gtk4-4.14.4:$XDG_DATA_DIRS
|
export XDG_DATA_DIRS=${gtk4}/share/gsettings-schemas/gtk4-4.14.4:$XDG_DATA_DIRS
|
||||||
export XDG_DATA_DIRS=${gsettings-desktop-schemas}/share/gsettings-schemas/gsettings-desktop-schemas-46.0:$XDG_DATA_DIRS
|
export XDG_DATA_DIRS=${gsettings-desktop-schemas}/share/gsettings-schemas/gsettings-desktop-schemas-46.0:$XDG_DATA_DIRS
|
||||||
|
|
||||||
export WEBVIEW_LIB_DIR=${webview-wrapper}/lib
|
export WEBVIEW_LIB_DIR=${webview-lib}/lib
|
||||||
'';
|
'';
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,7 +33,7 @@
|
|||||||
editor = pkgs.callPackage ./editor/clan-edit-codium.nix { };
|
editor = pkgs.callPackage ./editor/clan-edit-codium.nix { };
|
||||||
classgen = pkgs.callPackage ./classgen { };
|
classgen = pkgs.callPackage ./classgen { };
|
||||||
zerotierone = pkgs.callPackage ./zerotierone { };
|
zerotierone = pkgs.callPackage ./zerotierone { };
|
||||||
webview-wrapper = pkgs.callPackage ./webview-wrapper { };
|
webview-lib = pkgs.callPackage ./webview-lib { };
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,7 +11,10 @@ pkgs.stdenv.mkDerivation {
|
|||||||
sha256 = "sha256-5R8kllvP2EBuDANIl07fxv/EcbPpYgeav8Wfz7Kt13c=";
|
sha256 = "sha256-5R8kllvP2EBuDANIl07fxv/EcbPpYgeav8Wfz7Kt13c=";
|
||||||
};
|
};
|
||||||
|
|
||||||
outputs = [ "out" "dev" ];
|
outputs = [
|
||||||
|
"out"
|
||||||
|
"dev"
|
||||||
|
];
|
||||||
|
|
||||||
# Dependencies used during the build process, if any
|
# Dependencies used during the build process, if any
|
||||||
buildInputs = with pkgs; [
|
buildInputs = with pkgs; [
|
||||||
Reference in New Issue
Block a user