max / alloy
17 files changed,
+62 insertions,
-61 deletions
| @@ -24,18 +24,18 @@ | |||
| 24 | 24 | "alloy_tui", | |
| 25 | 25 | "anyhow", | |
| 26 | 26 | "clap", | |
| 27 | + | "makeover", | |
| 27 | 28 | "ratatui", | |
| 28 | 29 | "serde", | |
| 29 | 30 | "serde_json", | |
| 30 | - | "theme-common", | |
| 31 | 31 | ] | |
| 32 | 32 | ||
| 33 | 33 | [[package]] | |
| 34 | 34 | name = "alloy_tui" | |
| 35 | 35 | version = "0.0.0" | |
| 36 | 36 | dependencies = [ | |
| 37 | + | "makeover", | |
| 37 | 38 | "ratatui", | |
| 38 | - | "theme-common", | |
| 39 | 39 | ] | |
| 40 | 40 | ||
| 41 | 41 | [[package]] | |
| @@ -716,6 +716,14 @@ | |||
| 716 | 716 | "winapi", | |
| 717 | 717 | ] | |
| 718 | 718 | ||
| 719 | + | [[package]] | |
| 720 | + | name = "makeover" | |
| 721 | + | version = "0.8.0" | |
| 722 | + | dependencies = [ | |
| 723 | + | "serde", | |
| 724 | + | "toml", | |
| 725 | + | ] | |
| 726 | + | ||
| 719 | 727 | [[package]] | |
| 720 | 728 | name = "memchr" | |
| 721 | 729 | version = "2.8.2" | |
| @@ -1424,14 +1432,6 @@ | |||
| 1424 | 1432 | "winapi", | |
| 1425 | 1433 | ] | |
| 1426 | 1434 | ||
| 1427 | - | [[package]] | |
| 1428 | - | name = "theme-common" | |
| 1429 | - | version = "0.7.0" | |
| 1430 | - | dependencies = [ | |
| 1431 | - | "serde", | |
| 1432 | - | "toml", | |
| 1433 | - | ] | |
| 1434 | - | ||
| 1435 | 1435 | [[package]] | |
| 1436 | 1436 | name = "thiserror" | |
| 1437 | 1437 | version = "1.0.69" |
| @@ -11,7 +11,7 @@ | |||
| 11 | 11 | ||
| 12 | 12 | [workspace.dependencies] | |
| 13 | 13 | ratatui = "0.30" | |
| 14 | - | theme-common = { path = "../MNW/shared/theme-common" } | |
| 14 | + | makeover = { git = "https://git.sr.ht/~maxmj/makeover", tag = "v0.8.0" } | |
| 15 | 15 | ||
| 16 | 16 | [profile.release] | |
| 17 | 17 | lto = "thin" |
| @@ -6,7 +6,7 @@ | |||
| 6 | 6 | ||
| 7 | 7 | `alloy_tui` exposes: | |
| 8 | 8 | ||
| 9 | - | - **Design tokens** as a runtime `Theme` value resolving theme-common intents into ratatui `Color` / `Style`. Single source of truth for everything in [DESIGN-LANGUAGE.md](DESIGN-LANGUAGE.md). | |
| 9 | + | - **Design tokens** as a runtime `Theme` value resolving makeover intents into ratatui `Color` / `Style`. Single source of truth for everything in [DESIGN-LANGUAGE.md](DESIGN-LANGUAGE.md). | |
| 10 | 10 | - **Themed primitives** configured against the tokens: blocks, list rows, tags, form fields, status bars, log panes. Each carries its focus/selected/disabled variants. | |
| 11 | 11 | - **Layout helpers**: Alloy-flavored wrappers over ratatui's `Layout` constraint solver (section spacing measured in cells). | |
| 12 | 12 | - **A focus + keymap model**: the piece ratatui does not give you. Rendering is immediate-mode, but input is event-driven, so the crate owns the navigation model the app drives from its event loop: `FocusRing` across panes, `Cursor` down a list. | |
| @@ -143,9 +143,9 @@ | |||
| 143 | 143 | ||
| 144 | 144 | ## Design tokens | |
| 145 | 145 | ||
| 146 | - | Tokens are the single source of truth, and they are read at runtime. Earlier drafts of this document described a `tokens.toml` at the crate root compiled to Rust constants by a `build.rs` step. That pipeline was replaced by theme-common before `alloy_tui` was written, and no part of it exists. [TOKENS.md](TOKENS.md) is authoritative; this section only describes the crate-side shape. | |
| 146 | + | Tokens are the single source of truth, and they are read at runtime. Earlier drafts of this document described a `tokens.toml` at the crate root compiled to Rust constants by a `build.rs` step. That pipeline was replaced by makeover before `alloy_tui` was written, and no part of it exists. [TOKENS.md](TOKENS.md) is authoritative; this section only describes the crate-side shape. | |
| 147 | 147 | ||
| 148 | - | Themes are theme-common `.toml` files, the same schema every make-family app reads, living in `MNW/shared/themes/` and `~/.config/alloy/themes/`. `alloy_tui` loads one, resolves the intents it needs into ratatui colors, and derives two Alloy-specific tokens locally so theme files stay minimal and cross-app compatible: | |
| 148 | + | Themes are makeover `.toml` files, the same schema every make-family app reads, living in makeover's `themes/` and `~/.config/alloy/themes/`. `alloy_tui` loads one, resolves the intents it needs into ratatui colors, and derives two Alloy-specific tokens locally so theme files stay minimal and cross-app compatible: | |
| 149 | 149 | ||
| 150 | 150 | ``` | |
| 151 | 151 | border-subtle = mix(line.border, surface.page, 60%) decorative divider | |
| @@ -174,6 +174,6 @@ | |||
| 174 | 174 | ||
| 175 | 175 | ## Late-term: a design tool, and the token seam | |
| 176 | 176 | ||
| 177 | - | The theme file is the deliberate seam where external design tooling could live, and it is toolkit-agnostic: anything that emits a valid theme-common `.toml` is a viable producer (a hand-edited file, Tokens Studio, a future Alloy-built tool). The move to theme-common widened that seam rather than closing it, since the format is now shared with every make-family app instead of being Alloy's own. A speculative token-sync tool keeps the working name **`cast`** (alloys are cast; the tool casts a design source into Alloy tokens). The broader research question of a design tool native to immediate-mode UI is captured in [RESEARCH-IMMEDIATE-MODE-DESIGN.md](RESEARCH-IMMEDIATE-MODE-DESIGN.md); with the pivot to ratatui, that question now targets terminal compositions rather than GPU-drawn ones, but the architecture is the same. | |
| 177 | + | The theme file is the deliberate seam where external design tooling could live, and it is toolkit-agnostic: anything that emits a valid makeover `.toml` is a viable producer (a hand-edited file, Tokens Studio, a future Alloy-built tool). The move to makeover widened that seam rather than closing it, since the format is now shared with every make-family app instead of being Alloy's own. A speculative token-sync tool keeps the working name **`cast`** (alloys are cast; the tool casts a design source into Alloy tokens). The broader research question of a design tool native to immediate-mode UI is captured in [RESEARCH-IMMEDIATE-MODE-DESIGN.md](RESEARCH-IMMEDIATE-MODE-DESIGN.md); with the pivot to ratatui, that question now targets terminal compositions rather than GPU-drawn ones, but the architecture is the same. | |
| 178 | 178 | ||
| 179 | 179 | The commitment that keeps the door open: **tokens are a file, not Rust source.** As long as the theme file is the authority and Rust holds no hex values of its own, the producer is swappable and the design system stays portable. Runtime loading strengthens that commitment: swapping a theme is now a file change and a relaunch rather than a rebuild. |
| @@ -22,8 +22,8 @@ | |||
| 22 | 22 | alloy update # system updates: rpm-ostree front | |
| 23 | 23 | alloy sync # syncthing front (see CONTINUITY.md) | |
| 24 | 24 | alloy config <path> # schema-driven TOML/KDL editor | |
| 25 | - | alloy theme <name> # swap the runtime theme; reads MNW/shared/themes/*.toml | |
| 26 | - | # or ~/.config/alloy/themes/*.toml via theme-common | |
| 25 | + | alloy theme <name> # swap the runtime theme; reads makeover's themes/*.toml | |
| 26 | + | # or ~/.config/alloy/themes/*.toml via makeover | |
| 27 | 27 | ``` | |
| 28 | 28 | ||
| 29 | 29 | `alloy mesh` was named `alloy tail` when this document was written. It is | |
| @@ -111,7 +111,7 @@ | |||
| 111 | 111 | ||
| 112 | 112 | ## `alloy_tui`: the ratatui design system | |
| 113 | 113 | ||
| 114 | - | `alloy_tui` **is** Alloy's design system. The pivot dropped the egui `alloy_ui` sibling, so there is no GUI counterpart, and this crate carries the whole authored visual identity. It consumes theme-common `.toml` theme files at runtime (see [TOKENS.md](TOKENS.md)); palette and semantic colors render as terminal chrome. | |
| 114 | + | `alloy_tui` **is** Alloy's design system. The pivot dropped the egui `alloy_ui` sibling, so there is no GUI counterpart, and this crate carries the whole authored visual identity. It consumes makeover `.toml` theme files at runtime (see [TOKENS.md](TOKENS.md)); palette and semantic colors render as terminal chrome. | |
| 115 | 115 | ||
| 116 | 116 | Contents (v1). Shipped: | |
| 117 | 117 | - Themed `ratatui` widget wrappers: `AlloyBlock`, `AlloyList`, `AlloyStatusBar`, `AlloyLog`. | |
| @@ -142,7 +142,7 @@ | |||
| 142 | 142 | - **Shipped.** `alloy net`, `alloy audio`, `alloy mesh`. Plus the shell they share: frame, reserved keys, focus, command-log pane, background tick. | |
| 143 | 143 | - **Next.** `alloy config`, with schemas for the v0-adopted TOML configs (rio, yazi, mako, and others; the sway config takes the text-edit fallback). The largest remaining piece: schema-DSL v1 parser, `toml_edit` roundtrip layer, and the form widgets together. | |
| 144 | 144 | - **Blocked on the target machine.** `alloy display` and `alloy update` front `swaymsg`/`wlr-randr` and `rpm-ostree`, none of which exist on a non-Fedora, non-sway development box. Writing them now would mean shipping parsers checked against nothing but their own fixtures, which is exactly how the two parser bugs found so far got written. They want the QEMU image or real hardware. | |
| 145 | - | - **Then.** `alloy sync`, `alloy theme`. `alloy theme` swaps the runtime theme in place (theme-common consumer, no re-login). First-boot flow (see [CONTINUITY.md](CONTINUITY.md)) is a thin shim over `alloy mesh` and `alloy sync` enrollments. | |
| 145 | + | - **Then.** `alloy sync`, `alloy theme`. `alloy theme` swaps the runtime theme in place (makeover consumer, no re-login). First-boot flow (see [CONTINUITY.md](CONTINUITY.md)) is a thin shim over `alloy mesh` and `alloy sync` enrollments. | |
| 146 | 146 | - **v1.x.** Additional adopted-tool schemas as the v0 stack grows. (`alloy hinged` was shelved with the FW12 tablet flow in the pivot.) | |
| 147 | 147 | - **v2+.** Third-party subcommand registration (a well-known directory of ratatui adapters the console discovers at runtime), if a real ecosystem case emerges. Not planned. | |
| 148 | 148 |
| @@ -32,7 +32,7 @@ | |||
| 32 | 32 | ||
| 33 | 33 | Immediate-mode reframes the unit of design work. There is no screen, only a *function rendered every frame from current state*. So the meaningful artifacts a designer produces are: | |
| 34 | 34 | ||
| 35 | - | 1. **Tokens**: palette, type scale, spacing, radii, motion (or its absence). Flat data, already covered by the theme-common theme files described in [COMPONENT-LIBRARY.md](COMPONENT-LIBRARY.md). | |
| 35 | + | 1. **Tokens**: palette, type scale, spacing, radii, motion (or its absence). Flat data, already covered by the makeover theme files described in [COMPONENT-LIBRARY.md](COMPONENT-LIBRARY.md). | |
| 36 | 36 | 2. **Themed primitives**: buttons, text fields, tags, focus rings. Visual specification of state variants. | |
| 37 | 37 | 3. **Composition vocabulary**: "this is a card with these slots," "this is a list row with this arrangement," "this section is dense; this one is breathy." A *grammar*, not a pixel layout. | |
| 38 | 38 | 4. **Data shapes the composition accepts.** A `PackageRow` accepts `(name, version, urgency, icon)`. The designer specifies what fields render in what arrangement; the engineer provides the data. | |
| @@ -49,7 +49,7 @@ | |||
| 49 | 49 | ||
| 50 | 50 | ### Token-first authoring | |
| 51 | 51 | ||
| 52 | - | Most design work is the token graph. The tool's primary editing surface is the palette, the typography scale, the spacing scale, the radii, the elevation steps. Changes propagate instantly to every component preview. This is already most of Figma's value, and it round-trips cleanly to a theme-common theme file (the `cast` tool idea generalized). | |
| 52 | + | Most design work is the token graph. The tool's primary editing surface is the palette, the typography scale, the spacing scale, the radii, the elevation steps. Changes propagate instantly to every component preview. This is already most of Figma's value, and it round-trips cleanly to a makeover theme file (the `cast` tool idea generalized). | |
| 53 | 53 | ||
| 54 | 54 | ### Composition grammar, not pixel layout | |
| 55 | 55 | ||
| @@ -78,7 +78,7 @@ | |||
| 78 | 78 | ||
| 79 | 79 | ### Engineer-facing output | |
| 80 | 80 | ||
| 81 | - | The artifacts produced are: a theme-common theme file (tokens), `<Component>.alloy.ron` files (compositions), and a small registry of which components exist. The engineer wires these into the authored TUIs (the console). There is no pixel-perfect mockup to translate; the composition file *is* the spec. | |
| 81 | + | The artifacts produced are: a makeover theme file (tokens), `<Component>.alloy.ron` files (compositions), and a small registry of which components exist. The engineer wires these into the authored TUIs (the console). There is no pixel-perfect mockup to translate; the composition file *is* the spec. | |
| 82 | 82 | ||
| 83 | 83 | ## Open research questions | |
| 84 | 84 | ||
| @@ -104,7 +104,7 @@ | |||
| 104 | 104 | ||
| 105 | 105 | Almost nothing. The architectural decisions already made in [COMPONENT-LIBRARY.md](COMPONENT-LIBRARY.md) keep the door open: | |
| 106 | 106 | ||
| 107 | - | - Tokens live in a TOML file, not in Rust source. Any tool that emits a valid theme-common theme file is a viable producer. | |
| 107 | + | - Tokens live in a TOML file, not in Rust source. Any tool that emits a valid makeover theme file is a viable producer. | |
| 108 | 108 | - The crate exposes a small, stable set of layout combinators and themed primitives. These would be the vocabulary the composition grammar references. | |
| 109 | 109 | ||
| 110 | 110 | The further commitments Alloy *could* make to keep this even more open (small, low-cost, worth considering): |
| @@ -4,9 +4,9 @@ | |||
| 4 | 4 | ||
| 5 | 5 | The rules these tokens implement live in [DESIGN-LANGUAGE.md](DESIGN-LANGUAGE.md). This file is where the rules become storage, derivation, and gates. | |
| 6 | 6 | ||
| 7 | - | ## Storage format: theme-common | |
| 7 | + | ## Storage format: makeover | |
| 8 | 8 | ||
| 9 | - | Alloy consumes themes in the [theme-common](../../MNW/shared/theme-common/) TOML format, the same schema GoingsOn, Balanced Breakfast, audiofiles, and makenot.work already consume. A theme file has seven sections: | |
| 9 | + | Alloy consumes themes in the [makeover](https://git.sr.ht/~maxmj/makeover) TOML format, the same schema GoingsOn, Balanced Breakfast, audiofiles, and makenot.work already consume. A theme file has seven sections: | |
| 10 | 10 | ||
| 11 | 11 | ```toml | |
| 12 | 12 | [meta] | |
| @@ -45,13 +45,13 @@ | |||
| 45 | 45 | six = "#305858" | |
| 46 | 46 | ``` | |
| 47 | 47 | ||
| 48 | - | Alloy's default light theme is **[Akari Dawn](../../MNW/shared/themes/akari-dawn.toml)**; the default dark is **[Akari Night](../../MNW/shared/themes/akari-night.toml)**. Both are based on Shu Kutsuzawa's [Akari](https://github.com/cappyzawa/akari-theme) (MIT). Users pick a mode at startup (`--theme` flag or `$COLORFGBG` / OSC background query); users can also drop any theme-common `.toml` into `~/.config/alloy/themes/` and pass its name to swap. | |
| 48 | + | Alloy's default light theme is **[Akari Dawn](https://git.sr.ht/~maxmj/makeover/tree/main/item/themes/akari-dawn.toml)**; the default dark is **[Akari Night](https://git.sr.ht/~maxmj/makeover/tree/main/item/themes/akari-night.toml)**. Both are based on Shu Kutsuzawa's [Akari](https://github.com/cappyzawa/akari-theme) (MIT). Users pick a mode at startup (`--theme` flag or `$COLORFGBG` / OSC background query); users can also drop any makeover `.toml` into `~/.config/alloy/themes/` and pass its name to swap. | |
| 49 | 49 | ||
| 50 | - | **Why theme-common as the storage format:** the future user-facing theme catalog (a website of downloadable `.toml` files) is only useful if every make-family app renders any downloaded theme the same way. Sharing the format across apps buys that with zero adapter code. | |
| 50 | + | **Why makeover as the storage format:** the future user-facing theme catalog (a website of downloadable `.toml` files) is only useful if every make-family app renders any downloaded theme the same way. Sharing the format across apps buys that with zero adapter code. | |
| 51 | 51 | ||
| 52 | 52 | ## Alloy-specific derived tokens | |
| 53 | 53 | ||
| 54 | - | Alloy needs three token tiers theme-common ships one of: `border-subtle`, `border`, `border-strong`. Rather than extend the schema (which would break existing consumers), Alloy computes the outer two from `line.border` at load time: | |
| 54 | + | Alloy needs three token tiers makeover ships one of: `border-subtle`, `border`, `border-strong`. Rather than extend the schema (which would break existing consumers), Alloy computes the outer two from `line.border` at load time: | |
| 55 | 55 | ||
| 56 | 56 | ``` | |
| 57 | 57 | border-subtle = mix(line.border, surface.page, 60%) # pulled toward surface — decorative divider | |
| @@ -74,12 +74,12 @@ | |||
| 74 | 74 | ||
| 75 | 75 | ## Gate: the WCAG audit | |
| 76 | 76 | ||
| 77 | - | Any theme, downloaded or authored, is audited by [`tools/wcag_audit.py`](../tools/wcag_audit.py). It reads a theme-common `.toml`, converts hex to linear sRGB to WCAG 2.1 relative luminance, and reports pass/fail for every affordance-carrying pair (text-on-surface, borders-on-surface, accents-on-surface, elevation deltas). Also computes `border-subtle` / `border-strong` via the same derivation Alloy uses at runtime. | |
| 77 | + | Any theme, downloaded or authored, is audited by [`tools/wcag_audit.py`](../tools/wcag_audit.py). It reads a makeover `.toml`, converts hex to linear sRGB to WCAG 2.1 relative luminance, and reports pass/fail for every affordance-carrying pair (text-on-surface, borders-on-surface, accents-on-surface, elevation deltas). Also computes `border-subtle` / `border-strong` via the same derivation Alloy uses at runtime. | |
| 78 | 78 | ||
| 79 | 79 | Run: | |
| 80 | 80 | ||
| 81 | 81 | ``` | |
| 82 | - | python3 tools/wcag_audit.py MNW/shared/themes/akari-dawn.toml | |
| 82 | + | python3 tools/wcag_audit.py ../Libraries/makeover/themes/akari-dawn.toml | |
| 83 | 83 | ``` | |
| 84 | 84 | ||
| 85 | 85 | The audit is not a hard-fail gate: a theme with accents that only reach AA-UI on some surface tiers is still shippable if the accent-on-glyph rule is honored. The audit's job is to make trade-offs visible. | |
| @@ -133,9 +133,9 @@ | |||
| 133 | 133 | ||
| 134 | 134 | ## How this file is consumed | |
| 135 | 135 | ||
| 136 | - | Themes at [`MNW/shared/themes/*.toml`](../../MNW/shared/themes/) are the ground truth. `alloy_tui` at load time: | |
| 136 | + | The themes [makeover ships](https://git.sr.ht/~maxmj/makeover/tree/main/item/themes/) are the ground truth. `alloy_tui` at load time: | |
| 137 | 137 | ||
| 138 | - | 1. Reads the selected theme file via the `theme_common` crate. | |
| 138 | + | 1. Reads the selected theme file via the `makeover` crate. | |
| 139 | 139 | 2. Computes the two derived tokens (`border-subtle`, `border-strong`) from `line.border`. | |
| 140 | 140 | 3. Exposes the full token map as a runtime `Theme` of ratatui `Style` / `Color` values. | |
| 141 | 141 | 4. Optionally emits a 256-color fallback table for terminals without truecolor. |
| @@ -1,20 +1,20 @@ | |||
| 1 | 1 | #!/usr/bin/env python3 | |
| 2 | - | """WCAG 2.1 contrast audit for theme-common theme files. | |
| 2 | + | """WCAG 2.1 contrast audit for makeover theme files. | |
| 3 | 3 | ||
| 4 | - | Reads any theme-common .toml (surface/content/action/status/line/ | |
| 4 | + | Reads any makeover .toml (surface/content/action/status/line/ | |
| 5 | 5 | category sections), converts hex to WCAG 2.1 relative luminance, | |
| 6 | 6 | and reports pass/fail against AA-text (>= 4.5) and AA-UI (>= 3.0) | |
| 7 | 7 | for every affordance-carrying token pair. | |
| 8 | 8 | ||
| 9 | 9 | Also computes Alloy's derived tokens (border-subtle, border-strong) | |
| 10 | - | via mix formulas and audits those too, so a theme-common file that | |
| 10 | + | via mix formulas and audits those too, so a makeover file that | |
| 11 | 11 | was authored without Alloy's discipline still gets a full report. | |
| 12 | 12 | ||
| 13 | 13 | Usage: | |
| 14 | 14 | python3 tools/wcag_audit.py <path/to/theme.toml> | |
| 15 | 15 | ||
| 16 | 16 | Example: | |
| 17 | - | python3 tools/wcag_audit.py ../../MNW/shared/themes/akari-dawn.toml | |
| 17 | + | python3 tools/wcag_audit.py https://git.sr.ht/~maxmj/makeover/tree/main/item/themes/akari-dawn.toml | |
| 18 | 18 | """ | |
| 19 | 19 | import sys | |
| 20 | 20 | import os | |
| @@ -77,11 +77,11 @@ | |||
| 77 | 77 | # ---------------------------------------------------------------- derivation | |
| 78 | 78 | ||
| 79 | 79 | def derive(theme): | |
| 80 | - | """Compute Alloy's extended tokens from a theme-common theme. | |
| 80 | + | """Compute Alloy's extended tokens from a makeover theme. | |
| 81 | 81 | ||
| 82 | - | theme-common ships one border tone (line.border); Alloy renders | |
| 82 | + | makeover ships one border tone (line.border); Alloy renders | |
| 83 | 83 | three tiers via mix. Formula lives here (not in the theme file) | |
| 84 | - | so any theme-common .toml downloaded from the wild gets a full | |
| 84 | + | so any makeover .toml downloaded from the wild gets a full | |
| 85 | 85 | Alloy-shaped token map. | |
| 86 | 86 | """ | |
| 87 | 87 | border = theme["line"]["border"] |
| @@ -19,4 +19,4 @@ | |||
| 19 | 19 | ratatui.workspace = true | |
| 20 | 20 | serde = { version = "1.0.229", features = ["derive"] } | |
| 21 | 21 | serde_json = "1.0.150" | |
| 22 | - | theme-common.workspace = true | |
| 22 | + | makeover.workspace = true |
| @@ -1,7 +1,7 @@ | |||
| 1 | 1 | [package] | |
| 2 | 2 | name = "alloy_tui" | |
| 3 | 3 | version = "0.0.0" | |
| 4 | - | description = "Alloy design system: theme-common intents rendered as ratatui Color/Style, plus themed widgets for the alloy console and siblings." | |
| 4 | + | description = "Alloy design system: makeover intents rendered as ratatui Color/Style, plus themed widgets for the alloy console and siblings." | |
| 5 | 5 | edition.workspace = true | |
| 6 | 6 | rust-version.workspace = true | |
| 7 | 7 | license.workspace = true | |
| @@ -10,4 +10,4 @@ | |||
| 10 | 10 | ||
| 11 | 11 | [dependencies] | |
| 12 | 12 | ratatui.workspace = true | |
| 13 | - | theme-common.workspace = true | |
| 13 | + | makeover.workspace = true |