diff --git a/pkgs/clan-cli/clan_lib/api/directory.py b/pkgs/clan-cli/clan_lib/api/directory.py index a1b4b11ad..c48994469 100644 --- a/pkgs/clan-cli/clan_lib/api/directory.py +++ b/pkgs/clan-cli/clan_lib/api/directory.py @@ -35,6 +35,15 @@ class FileRequest: @API.register_abstract def cancel_task(task_id: str) -> None: """Cancel a task by its op_key.""" + msg = "cancel_task() is not implemented" + raise NotImplementedError(msg) + + +@API.register_abstract +def list_tasks() -> list[str]: + """List all tasks.""" + msg = "list_tasks() is not implemented" + raise NotImplementedError(msg) @API.register_abstract @@ -43,6 +52,8 @@ def open_file(file_request: FileRequest) -> list[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. """ + msg = "open_file() is not implemented" + raise NotImplementedError(msg) @dataclass diff --git a/pkgs/webview-ui/app/src/api/index.tsx b/pkgs/webview-ui/app/src/api/index.tsx index edebba2ef..7593c7caf 100644 --- a/pkgs/webview-ui/app/src/api/index.tsx +++ b/pkgs/webview-ui/app/src/api/index.tsx @@ -56,8 +56,9 @@ const _callApi = ( ) => Promise> > )[method](args) as Promise>; - const op_key = (promise as any)._webviewMessageId as string; - debugger; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const op_key = (promise as any)._webviewMessageId as string; + return { promise, op_key }; }; diff --git a/pkgs/webview-ui/app/src/components/fileSelect/index.tsx b/pkgs/webview-ui/app/src/components/fileSelect/index.tsx index 5ee02604f..d33902fb3 100644 --- a/pkgs/webview-ui/app/src/components/fileSelect/index.tsx +++ b/pkgs/webview-ui/app/src/components/fileSelect/index.tsx @@ -3,11 +3,7 @@ import { Typography } from "@/src/components/Typography"; import Fieldset from "@/src/Form/fieldset"; import Icon from "@/src/components/icon"; // For displaying file icons import { callApi } from "@/src/api"; -import type { - FieldComponent, - FieldValues, - FieldName, -} from "@modular-forms/solid"; +import type { FieldValues } from "@modular-forms/solid"; import { Show, For, type Component, type JSX } from "solid-js"; // Types for the file dialog options passed to callApi @@ -23,24 +19,23 @@ export interface FileDialogOptions { } // Props for the CustomFileField component -interface FileSelectorOpts< - TForm extends FieldValues, - TFieldName extends FieldName, -> { - Field: FieldComponent; // The Field component from createForm +interface FileSelectorOpts { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + Field: any; // The Field component from createForm name: TFieldName; // Name of the form field (e.g., "sshKeys", "profilePicture") label: string; // Legend for Fieldset or main label for the input description?: string | JSX.Element; // Optional description text multiple?: boolean; // True if multiple files can be selected, false for single file fileDialogOptions: FileDialogOptions; // Configuration for the custom file dialog - + // eslint-disable-next-line @typescript-eslint/no-explicit-any + of: any; // Optional props for styling inputClass?: string; fileListClass?: string; // You can add more specific props like `validate` if you want to pass them to Field } -export const FileSelectorField: Component> = ( +export const FileSelectorField: Component> = ( props, ) => { const { @@ -57,7 +52,7 @@ export const FileSelectorField: Component> = ( // Ref to the underlying HTMLInputElement (assuming FileInput forwards refs or is simple) let actualInputElement: HTMLInputElement | undefined; - const openAndSetFiles = async (event: MouseEvent) => { + const openAndSetFiles = async (event: Event) => { event.preventDefault(); if (!actualInputElement) { console.error( @@ -127,7 +122,10 @@ export const FileSelectorField: Component> = ( ))} - {(field, fieldProps) => ( + {( + field: { value: File | File[]; error?: string }, + fieldProps: Record, + ) => ( <> {/* This FileInput component should be clickable. @@ -135,8 +133,9 @@ export const FileSelectorField: Component> = ( If FileInput is complex, it might need an 'inputRef' prop or similar. */} { + // eslint-disable-next-line @typescript-eslint/no-explicit-any (fieldProps as any).ref(el); // Pass ref to modular-forms actualInputElement = el; // Capture for local use }} @@ -150,7 +149,7 @@ export const FileSelectorField: Component> = ( error={field.error} // Display error from modular-forms /> {field.error && ( - + {field.error} )} @@ -175,9 +174,8 @@ export const FileSelectorField: Component> = ( } > {(file) => ( -
+
- {file.name} {/* A remove button per file is complex with FileList & modular-forms. diff --git a/pkgs/webview-ui/app/src/components/toast/index.tsx b/pkgs/webview-ui/app/src/components/toast/index.tsx index 2c8177a20..745e0618d 100644 --- a/pkgs/webview-ui/app/src/components/toast/index.tsx +++ b/pkgs/webview-ui/app/src/components/toast/index.tsx @@ -107,8 +107,7 @@ const closeButtonStyle: JSX.CSSProperties = { // --- Toast Component Definitions --- // Error Toast -export interface ErrorToastProps extends BaseToastProps {} -export const ErrorToastComponent: Component = (props) => { +export const ErrorToastComponent: Component = (props) => { const handleCancelClick = () => { if (props.onCancel) { props.onCancel(); @@ -136,8 +135,7 @@ export const ErrorToastComponent: Component = (props) => { }; // Info Toast -export interface InfoToastProps extends BaseToastProps {} -export const InfoToastComponent: Component = (props) => { +export const InfoToastComponent: Component = (props) => { const handleCancelClick = () => { if (props.onCancel) { props.onCancel(); @@ -165,8 +163,7 @@ export const InfoToastComponent: Component = (props) => { }; // Warning Toast -export interface WarningToastProps extends BaseToastProps {} -export const WarningToastComponent: Component = (props) => { +export const WarningToastComponent: Component = (props) => { const handleCancelClick = () => { if (props.onCancel) { props.onCancel(); diff --git a/pkgs/webview-ui/app/src/routes/flash/view.tsx b/pkgs/webview-ui/app/src/routes/flash/view.tsx index 694383c2a..000489b08 100644 --- a/pkgs/webview-ui/app/src/routes/flash/view.tsx +++ b/pkgs/webview-ui/app/src/routes/flash/view.tsx @@ -243,6 +243,7 @@ export const Flash = () => { description="Provide your SSH public key(s) for secure, passwordless connections. (.pub files)" multiple={true} // Allow multiple SSH keys fileDialogOptions={sshKeyDialogOptions} + of={Array} // You could add custom validation via modular-forms 'validate' prop on CustomFileField if needed // e.g. validate={[required("At least one SSH key is required.")]} // This would require CustomFileField to accept and pass `validate` to its internal `Field`. diff --git a/pkgs/webview-ui/app/src/routes/machines/details.tsx b/pkgs/webview-ui/app/src/routes/machines/details.tsx index 4f7ebc742..28f54f6f4 100644 --- a/pkgs/webview-ui/app/src/routes/machines/details.tsx +++ b/pkgs/webview-ui/app/src/routes/machines/details.tsx @@ -623,10 +623,11 @@ const MachineForm = (props: MachineDetailsProps) => { } + multiple={true} name="sshKeys" // Corresponds to FlashFormValues.sshKeys label="SSH Private Key" description="Provide your SSH private key for secure, passwordless connections." - multiple={false} fileDialogOptions={ { title: "Select SSH Keys", diff --git a/pkgs/webview-ui/app/src/routes/machines/install/hardware-step.tsx b/pkgs/webview-ui/app/src/routes/machines/install/hardware-step.tsx index b940f99b6..3f267b0f2 100644 --- a/pkgs/webview-ui/app/src/routes/machines/install/hardware-step.tsx +++ b/pkgs/webview-ui/app/src/routes/machines/install/hardware-step.tsx @@ -127,10 +127,11 @@ export const HWStep = (props: StepProps) => {