diff --git a/docs/advanced-apis/action-stack.md b/docs/advanced-apis/action-stack.md index be4276a..54e9a8c 100644 --- a/docs/advanced-apis/action-stack.md +++ b/docs/advanced-apis/action-stack.md @@ -1,3 +1,8 @@ +--- +title: Action Stack +description: Control what the Android back button does. +--- + # Action Stack The Action Stack is a crucial component for managing back button behavior in Acode. It allows you to handle navigation and state management by maintaining a stack of actions that can be executed when users press the back button. diff --git a/docs/advanced-apis/executor.md b/docs/advanced-apis/executor.md index c980ca2..0f20365 100644 --- a/docs/advanced-apis/executor.md +++ b/docs/advanced-apis/executor.md @@ -1,3 +1,8 @@ +--- +title: Executor +description: Run shell commands without opening a terminal. +--- + # Executor The `Executor` API lets you run shell commands on the device without opening a visual terminal session. It supports one-off commands, long-running processes with real-time streaming, stdin writes, and background execution via a foreground service. diff --git a/docs/advanced-apis/lsp.md b/docs/advanced-apis/lsp.md index 9f720aa..e7ad4ad 100644 --- a/docs/advanced-apis/lsp.md +++ b/docs/advanced-apis/lsp.md @@ -1,3 +1,8 @@ +--- +title: LSP +description: Register language servers for CodeMirror LSP support. +--- + # LSP API Use the LSP API to register language servers for Acode's CodeMirror LSP integration. diff --git a/docs/global-apis/acode.md b/docs/global-apis/acode.md index 668fba8..d752624 100644 --- a/docs/global-apis/acode.md +++ b/docs/global-apis/acode.md @@ -82,7 +82,7 @@ Set exactly one of the following to choose how the user edits the item: | `select` | `Array` | A [select](../ui-components/dialogs/select.md) dialog. | | `prompt` | `string` | A [prompt](../ui-components/dialogs/prompt.md) with this text as the message. | | `promptType` | `string` | Input type of that prompt (default `text`). Only with `prompt`. | -| `promptOptions` | `object` | [Prompt options](../ui-components/dialogs/prompt.md#promptoptions) such as `match`, `required`, `placeholder` and `test`. Only with `prompt`. | +| `promptOptions` | `object` | [Prompt options](../ui-components/dialogs/prompt.md#options) such as `match`, `required`, `placeholder` and `test`. Only with `prompt`. | | `color` | `boolean` | A [color picker](../ui-components/dialogs/color-picker.md). | | `file` / `folder` | `boolean` | The file browser, in file or folder mode. The value is the chosen URL. | | `link` | `string` | Opens this URL in the browser. `cb` is not called. | diff --git a/docs/helpers/fonts.md b/docs/helpers/fonts.md index 637d4f8..25480bd 100644 --- a/docs/helpers/fonts.md +++ b/docs/helpers/fonts.md @@ -1,45 +1,159 @@ -# Acode Fonts Module +--- +title: Fonts +description: Register, remove and apply editor and app fonts from a plugin. +--- -A straightforward API for managing fonts in your Acode project. +# Fonts -## Quick Start +The `fonts` module manages the fonts Acode can use for the editor and the app UI. Plugins use it to register extra fonts, so they appear in the **Editor font**, **App font** and terminal font pickers and in the Font Manager, and to apply a font programmatically. -```javascript -const fonts = acode.require('fonts'); +```js +const fonts = acode.require("fonts"); ``` -## Core Methods +A font is stored as a name plus a CSS `@font-face` declaration. Acode ships with several built-in fonts (for example `Roboto Mono`, `Fira Code`, `JetBrains Mono Regular`). + +## Methods + +| Method | Returns | Description | +| --- | --- | --- | +| [`add(name, css)`](#add-name-css) | `void` | Register a font for this session. | +| [`addCustom(name, css)`](#addcustom-name-css) | `void` | Register a font and persist it across restarts. | +| [`get(name)`](#get-name) | `string \| undefined` | Get the `@font-face` CSS of a font. | +| [`getNames()`](#getnames) | `string[]` | List every registered font name. | +| [`has(name)`](#has-name) | `boolean` | Check whether a font is registered. | +| [`isCustom(name)`](#iscustom-name) | `boolean` | Check whether a font was added with `addCustom`. | +| [`remove(name)`](#remove-name) | `boolean` | Unregister a font. | +| [`setEditorFont(name)`](#seteditorfont-name) | `Promise` | Apply a font to the editor. | +| [`setAppFont(name?)`](#setappfont-name) | `Promise` | Apply a font to the app UI. | +| [`loadFont(name)`](#loadfont-name) | `Promise` | Load a font's files and inject its `@font-face`. | ### `add(name, css)` -Adds a new font to your project. -**Parameters:** -- `name`: Unique identifier for the font -- `css`: CSS `@font-face` declaration +Registers a font in memory. It is gone after Acode restarts, so call it from your plugin's `init` every time. + +**Parameters** -**Example:** -```javascript +- `name` (`string`): unique font name. Registering an existing name replaces it. +- `css` (`string`): a complete `@font-face` declaration. The `font-family` inside it should match `name`. + +```js fonts.add( - 'Developer Mono', + "Developer Mono", `@font-face { font-family: 'Developer Mono'; - src: url('/fonts/devmono.woff2') format('woff2'); + src: url(https://example.com/devmono.woff2) format('woff2'); font-weight: 400; - }` + }`, ); ``` +::: tip Remote fonts are cached +When a font is applied, every **unquoted** `http(s)://` URL in its `src` (except `localhost`) is downloaded once and stored in Acode's data directory under `fonts/`. Later loads use the local copy, so the font works offline. + +- Write remote URLs without quotes: `url(https://...)`. A quoted URL such as `url('https://...')` is not cached and is always loaded from the network. +- The cached file is named after the font, so use one remote URL per font. If a font lists several remote URLs (for example, one per weight), all of them load the first downloaded file. +::: + +### `addCustom(name, css)` + +Same as `add`, but the font is also saved to storage and restored on the next launch. This is what the Font Manager uses when a user adds a font. + +```js +fonts.addCustom("Developer Mono", css); +``` + +::: warning +A plugin that calls `addCustom` should also call `remove` in its unmount handler if the font must disappear when the plugin is uninstalled. Otherwise the font stays registered. +::: + ### `get(name)` -Retrieves a specific font's details. -**Returns:** Font object with `name` and `css` properties +Returns the stored CSS string, or `undefined` if no font has that name. -```javascript -const font = fonts.get('Developer Mono'); +```js +const css = fonts.get("Developer Mono"); ``` ### `getNames()` -Lists all available font names. -```javascript -const fontList = fonts.getNames(); +Returns the names of all registered fonts, built-in and custom. + +```js +console.log(fonts.getNames()); // ["Fira Code", "Roboto Mono", ...] +``` + +### `has(name)` + +```js +if (!fonts.has("Developer Mono")) { + fonts.add("Developer Mono", css); +} +``` + +### `isCustom(name)` + +Returns `true` for fonts registered with `addCustom` (including ones restored from a previous session). + +### `remove(name)` + +Removes a font. If it was a custom font, the saved copy is removed as well. + +**Returns:** `true` if a font was removed, `false` if the name was unknown. + +### `setEditorFont(name)` + +Loads the font and applies it to the editor. If the font is unknown or fails to load, Acode shows an error toast and falls back to `Roboto Mono`. + +`fonts.setFont(name)` is an alias for this method. + +```js +await fonts.setEditorFont("Fira Code"); +``` + +::: info +This changes the active editor style only. It does not update the saved **Editor font** setting. To make the choice persist, also call `acode.require("settings").update({ editorFont: name })` (see [Settings](../editor-components/settings.md)). +::: + +### `setAppFont(name?)` + +Loads the font and applies it as the app UI font (`--app-font-family`). Call it without arguments to restore the default (`Roboto`). + +```js +await fonts.setAppFont("Developer Mono"); +await fonts.setAppFont(); // back to default +``` + +### `loadFont(name)` + +Downloads any remote files referenced by the font, injects the `@font-face` rule and waits for the browser to load it. `setEditorFont` and `setAppFont` call this for you; use it directly only when you need the font available (for example in your own UI) without changing the editor or app font. + +**Throws** an `Error` if the font is not registered. + +```js +await fonts.loadFont("Developer Mono"); +element.style.fontFamily = "'Developer Mono'"; +``` + +## Example: bundle a font with your plugin + +```js +async init(_page, _cacheFile, _cacheFileUrl, _firstInit) { + const fonts = acode.require("fonts"); + const fontUrl = `${this.baseUrl}fonts/Mono.woff2`; + + fonts.add( + "My Plugin Mono", + `@font-face { + font-family: 'My Plugin Mono'; + src: url('${fontUrl}') format('woff2'); + }`, + ); +} + +async destroy() { + acode.require("fonts").remove("My Plugin Mono"); +} +``` + +Remember to list font files in the [`files`](../plugin-essentials/manifest.md#files) array of `plugin.json` so they are included in your zip. diff --git a/docs/helpers/theme-builder.md b/docs/helpers/theme-builder.md index 4ee7387..df84d8b 100644 --- a/docs/helpers/theme-builder.md +++ b/docs/helpers/theme-builder.md @@ -1,150 +1,188 @@ +--- +title: Theme Builder +description: Create and customize app themes with the ThemeBuilder class. +--- + # Theme Builder +`ThemeBuilder` describes an **app theme**: a named set of colors that Acode turns into CSS variables on `:root`. Build one, then register it with the [`themes`](./themes.md) module. -### Introduction +```js +const ThemeBuilder = acode.require("themeBuilder"); +``` -The `ThemeBuilder` api from the `acode` core libraries provides a solution for creating and customizing themes in Acode . It offers control over various UI elements, colors, and styles. +## Create a theme -### Basic Usage +```js +new ThemeBuilder(name, type, version); +``` -1. **Import the ThemeBuilder Class** +| Parameter | Type | Default | Description | +| --- | --- | --- | --- | +| `name` | `string` | `""` | Theme name shown to the user. Its lowercase form is the theme [`id`](#id). | +| `type` | `"dark" \| "light"` | `"dark"` | Base color scheme. Sets the `theme-type` attribute on ``. | +| `version` | `"free" \| "paid"` | `"free"` | Leave as `"free"`. Themes that are not `"free"` are treated as Pro themes, and Acode falls back to the default theme for users without Pro. | -```javascript -const ThemeBuilder = acode.require('themeBuilder'); +```js +const theme = new ThemeBuilder("Midnight", "dark"); +theme.primaryColor = "#0b1020"; +theme.primaryTextColor = "#e6e9f5"; ``` -2. **Create a Theme Instance** +Every color starts from a default (listed below), so you only set the values you want to change. Colors can be any CSS color string: hex, `rgb()`, `rgba()`, and so on. -```javascript -const myTheme = new ThemeBuilder("MyDarkTheme", "dark"); -``` +## Color and style properties -- **Theme Name**: A descriptive name reflecting the theme's style (e.g., `"MyDarkTheme"`) -- **Theme Mode**: Specifies the base mode (`"light"` or `"dark"`) +### Surface & text -3. **Customize Theme Properties** +| Property | CSS variable | Default | +| --- | --- | --- | +| `primaryColor` | `--primary-color` | `rgb(153, 153, 255)` | +| `primaryTextColor` | `--primary-text-color` | `rgb(255, 255, 255)` | +| `secondaryColor` | `--secondary-color` | `rgb(255, 255, 255)` | +| `secondaryTextColor` | `--secondary-text-color` | `rgb(37, 37, 37)` | +| `linkTextColor` | `--link-text-color` | `rgb(97, 94, 253)` | +| `borderColor` | `--border-color` | `rgba(122, 122, 122, 0.2)` | +| `boxShadowColor` | `--box-shadow-color` | `rgba(0, 0, 0, 0.2)` | +| `scrollbarColor` | `--scrollbar-color` | `rgba(0, 0, 0, 0.3)` | -```javascript -myTheme.primaryColor = "#333"; -myTheme.secondaryColor = "#666"; -myTheme.textColor = "#ffffff"; -myTheme.backgroundColor = "#121212"; -``` +### Accent & state + +| Property | CSS variable | Default | +| --- | --- | --- | +| `activeColor` | `--active-color` | `rgb(51, 153, 255)` | +| `activeTextColor` | `--active-text-color` | `rgb(255, 215, 0)` | +| `activeIconColor` | `--active-icon-color` | `rgba(0, 0, 0, 0.2)` | +| `errorTextColor` | `--error-text-color` | `rgb(255, 185, 92)` | +| `successTextColor` | `--success-text-color` | `rgb(22, 152, 44)` | +| `dangerColor` | `--danger-color` | `rgb(160, 51, 0)` | + +### Buttons + +| Property | CSS variable | Default | +| --- | --- | --- | +| `buttonBackgroundColor` | `--button-background-color` | `rgb(51, 153, 255)` | +| `buttonTextColor` | `--button-text-color` | `rgb(255, 255, 255)` | +| `buttonActiveColor` | `--button-active-color` | `rgb(44, 142, 240)` | + +### Popups & dialogs + +| Property | CSS variable | Default | +| --- | --- | --- | +| `popupBackgroundColor` | `--popup-background-color` | `rgb(255, 255, 255)` | +| `popupTextColor` | `--popup-text-color` | `rgb(37, 37, 37)` | +| `popupIconColor` | `--popup-icon-color` | `rgb(153, 153, 255)` | +| `popupActiveColor` | `--popup-active-color` | `rgb(169, 0, 0)` | +| `popupBorderColor` | `--popup-border-color` | `rgba(0, 0, 0, 0)` | +| `popupBorderRadius` | `--popup-border-radius` | `4px` | + +### Layout -### Customizable Theme Properties - -#### Color Palette -- `primaryColor`: Main color for primary elements -- `secondaryColor`: Accent color for secondary elements -- `textColor`: Main text color -- `backgroundColor`: Application background color -- `activeColor`: Color for active elements -- `dangerColor`: Color for error or destructive actions -- `linkTextColor`: Color for clickable links - -#### Typography -- `fontFamily`: Font type and fallbacks -- `fontSize`: Base font size -- `fontWeight`: Text thickness - -#### Specific Element Styles -- `buttonBackgroundColor`: Button background -- `buttonTextColor`: Button text color -- `borderColor`: Element border color -- `popupBackgroundColor`: Popup/modal background -- `scrollbarColor`: Scrollbar color - -### More Styling Options - -#### Color Manipulation -```javascript -// Generate a darker version of the primary color -const darkenedPrimaryColor = myTheme.darkenPrimaryColor(); +| Property | CSS variable | Default | +| --- | --- | --- | +| `fileTabWidth` | `--file-tab-width` | `120px` | + +::: tip Start with four +Most themes look coherent after setting just `primaryColor`, `primaryTextColor`, `secondaryColor` and `secondaryTextColor`. Adjust the rest as needed. +::: + +::: info +The CSS variable `--danger-text-color` exists (default `rgb(255, 255, 255)`) but has no property on `ThemeBuilder`. +::: + +## Other properties + +### `id` + +Read-only. The theme name in lowercase. The `themes` module uses it as the key, so two themes with names that differ only by case are the same theme. + +### `darkenedPrimaryColor` + +A darker variant of `primaryColor`. Acode uses it to darken the status and navigation bars while a dialog is open. While `autoDarkened` is `true` (the default), it is recalculated every time you assign `primaryColor`. Set `autoDarkened = false` first if you want to pick the value yourself: + +```js +theme.autoDarkened = false; +theme.primaryColor = "#000000"; +theme.darkenedPrimaryColor = "#000000"; ``` -#### Theme Types -- `"light"`: Light color scheme -- `"dark"`: Dark color scheme +### `preferredEditorTheme`, `preferredTerminalTheme`, `preferredFont` + +Optional pairings that Acode applies together with the theme when the user selects it in **Settings → Themes**: + +- `preferredEditorTheme`: id of an [editor theme](../utilities/editor-themes.md). +- `preferredFont`: name of a registered [font](./fonts.md). +- `preferredTerminalTheme`: id of a terminal theme (for example `"dark"`). Acode only applies this one the first time it applies a theme after starting, so don't rely on it to switch the terminal theme. -### Complete Theme Configuration Example +All three default to `null`, meaning "leave the user's choice alone". -```javascript -const myCustomTheme = new ThemeBuilder("ModernDark", "dark"); +## Methods -// Color Configuration -myCustomTheme.primaryColor = "#2196F3"; -myCustomTheme.secondaryColor = "#FF4081"; -myCustomTheme.textColor = "#FFFFFF"; -myCustomTheme.backgroundColor = "#121212"; +### `toJSON(colorType?)` -// Typography -myCustomTheme.fontFamily = "Roboto, sans-serif"; -myCustomTheme.fontSize = "16px"; -myCustomTheme.fontWeight = "400"; +Returns a plain object with `name`, `type`, `version` and one camelCase key per color property (for example `primaryColor`). -// Element-Specific Styles -myCustomTheme.buttonBackgroundColor = "#2196F3"; -myCustomTheme.buttonTextColor = "#FFFFFF"; -myCustomTheme.borderColor = "#333333"; +`colorType` controls how colors are written: `"none"` (default) keeps them as you set them, `"hex"` converts to hex and `"rgba"` converts to `rgba()`. + +### `toString()` + +`JSON.stringify(theme.toJSON())`. + +### `css` + +Read-only getter that returns the theme as a single `:root { ... }` rule with all CSS variables. + +### `matches(id)` + +Returns `true` if the theme's id equals `id` (case-insensitive). + +### `darkenPrimaryColor()` + +Recomputes `darkenedPrimaryColor` from the current `primaryColor`. It returns nothing; read `darkenedPrimaryColor` afterwards. + +### `ThemeBuilder.fromJSON(json)` + +Creates a theme from an object shaped like the output of `toJSON()`. `name`, `type` and `version` are required; unknown keys are ignored. + +```js +const copy = ThemeBuilder.fromJSON(theme.toJSON()); ``` -### Best Practices -- Choose a consistent color palette -- Ensure sufficient contrast between text and background -- Test your theme across different components and states -- Use the `darkenPrimaryColor()` method for dynamic color variations - -### Supported CSS Custom Properties - -The ThemeBuilder generates the following CSS custom properties: -- `--primary-color` -- `--secondary-color` -- `--text-color` -- `--background-color` -- `--active-color` -- `--button-background-color` -- `--border-color` -- And many more... - -### Notes -- Always import the ThemeBuilder from the `acode` library -- Theme customization is flexible and supports both light and dark modes -- You can override default styles for specific UI components -- for theme management check the `themes`documentation -======= -**Theme Builder** - -**Introduction** - -To create a new theme for your application, you'll need to utilize the `ThemeBuilder` class provided by the `acode` library. This class offers a straightforward way to customize various aspects of your theme, from primary and secondary colors to font styles and more. - -**Basic Usage** - -1. **Import the `ThemeBuilder` class:** - ```javascript - const ThemeBuilder = acode.require('themeBuilder'); - ``` -2. **Create a new theme instance:** - ```javascript - const myTheme = new ThemeBuilder("MyDarkTheme", "dark"); - ``` - * **Theme Name:** The first argument, `"MyDarkTheme"`, is the name of your theme. It should be a descriptive name that reflects the theme's style. - * **Theme Mode:** The second argument, `"dark"`, specifies the base mode of the theme (either "light" or "dark"). - -3. **Customize theme properties:** - ```javascript - myTheme.primaryColor = "#333"; - myTheme.secondaryColor = "#666"; - // ... other theme property customizations - ``` - You can customize various theme properties, such as: - * `primaryColor` - * `secondaryColor` - * `textColor` - * `backgroundColor` - * `fontFamily` - * `fontSize` - * `fontWeight` - * // ... and many more +The copy holds the name, type, version and colors only. `toJSON()` leaves out `preferredEditorTheme`, `preferredTerminalTheme`, `preferredFont`, `autoDarkened` and `darkenedPrimaryColor`, so set them again on the copy if you need them. + +## Full example + +```js +const ThemeBuilder = acode.require("themeBuilder"); +const themes = acode.require("themes"); + +const theme = new ThemeBuilder("Ocean Night", "dark"); + +// Surfaces and text +theme.primaryColor = "#0d1b2a"; +theme.primaryTextColor = "#e0e1dd"; +theme.secondaryColor = "#1b263b"; +theme.secondaryTextColor = "#c8ccd4"; +theme.linkTextColor = "#7aa2f7"; +theme.borderColor = "rgba(255, 255, 255, 0.12)"; + +// Accent and buttons +theme.activeColor = "#4cc9f0"; +theme.buttonBackgroundColor = "#4cc9f0"; +theme.buttonTextColor = "#0d1b2a"; + +// Popups +theme.popupBackgroundColor = "#1b263b"; +theme.popupTextColor = "#e0e1dd"; + +// Pair it with an editor theme (optional) +theme.preferredEditorTheme = "tokyoNight"; + +themes.add(theme); +``` +## Design tips +- Keep enough contrast between `primaryColor`/`primaryTextColor` and `secondaryColor`/`secondaryTextColor`. +- Set `type` to match your colors (`"dark"` for dark backgrounds). Acode exposes it as the `theme-type` attribute on ``, which the built-in console and preview use to match light or dark. +- Test the theme on dialogs, the file browser and the settings pages, not only the editor. diff --git a/docs/helpers/themes.md b/docs/helpers/themes.md index 75930c7..fcc9e8b 100644 --- a/docs/helpers/themes.md +++ b/docs/helpers/themes.md @@ -1,44 +1,106 @@ -# Acode Theme Management +--- +title: Themes +description: Add, look up, list and update app themes from a plugin. +--- -Acode provides a flexible and intuitive module for managing themes, enabling developers to seamlessly add, retrieve, update, and list themes within their project. +# Themes -## API Overview +The `themes` module manages Acode's **app themes** (the colors of the UI: toolbar, dialogs, buttons and so on). To change how *code* is colored, see [Editor Themes](../utilities/editor-themes.md) instead. -```javascript -const themes = acode.require('themes'); +```js +const themes = acode.require("themes"); ``` +Themes are [`ThemeBuilder`](./theme-builder.md) instances. A theme's **id** is its `name` in lowercase, and ids are unique. + ## Methods -### `add(theme: ThemeBuilder)` +### `add(theme)` + +Registers a theme so it appears in **Settings → Themes**. + +**Parameters** + +- `theme` (`ThemeBuilder`): the theme to register. Anything that is not a `ThemeBuilder` instance is silently ignored. + +**Behavior** + +- If a theme with the same id already exists, the call does nothing. Use [`update`](#update-theme) to change an existing theme. +- If the user already selected this theme (for example, they picked it in a previous session), it is applied as soon as it is added. -Adds a new theme to the theme collection. +```js +const ThemeBuilder = acode.require("themeBuilder"); -**Parameters:** -- `theme` (required): An instance of ThemeBuilder defining the theme's properties +const theme = new ThemeBuilder("Modern Dark", "dark"); +theme.primaryColor = "#1e1e2e"; +theme.primaryTextColor = "#cdd6f4"; -**Example:** -```javascript -const theme = new ThemeBuilder('Modern Dark', 'dark'); themes.add(theme); ``` -### `get(name: string)` +### `get(name)` -Retrieves a specific theme by its name. +Returns the registered `ThemeBuilder` for a name, or `undefined`. The lookup is case-insensitive. -**Parameters:** -- `name` (required): The unique name of the theme to retrieve +```js +const theme = themes.get("Modern Dark"); +``` + +### `list()` + +Returns a summary of every registered theme (built-in and plugin-provided). -**Returns:** -- ThemeBuilder instance representing the requested theme +**Returns:** `Array<{ id: string, name: string, type: string, version: string, primaryColor: string }>` -**Example:** -```javascript -const theme = themes.get('Modern Dark'); +`name` here is the id with its first letter capitalized (for example `"Ocean night"` for a theme named `"Ocean Night"`), not the original `name`. Compare by `id` instead. + +```js +themes.list().forEach(({ id, name, type }) => { + console.log(id, name, type); +}); ``` -### `update(theme: ThemeBuilder)` +### `update(theme)` + +Copies every value from `theme.toJSON()` (`name`, `type`, `version` and **all** colors) onto the registered theme with the same id. If no such theme exists, it is added instead. -Updates an existing theme in the theme collection. +Colors you did not set on `theme` are copied too, as ThemeBuilder defaults. To change a few colors, edit the registered theme directly, or pass a theme with every color set: + +```js +const theme = themes.get("Modern Dark"); +theme.primaryColor = "#11111b"; +``` + +::: info +`toJSON()` does not include `preferredEditorTheme`, `preferredTerminalTheme`, `preferredFont`, `autoDarkened` or `darkenedPrimaryColor`, so `update` does not copy them. Set those on the registered theme directly. +::: + +::: warning +`update` (or editing the registered theme) changes the stored theme, but it does **not** re-render the UI. If the theme is currently active, its new colors show up the next time the theme is applied (for example, when the user re-selects it or restarts Acode). +::: + +## Example: ship a theme with your plugin + +```js +class AcodePlugin { + async init() { + const ThemeBuilder = acode.require("themeBuilder"); + const themes = acode.require("themes"); + + const theme = new ThemeBuilder("Midnight", "dark"); + theme.primaryColor = "#0b1020"; + theme.primaryTextColor = "#e6e9f5"; + theme.secondaryColor = "#131a30"; + theme.secondaryTextColor = "#c9cee6"; + theme.activeColor = "#5b8cff"; + + themes.add(theme); + } + + async destroy() {} +} +``` +::: info +There is no `remove` method: a theme stays registered until Acode restarts. Because `add` ignores duplicate ids, running `init` again in the same session is safe. +::: diff --git a/docs/ui-components/dialogs/alert.md b/docs/ui-components/dialogs/alert.md index d7251d2..c032fbf 100644 --- a/docs/ui-components/dialogs/alert.md +++ b/docs/ui-components/dialogs/alert.md @@ -1,49 +1,53 @@ +--- +title: Alert +description: Show a modal message with a single OK button. +--- + # Alert -The `alert` component in Acode is a dialog box for displaying messages, warnings, or errors to users within a modal window. Similar to the traditional JavaScript `alert()`. +`alert` shows a modal message and an **OK** button. It is the Acode equivalent of the browser's `alert()`. -## Usage +```js +const alert = acode.require("alert"); +``` -To use the `alert` component in your Acode plugin, you can require it using the following code: +## Signature -```javascript -const alert = acode.require('alert'); +```ts +alert(title, message, onhide?): void ``` -Once you have the `alert` component, you can create an instance with the following syntax: +| Parameter | Type | Description | +| --- | --- | --- | +| `title` | `string` | Heading of the dialog. | +| `message` | `string` | Body text. Treated as HTML after sanitizing, and any `http(s)://` URL in it becomes a link. | +| `onhide` | `() => void` | Optional. Called when the user taps **OK** or outside the dialog. Not called when the dialog is closed with the back button. | + +If you pass only one argument, it is used as the **message** and the dialog has no title: ```js -alert( - 'Title of Alert', // Title of the alert modal - 'The alert body message..', // Message to display in the body of the alert modal - () => { - // Optional function to call when the alert modal is closed - window.toast('Alert modal closed', 4000); - } -); +alert("Saved!"); ``` -## Parameters - -- **titleText (string):** - - The text to display in the title of the alert modal. +`alert` returns immediately; it does not wait for the user. Use `onhide` to run code after the dialog closes, but don't rely on it running: closing the dialog with the back button skips it. -- **message (string):** - - The message to display in the body of the alert modal. - -- **onhide (Function):** - - An optional function to call when the alert modal is closed. +::: warning +Because `message` is rendered as HTML, escape any text that comes from a file, a network response or the user before putting it in the message. +::: ## Example -```javascript:line-numbers{1,7} -const alert = acode.require('alert'); - -const handleOnHide = () => { - window.toast('Alert modal closed', 4000); -}; +```js +const alert = acode.require("alert"); -alert('Title of Alert', 'The alert body message..', handleOnHide); +alert( + "Update available", + "Version 2.0 is out. Details: https://example.com/changelog", + () => window.toast("Alert closed", 3000), +); ``` -In this example, when the alert modal is closed, the `handleOnHide` function will be called, and a toast message **'Alert modal closed'** will be displayed for 4000 milliseconds. This allows you to perform additional actions or provide feedback when the user interacts with the alert dialog. \ No newline at end of file +## See also + +- [Confirm](./confirm.md) for a yes/no question +- [Toast](../toast.md) for a message that disappears by itself diff --git a/docs/ui-components/dialogs/confirm.md b/docs/ui-components/dialogs/confirm.md index 509089b..8314517 100644 --- a/docs/ui-components/dialogs/confirm.md +++ b/docs/ui-components/dialogs/confirm.md @@ -1,47 +1,93 @@ +--- +title: Confirm +description: Ask the user a yes/no question in a modal dialog. +--- + # Confirm -The `confirm` ui component in Acode is a dialog box for displaying confirmation message modals to users. Whether you're seeking user approval for a critical action or confirming a decision, this component is best suited for this process. +`confirm` shows a modal with **Cancel** and **OK** buttons and resolves with the user's answer. -## Usage +```js +const confirm = acode.require("confirm"); +``` -To use the `confirm` component in your Acode plugin, you can require it using the following code: +## Signature -```javascript -const confirm = acode.require('confirm'); +```ts +confirm(title, message?, isHTML?, options?): Promise ``` -Once you have the `confirm` component, you can create an instance with the following syntax: +| Parameter | Type | Default | Description | +| --- | --- | --- | --- | +| `title` | `string` | | Heading of the dialog. | +| `message` | `string` | | Body text. | +| `isHTML` | `boolean` | `false` | When `true`, `message` is rendered as sanitized HTML. Otherwise it is shown as plain text. | +| `options` | `object` | `{}` | See [Options](#options). | -```javascript -const confirmation = await confirm( - 'Warning', // Title of the confirmation message modal - 'Are you sure?' // Body of the confirmation message modal -); -``` +If you pass only one argument, it is used as the **message** and the dialog has no title. + +## Returns -## Parameters +A `Promise` that resolves with: -- **titleText (string):** - - A string representing the title of the confirmation message modal. This title will be displayed at the top of the message modal. +- `true` if the user pressed **OK** +- `false` if the user pressed **Cancel** or the back button, or the `signal` was aborted -- **message (string):** - - A string representing the body of the confirmation message modal. +With `options.returnState` set, it resolves with an object instead. See below. -## Returns +## Options + +| Option | Type | Description | +| --- | --- | --- | +| `checkboxText` | `string` | Adds a checkbox (unchecked) with this label under the message, such as "Don't ask again". | +| `returnState` | `boolean` | Resolve with `{ confirmed, checked }` instead of a boolean, so you can read the checkbox. | +| `signal` | `AbortSignal` | Aborting the signal closes the dialog and counts as **Cancel**. | +| `direction` | `"ltr" \| "rtl"` | Text direction of the dialog. | +| `aboveOverlay` | `boolean` | Draw the dialog above other overlays such as a full-screen page. | + +## Examples + +### Basic -The `confirm` component returns a promise that resolves to a `boolean` value. The boolean value represents whether the user confirmed or denied the message. A value of `true` represents confirmation, while `false` represents denial. +```js +const confirm = acode.require("confirm"); -## Example +if (await confirm("Delete file", "This cannot be undone. Continue?")) { + // delete it +} +``` -```javascript:line-numbers{1,3} -const confirm = acode.require('confirm'); +### With a checkbox -let confirmation = await confirm('Warning', 'Are you sure?'); -if (confirmation) { - window.toast('File deleted...', 4000); -} else { - window.toast('File not deleted...', 4000); +```js +const { confirmed, checked } = await confirm( + "Reset settings", + "All settings return to their defaults.", + false, + { checkboxText: "Also clear saved layouts", returnState: true }, +); + +if (confirmed) { + resetSettings({ clearLayouts: checked }); } ``` -In this example, the `confirm` component is utilized to ask the user if they want to delete a file. If the user confirms, the message "File deleted." will be toasted. If the user denies, the message "File not deleted." will be toasted. \ No newline at end of file +### Auto-close after a timeout + +```js +const controller = new AbortController(); +setTimeout(() => controller.abort(), 10_000); + +const ok = await confirm("Still there?", "Continue syncing?", false, { + signal: controller.signal, +}); +``` + +::: tip +`acode.confirm(title, message)` is a shorter wrapper that accepts only the first two arguments. +::: + +## See also + +- [Alert](./alert.md) for a message without a choice +- [Select](./select.md) for more than two choices diff --git a/docs/ui-components/dialogs/multi-prompt.md b/docs/ui-components/dialogs/multi-prompt.md index 93d99f8..616e935 100644 --- a/docs/ui-components/dialogs/multi-prompt.md +++ b/docs/ui-components/dialogs/multi-prompt.md @@ -1,74 +1,140 @@ +--- +title: Multi Prompt +description: Collect several values at once with a form-style dialog. +--- + # Multi Prompt -The `multiPrompt` ui component in Acode is a dialog box for prompting users with multiple inputs at once. Whether you need to collect various pieces of information or gather complex input data. +`multiPrompt` shows one dialog containing several inputs (text fields, numbers, checkboxes and so on) and resolves with all the values together. -## Usage +```js +const multiPrompt = acode.require("multiPrompt"); +``` -To use the `multiPrompt` component in your Acode plugin, you can require it using the following code: +## Signature -```javascript -const multiPrompt = acode.require('multiPrompt'); +```ts +multiPrompt(message, inputs, help?): Promise> ``` -Once you have the `multiPrompt` component, you can create an instance with the following syntax: - -```javascript -const myPrompt = await multiPrompt( - 'Enter your name & age', // Message for the prompt modal - [ - { type: 'text', id: 'name' }, // Example: Text input for the name - { type: 'number', id: 'age' }, // Example: Number input for the age - ], - 'https://example.com/help/' // Help text with the associated URL -); +| Parameter | Type | Description | +| --- | --- | --- | +| `message` | `string` | Title of the dialog. | +| `inputs` | `Array>` | The fields to show, in order. An inner array creates a [group](#groups). | +| `help` | `string` | Optional. Adds a help icon to the title. See [Help](#help). | + +## Returns + +A `Promise` that resolves with an **object keyed by each input's `id`**: + +- text-like inputs give a `string` +- `checkbox` and `radio` inputs give a `boolean` + +```js +const { name, age, subscribe } = await multiPrompt("Sign up", [ + { id: "name", type: "text", placeholder: "Name", required: true }, + { id: "age", type: "number", placeholder: "Age" }, + { id: "subscribe", type: "checkbox", placeholder: "Send me updates" }, +]); ``` -## Parameters +::: warning Cancel rejects the promise +Pressing **Cancel** rejects the promise with no value. Wrap the call in `try`/`catch` if the user is allowed to cancel. -- **`message (string):`** - - The title for the prompt modal. +Closing the dialog with the back button does **not** reject or resolve: the promise stays pending. -- **`inputs (Array>):`** - - The inputs to prompt the user for. It can be a single input or an array of inputs. Each input is defined by an object with various properties such as `id`, `type`, `placeholder`, etc. +```js +try { + const values = await multiPrompt("Settings", inputs); +} catch { + return; // cancelled +} +``` +::: - :::tip - - Provide clear and concise messages to guide users through the input process. - - Utilize various input types, such as text, number, etc., based on the type of information you need. [Check this reference for more](http://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#input_types). - ::: +::: info +Numbers are returned as strings, like the browser's `input.value`. Convert with `Number(age)` when you need a number. +::: -- **`help (string):`** - - The help icon at the top of the `multiPrompt` will be enabled with the specified help URL. - - It must be valid url +## Input options + +| Option | Type | Description | +| --- | --- | --- | +| `id` | `string` | **Required.** Key of this value in the result. | +| `type` | `string` | Any [HTML input type](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#input_types) such as `text`, `number`, `email`, `password`, `checkbox` or `radio`. Defaults to `text`. `textarea` is drawn, but its value is not included in the result and `required` is not checked for it. | +| `value` | `string \| boolean` | Initial value. For `checkbox` and `radio`, whether it starts checked. | +| `placeholder` | `string` | Placeholder text. For `checkbox` and `radio`, this is the **label** next to the box. | +| `required` | `boolean` | Block **OK** while the field is empty. | +| `match` | `RegExp` | The value must match, otherwise an "invalid value" message is shown and **OK** is disabled. | +| `hints` | `string[] \| function` | Autocomplete suggestions shown while typing. See [Input Hints](../../helpers/input-hints.md). | +| `name` | `string` | Group name for `radio` inputs; radios with the same `name` are mutually exclusive. | +| `disabled` | `boolean` | Show the field but do not let the user edit it. | +| `readOnly` | `boolean` | Show the value and allow selecting or copying it. | +| `hidden` | `boolean` | Keep the field in the result but do not show it. | +| `autofocus` | `boolean` | Focus this field when the dialog opens. | +| `sensitive` | `boolean` | Clear the field's contents when the dialog closes. `password` fields are always cleared. | +| `onclick` | `(event) => void` | Click handler. `this` is the input element (for `checkbox`/`radio`, its `