Merge pull request 'API: add abstract open_file method, implement open_file' (#1591) from hsjobeki/clan-core:hsjobeki-main into main
All checks were successful
deploy / deploy-docs (push) Successful in 20s
buildbot/nix-build .#checks.aarch64-darwin.nixos-test_install_machine Build done.
buildbot/nix-build .#checks.aarch64-darwin.nixos-test-backup Build done.
buildbot/nix-build .#checks.x86_64-linux.clan-dep-zbar Build done.
buildbot/nix-build .#checks.aarch64-darwin.nixos-iso-installer Build done.
buildbot/nix-build .#checks.aarch64-darwin.nixos-flash-installer Build done.
buildbot/nix-build .#checks.aarch64-linux.nixos-test-backup Build done.
buildbot/nix-build .#checks.aarch64-linux.nixos-test_install_machine Build done.
buildbot/nix-build .#checks.x86_64-linux.check-for-breakpoints Build done.
buildbot/nix-build .#checks.aarch64-linux.nixos-flash-installer Build done.
buildbot/nix-build .#checks.aarch64-linux.nixos-iso-installer Build done.
buildbot/nix-build .#checks.x86_64-linux.package-gui-installer-deb Build done.
buildbot/nix-build .#checks.x86_64-linux.package-gui-installer-archlinux Build done.
buildbot/nix-build .#checks.x86_64-linux.package-gui-installer-rpm Build done.
buildbot/nix-build .#checks.x86_64-linux.clan-app-pytest Build done.
buildbot/nix-build .#checks.x86_64-linux.clan-dep-e2fsprogs Build done.
buildbot/nix-build .#checks.x86_64-linux.clan-dep-fakeroot Build done.
buildbot/nix-build .#checks.x86_64-linux.clan-dep-git Build done.
buildbot/nix-build .#checks.x86_64-linux.clan-dep-nix Build done.
buildbot/nix-build .#checks.x86_64-linux.clan-dep-openssh Build done.
buildbot/nix-build .#checks.x86_64-linux."clan-dep-python3.11-mypy" Build done.
buildbot/nix-build .#checks.x86_64-linux."clan-dep-python3.11-qemu" Build done.
buildbot/nix-build .#checks.x86_64-linux.clan-dep-rsync Build done.
buildbot/nix-build .#checks.x86_64-linux.clan-dep-sops Build done.
buildbot/nix-build .#checks.x86_64-linux.clan-dep-sshpass Build done.
buildbot/nix-build .#checks.x86_64-linux.clan-dep-tor Build done.
buildbot/nix-build .#checks.x86_64-linux.container Build done.
buildbot/nix-build .#checks.x86_64-linux.lib-jsonschema-example-valid Build done.
buildbot/nix-build .#checks.x86_64-linux.devShell-default Build done.
buildbot/nix-build .#checks.x86_64-linux.devShell-clan-cli Build done.
buildbot/nix-build .#checks.x86_64-linux.clan-pytest-with-core Build done.
buildbot/nix-build .#checks.x86_64-linux.clan-pytest-without-core Build done.
buildbot/nix-build .#checks.x86_64-linux.clan-app-no-breakpoints Build done.
buildbot/nix-build .#checks.x86_64-linux.clan-dep-bash Build done.
buildbot/nix-build .#checks.x86_64-linux.clan-dep-age Build done.
buildbot/nix-build .#checks.x86_64-linux.devShell-clan-app Build done.
buildbot/nix-build .#checks.x86_64-linux.devShell-webview-ui Build done.
buildbot/nix-build .#checks.x86_64-linux.module-schema Build done.
buildbot/nix-build .#checks.x86_64-linux.package-impure-checks Build done.
buildbot/nix-build .#checks.x86_64-linux.package-merge-after-ci Build done.
buildbot/nix-build .#checks.x86_64-linux.package-module-docs Build done.
buildbot/nix-build .#checks.x86_64-linux.nixos-test-backup Build done.
buildbot/nix-build .#checks.x86_64-linux.package-deploy-docs Build done.
buildbot/nix-build .#checks.x86_64-linux.package-moonlight-sunshine-accept Build done.
buildbot/nix-build .#checks.x86_64-linux.deltachat Build done.
buildbot/nix-build .#checks.x86_64-linux.nixos-test_install_machine Build done.
buildbot/nix-build .#checks.x86_64-linux.borgbackup Build done.
buildbot/nix-build .#checks.x86_64-linux.package-clan-cli Build done.
buildbot/nix-build .#checks.x86_64-linux.package-clan-cli-docs Build done.
buildbot/nix-build .#checks.x86_64-linux.package-clan-ts-api Build done.
buildbot/nix-build .#checks.x86_64-linux.package-default Build done.
buildbot/nix-build .#checks.x86_64-linux.package-clan-app Build done.
buildbot/nix-build .#checks.x86_64-linux.devShell-docs Build done.
buildbot/nix-build .#checks.x86_64-linux.package-gui-installer-apk Build done.
buildbot/nix-build .#checks.x86_64-linux.matrix-synapse Build done.
buildbot/nix-build .#checks.x86_64-linux.package-editor Build done.
buildbot/nix-build .#checks.x86_64-linux.package-gui-install-test-ubuntu-22-04 Build done.
checks / checks-impure (push) Successful in 2m8s
buildbot/nix-build .#checks.x86_64-linux.flash Build done.
buildbot/nix-build .#checks.x86_64-linux.package-pending-reviews Build done.
buildbot/nix-build .#checks.x86_64-linux.package-webview-ui Build done.
buildbot/nix-build .#checks.x86_64-linux.package-zerotier-members Build done.
buildbot/nix-build .#checks.x86_64-linux.package-tea-create-pr Build done.
buildbot/nix-build .#checks.x86_64-linux.package-zt-tcp-relay Build done.
buildbot/nix-build .#checks.x86_64-linux.package-zerotierone Build done.
buildbot/nix-build .#checks.x86_64-linux.lib-jsonschema-nix-unit-tests Build done.
buildbot/nix-build .#checks.x86_64-linux.package-docs Build done.
buildbot/nix-build .#checks.x86_64-linux.renderClanOptions Build done.
buildbot/nix-build .#checks.x86_64-linux.treefmt Build done.
buildbot/nix-build .#checks.x86_64-linux.package-function-schema Build done.
buildbot/nix-build .#checks.x86_64-linux.package-module-schema Build done.
buildbot/nix-build .#checks.x86_64-linux.wayland-proxy-virtwl Build done.
buildbot/nix-build .#checks.x86_64-linux.nixos-iso-installer Build done.
buildbot/nix-build .#checks.x86_64-linux.nixos-flash-installer Build done.
buildbot/nix-build .#checks.x86_64-linux.secrets Build done.
buildbot/nix-build .#checks.x86_64-linux.syncthing Build done.
buildbot/nix-build .#checks.x86_64-linux.test-backups Build done.
buildbot/nix-build .#checks.x86_64-linux.package-iso-installer Build done.
buildbot/nix-build .#checks.x86_64-linux.zt-tcp-relay Build done.
buildbot/nix-build .#checks.x86_64-linux.test-installation Build done.
buildbot/nix-eval Build done.

This commit is contained in:
clan-bot 2024-06-08 15:09:35 +00:00
commit f14f7368d7
7 changed files with 1123 additions and 7 deletions

View File

@ -10,10 +10,11 @@ from typing import Any
import gi
from clan_cli.api import API
from clan_cli.api.directory import FileRequest
gi.require_version("WebKit", "6.0")
from gi.repository import GLib, WebKit
from gi.repository import Gio, GLib, Gtk, WebKit
site_index: Path = (
Path(sys.argv[0]).absolute() / Path("../..") / Path("clan_app/.webui/index.html")
@ -39,6 +40,84 @@ def dataclass_to_dict(obj: Any) -> Any:
return obj
# Implement the abstract open_file function
def open_file(file_request: FileRequest) -> str | None:
# Function to handle the response and stop the loop
selected_path = None
def on_file_select(
file_dialog: Gtk.FileDialog, task: Gio.Task, main_loop: GLib.MainLoop
) -> None:
try:
gfile = file_dialog.open_finish(task)
if gfile:
nonlocal selected_path
selected_path = gfile.get_path()
except Exception as e:
print(f"Error getting selected file or directory: {e}")
finally:
main_loop.quit()
def on_folder_select(
file_dialog: Gtk.FileDialog, task: Gio.Task, main_loop: GLib.MainLoop
) -> None:
try:
gfile = file_dialog.select_folder_finish(task)
if gfile:
nonlocal selected_path
selected_path = gfile.get_path()
except Exception as e:
print(f"Error getting selected directory: {e}")
finally:
main_loop.quit()
dialog = Gtk.FileDialog()
if file_request.title:
dialog.set_title(file_request.title)
if file_request.filters:
filters = Gio.ListStore.new(Gtk.FileFilter)
file_filters = Gtk.FileFilter()
if file_request.filters.title:
file_filters.set_name(file_request.filters.title)
# Create and configure a filter for image files
if file_request.filters.mime_types:
for mime in file_request.filters.mime_types:
file_filters.add_mime_type(mime)
filters.append(file_filters)
if file_request.filters.patterns:
for pattern in file_request.filters.patterns:
file_filters.add_pattern(pattern)
if file_request.filters.suffixes:
for suffix in file_request.filters.suffixes:
file_filters.add_suffix(suffix)
filters.append(file_filters)
dialog.set_filters(filters)
main_loop = GLib.MainLoop()
# if select_folder
if file_request.mode == "select_folder":
dialog.select_folder(
callback=lambda dialog, task: on_folder_select(dialog, task, main_loop)
)
elif file_request.mode == "open_file":
dialog.open(
callback=lambda dialog, task: on_file_select(dialog, task, main_loop)
)
# Wait for the user to select a file or directory
main_loop.run() # type: ignore
return selected_path
class WebView:
def __init__(self, methods: dict[str, Callable]) -> None:
self.method_registry: dict[str, Callable] = methods

View File

@ -12,7 +12,7 @@ from clan_app.singletons.use_vms import ClanStore
from clan_app.views.details import Details
from clan_app.views.list import ClanList
from clan_app.views.logs import Logs
from clan_app.views.webview import WebView
from clan_app.views.webview import WebView, open_file
gi.require_version("Adw", "1")
@ -51,7 +51,11 @@ class MainWindow(Adw.ApplicationWindow):
stack_view.add_named(Details(), "details")
stack_view.add_named(Logs(), "logs")
# Override platform specific functions
API.register(open_file)
webview = WebView(methods=API._registry)
stack_view.add_named(webview.get_webview(), "webview")
stack_view.set_visible_child_name(config.initial_view)

View File

@ -8,6 +8,33 @@ from clan_cli.errors import ClanError
from . import API
@dataclass
class FileFilter:
title: str | None
mime_types: list[str] | None
patterns: list[str] | None
suffixes: list[str] | None
@dataclass
class FileRequest:
# Mode of the os dialog window
mode: Literal["open_file", "select_folder"]
# Title of the os dialog window
title: str | None = None
# Pre-applied filters for the file dialog
filters: FileFilter | None = None
@API.register
def open_file(file_request: FileRequest) -> str | None:
"""
Abstract api method to open a file dialog window.
It must return the name of the selected file or None if no file was selected.
"""
raise NotImplementedError("Each specific platform should implement this function.")
@dataclass
class File:
path: str

991
pkgs/clan-cli/out.ts Normal file
View File

@ -0,0 +1,991 @@
export const schema = {
"$comment": "An object containing API methods. ",
"type": "object",
"additionalProperties": false,
"required": [
"open_file",
"get_directory",
"create_machine",
"list_machines",
"show_machine",
"create_clan"
],
"properties": {
"open_file": {
"type": "object",
"required": [
"arguments",
"return"
],
"additionalProperties": false,
"properties": {
"return": {
"oneOf": [
{
"type": "object",
"properties": {
"status": {
"type": "string",
"enum": [
"success"
],
"description": "The status of the response."
},
"data": {
"oneOf": [
{
"type": "string"
},
{
"type": "null"
}
]
}
},
"required": [
"status"
],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"status": {
"type": "string",
"enum": [
"error"
]
},
"errors": {
"type": "array",
"items": {
"type": "object",
"properties": {
"message": {
"type": "string"
},
"description": {
"oneOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"location": {
"oneOf": [
{
"type": "array",
"items": {
"type": "string"
}
},
{
"type": "null"
}
]
}
},
"required": [
"message"
],
"additionalProperties": false
}
}
},
"required": [
"status",
"errors"
],
"additionalProperties": false
}
]
},
"arguments": {
"type": "object",
"required": [
"file_request"
],
"additionalProperties": false,
"properties": {
"file_request": {
"type": "object",
"properties": {
"mode": {
"type": "string",
"enum": [
"open_file",
"select_folder"
]
},
"title": {
"oneOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"filters": {
"oneOf": [
{
"type": "object",
"properties": {
"title": {
"oneOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"mime_types": {
"oneOf": [
{
"type": "array",
"items": {
"type": "string"
}
},
{
"type": "null"
}
]
},
"patterns": {
"oneOf": [
{
"type": "array",
"items": {
"type": "string"
}
},
{
"type": "null"
}
]
},
"suffixes": {
"oneOf": [
{
"type": "array",
"items": {
"type": "string"
}
},
{
"type": "null"
}
]
}
},
"required": [],
"additionalProperties": false
},
{
"type": "null"
}
]
}
},
"required": [
"mode"
],
"additionalProperties": false
}
}
}
}
},
"get_directory": {
"type": "object",
"required": [
"arguments",
"return"
],
"additionalProperties": false,
"properties": {
"return": {
"oneOf": [
{
"type": "object",
"properties": {
"status": {
"type": "string",
"enum": [
"success"
],
"description": "The status of the response."
},
"data": {
"type": "object",
"properties": {
"path": {
"type": "string"
},
"files": {
"type": "array",
"items": {
"type": "object",
"properties": {
"path": {
"type": "string"
},
"file_type": {
"type": "string",
"enum": [
"file",
"directory",
"symlink"
]
}
},
"required": [
"path",
"file_type"
],
"additionalProperties": false
}
}
},
"required": [
"path",
"files"
],
"additionalProperties": false
}
},
"required": [
"status",
"data"
],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"status": {
"type": "string",
"enum": [
"error"
]
},
"errors": {
"type": "array",
"items": {
"type": "object",
"properties": {
"message": {
"type": "string"
},
"description": {
"oneOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"location": {
"oneOf": [
{
"type": "array",
"items": {
"type": "string"
}
},
{
"type": "null"
}
]
}
},
"required": [
"message"
],
"additionalProperties": false
}
}
},
"required": [
"status",
"errors"
],
"additionalProperties": false
}
]
},
"arguments": {
"type": "object",
"required": [
"current_path"
],
"additionalProperties": false,
"properties": {
"current_path": {
"type": "string"
}
}
}
}
},
"create_machine": {
"type": "object",
"required": [
"arguments",
"return"
],
"additionalProperties": false,
"properties": {
"return": {
"oneOf": [
{
"type": "object",
"properties": {
"status": {
"type": "string",
"enum": [
"success"
],
"description": "The status of the response."
},
"data": {
"type": "null"
}
},
"required": [
"status"
],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"status": {
"type": "string",
"enum": [
"error"
]
},
"errors": {
"type": "array",
"items": {
"type": "object",
"properties": {
"message": {
"type": "string"
},
"description": {
"oneOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"location": {
"oneOf": [
{
"type": "array",
"items": {
"type": "string"
}
},
{
"type": "null"
}
]
}
},
"required": [
"message"
],
"additionalProperties": false
}
}
},
"required": [
"status",
"errors"
],
"additionalProperties": false
}
]
},
"arguments": {
"type": "object",
"required": [
"flake_dir",
"machine"
],
"additionalProperties": false,
"properties": {
"flake_dir": {
"oneOf": [
{
"type": "string"
},
{
"type": "string"
}
]
},
"machine": {
"type": "object",
"properties": {
"name": {
"type": "string"
},
"config": {
"type": "object",
"additionalProperties": {
"type": "integer"
}
}
},
"required": [
"name",
"config"
],
"additionalProperties": false
}
}
}
}
},
"list_machines": {
"type": "object",
"required": [
"arguments",
"return"
],
"additionalProperties": false,
"properties": {
"return": {
"oneOf": [
{
"type": "object",
"properties": {
"status": {
"type": "string",
"enum": [
"success"
],
"description": "The status of the response."
},
"data": {
"type": "array",
"items": {
"type": "string"
}
}
},
"required": [
"status",
"data"
],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"status": {
"type": "string",
"enum": [
"error"
]
},
"errors": {
"type": "array",
"items": {
"type": "object",
"properties": {
"message": {
"type": "string"
},
"description": {
"oneOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"location": {
"oneOf": [
{
"type": "array",
"items": {
"type": "string"
}
},
{
"type": "null"
}
]
}
},
"required": [
"message"
],
"additionalProperties": false
}
}
},
"required": [
"status",
"errors"
],
"additionalProperties": false
}
]
},
"arguments": {
"type": "object",
"required": [
"flake_url",
"debug"
],
"additionalProperties": false,
"properties": {
"flake_url": {
"oneOf": [
{
"type": "string"
},
{
"type": "string"
}
]
},
"debug": {
"type": "boolean"
}
}
}
}
},
"show_machine": {
"type": "object",
"required": [
"arguments",
"return"
],
"additionalProperties": false,
"properties": {
"return": {
"oneOf": [
{
"type": "object",
"properties": {
"status": {
"type": "string",
"enum": [
"success"
],
"description": "The status of the response."
},
"data": {
"type": "object",
"properties": {
"machine_name": {
"type": "string"
},
"machine_description": {
"oneOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"machine_icon": {
"oneOf": [
{
"type": "string"
},
{
"type": "null"
}
]
}
},
"required": [
"machine_name"
],
"additionalProperties": false
}
},
"required": [
"status",
"data"
],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"status": {
"type": "string",
"enum": [
"error"
]
},
"errors": {
"type": "array",
"items": {
"type": "object",
"properties": {
"message": {
"type": "string"
},
"description": {
"oneOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"location": {
"oneOf": [
{
"type": "array",
"items": {
"type": "string"
}
},
{
"type": "null"
}
]
}
},
"required": [
"message"
],
"additionalProperties": false
}
}
},
"required": [
"status",
"errors"
],
"additionalProperties": false
}
]
},
"arguments": {
"type": "object",
"required": [
"flake_url",
"machine_name",
"debug"
],
"additionalProperties": false,
"properties": {
"flake_url": {
"oneOf": [
{
"type": "string"
},
{
"type": "string"
}
]
},
"machine_name": {
"type": "string"
},
"debug": {
"type": "boolean"
}
}
}
}
},
"create_clan": {
"type": "object",
"required": [
"arguments",
"return"
],
"additionalProperties": false,
"properties": {
"return": {
"oneOf": [
{
"type": "object",
"properties": {
"status": {
"type": "string",
"enum": [
"success"
],
"description": "The status of the response."
},
"data": {
"type": "object",
"properties": {
"git_init": {
"type": "object",
"properties": {
"stdout": {
"type": "string"
},
"stderr": {
"type": "string"
},
"cwd": {
"type": "string"
},
"command": {
"type": "string"
},
"returncode": {
"type": "integer"
},
"msg": {
"oneOf": [
{
"type": "string"
},
{
"type": "null"
}
]
}
},
"required": [
"stdout",
"stderr",
"cwd",
"command",
"returncode"
],
"additionalProperties": false
},
"git_add": {
"type": "object",
"properties": {
"stdout": {
"type": "string"
},
"stderr": {
"type": "string"
},
"cwd": {
"type": "string"
},
"command": {
"type": "string"
},
"returncode": {
"type": "integer"
},
"msg": {
"oneOf": [
{
"type": "string"
},
{
"type": "null"
}
]
}
},
"required": [
"stdout",
"stderr",
"cwd",
"command",
"returncode"
],
"additionalProperties": false
},
"git_config": {
"type": "object",
"properties": {
"stdout": {
"type": "string"
},
"stderr": {
"type": "string"
},
"cwd": {
"type": "string"
},
"command": {
"type": "string"
},
"returncode": {
"type": "integer"
},
"msg": {
"oneOf": [
{
"type": "string"
},
{
"type": "null"
}
]
}
},
"required": [
"stdout",
"stderr",
"cwd",
"command",
"returncode"
],
"additionalProperties": false
},
"flake_update": {
"type": "object",
"properties": {
"stdout": {
"type": "string"
},
"stderr": {
"type": "string"
},
"cwd": {
"type": "string"
},
"command": {
"type": "string"
},
"returncode": {
"type": "integer"
},
"msg": {
"oneOf": [
{
"type": "string"
},
{
"type": "null"
}
]
}
},
"required": [
"stdout",
"stderr",
"cwd",
"command",
"returncode"
],
"additionalProperties": false
}
},
"required": [
"git_init",
"git_add",
"git_config",
"flake_update"
],
"additionalProperties": false
}
},
"required": [
"status",
"data"
],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"status": {
"type": "string",
"enum": [
"error"
]
},
"errors": {
"type": "array",
"items": {
"type": "object",
"properties": {
"message": {
"type": "string"
},
"description": {
"oneOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"location": {
"oneOf": [
{
"type": "array",
"items": {
"type": "string"
}
},
{
"type": "null"
}
]
}
},
"required": [
"message"
],
"additionalProperties": false
}
}
},
"required": [
"status",
"errors"
],
"additionalProperties": false
}
]
},
"arguments": {
"type": "object",
"required": [
"directory",
"template_url"
],
"additionalProperties": false,
"properties": {
"directory": {
"type": "string"
},
"template_url": {
"type": "string"
}
}
}
}
}
}
} as const;

View File

@ -12,11 +12,12 @@ export type SuccessData<T extends OperationNames> = Extract<
{ status: "success" }
>;
export type ClanOperations = {
[K in OperationNames]: (str: string) => void;
};
declare global {
interface Window {
clan: {
[K in OperationNames]: (str: string) => void;
};
clan: ClanOperations;
webkit: {
messageHandlers: {
gtk: {
@ -29,6 +30,8 @@ declare global {
};
}
}
// Make sure window.webkit is defined although the type is not correctly filled yet.
window.clan = {} as ClanOperations;
function createFunctions<K extends OperationNames>(
operationName: K
@ -84,4 +87,11 @@ operationNames.forEach((opName) => {
pyApi[name] = createFunctions(name);
});
pyApi.open_file.receive((r) => {
const { status } = r;
if (status === "error") return console.error(r.errors);
console.log(r.data);
alert(r.data);
});
export { pyApi };

View File

@ -34,6 +34,6 @@ if (import.meta.env.DEV) {
},
};
}
postMessage;
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
render(() => <App />, root!);

View File

@ -48,7 +48,12 @@ export const MachineListView: Component = () => {
<button
class="btn btn-ghost"
onClick={() =>
pyApi.get_directory.dispatch({ current_path: "/home/" })
pyApi.open_file.dispatch({
file_request: {
title: "Open Clan",
mode: "select_folder",
},
})
}
>
<span class="material-icons ">folder_open</span>