API Reference devaction_ui v#0.1.0

Copy Markdown View Source

Modules

Sections that open and close. Built on <details>, so it works with the keyboard, in assistive technology and with no JavaScript. In exclusive mode (the default) opening one closes the others, which the browser does itself through the shared name of the <details>.

The field that talks to the AI: a pill in ink with the amber dot, a text field and a round send button. It is a form, so Enter sends; the LiveView gets event with the text under name.

A short message under a form or at the top of a screen: it worked, the AI did something, here is how it is, this failed. Smaller than a Callout: one line of title and a line or two of text, with an icon. kind: :risk is announced as an alert; the others as a status.

A turning ring of light around something, for the AI at work. It wraps its content, which keeps its own surface; the ring is the wrapper's padding, painted by a conic gradient whose angle is animated.

The card of a sign-in screen: a header with a soft grid and an amber glow behind an icon tile, a title and a line of text, then the form, and optionally a quieter strip at the bottom for the other way in ("No account? Create one"). One screen is one card, or a card and a note under it.

The page around a sign-in screen: a framed panel with two columns. On the left the product (its mark and name, a headline and a few reasons to sign in); on the right whatever the screen needs, usually an AuthCard; along the bottom a copyright line and a few links. The columns wrap on a narrow screen, the product above the form.

Suggestions while you type, in groups (lessons, pull requests, tasks), with the part that matched in bold and an optional last row that asks the AI. The server owns the search: the field sends event (debounced) with the text, and the caller answers with groups; this component draws the field and the list and moves the active row with ↑ ↓, opens it with Enter (the row is a link) and runs the AI row with ⌘ + Enter (or Ctrl + Enter). Esc closes the list, typing opens it again.

A person as two letters in a circle, and the stack of them overlapping by 8px. The letters come from the name (initials/1); the tone only tells people apart, it never carries meaning. A circle with no name is decoration, so a stack names its people for assistive technology once, as a group.

Two small signals: count/1, the round bubble with a number (4 pull requests waiting), and dot/1, the status dot (connected, running). A dot can pulse, and stops under prefers-reduced-motion.

Where the page is: Today / SGE / PR #142. The items are %{label: ..., to: path}; the last one has no to and is the current page (aria-current="page").

The buttons of the canvas, as a button or as a link, in the same colors and sizes.

A month or a week of days with their events, as an agenda of deliveries: each event is a coloured chip by kind (risk, warning, ok, info, neutral). It is a grid (role="grid", rows, column headers, cells) with one cell in the tab order and the arrows, Home, End and Page keys moving between days (DevactionUI.Calendar hook); pressing a day, or Enter on it, sends select_event with phx-value-date, and the previous and next buttons send prev_event and next_event.

A tinted block that says something to the reader: a note, what the AI did, a tip, a warning, a decision. Color is never alone: it always carries a title or an icon slot, and an :alert role when it is urgent.

Light charts in plain SVG, drawn on the server: a line with two series, horizontal bars, columns, a ring and the sparkline of a number card. No library and no script: the hover is CSS (and keyboard focus shows the same).

The arithmetic of the charts, with no markup: where a value sits on an axis, the path of a line, an area, a column with a rounded top and a slice of a ring. Every function is pure, so the charts can be drawn on the server and tested without a browser.

One message of the conversation with the AI. The AI speaks on the left, with its mark beside the bubble and the corner nearest the mark squared; the person speaks on the right in ink. A message that did something (a lesson sent, a task made) is drawn :highlight, in the amber of "the AI did this".

Boxes to tick and options to pick, as the canvas draws them: the checkbox (with the partial state of "select all"), the radio with its description and the choice card. They are real <input>s, so the browser, the form and the keyboard do the work; the look comes from the checkbox, radio and choice-card classes.

A block of code in mono, given as the code attribute. lang: :heex colours it (components, slots, attributes, strings, expressions, comments) through DevactionUI.Highlight. tone: :dark is the ink block of a suggestion; :risk and :ok are the "your code" and "idiomatic" blocks of a lesson, tinted red and green. It keeps its own line breaks and scrolls sideways instead of widening the page.

A colour field with a panel: a square for saturation and brightness, a hue strip, an optional opacity strip, a row of preset colours and, in a browser that has one, the eyedropper. It always sends hex (#RRGGBB, or #RRGGBBAA with alpha) in the text field named name, so a form reads it like any other field and phx-change hears every change.

A select with a search box: the button shows the choice, the panel has a field that filters the list while you type (ignoring case and accents), ↑ ↓ move, Enter chooses, Esc closes, and an empty result says so. The value goes in a hidden input named name, and the hook fires input on it so phx-change hears it. Options are %{value: ..., label: ..., hint: ...}; hint is a small mono text at the right.

The ⌘K palette as a library piece: a dialog near the top with a large field, an optional row of scopes (all, pull requests, tasks...), the results in groups with their hints, an "ask the AI" action with the typed text and a footer that teaches the keys. ↑ ↓ move the active row, ↵ opens it, ⌘↵ asks the AI and Esc closes it.

A modal that asks before something that cannot be taken back, such as publishing a review to GitHub. The LiveView decides when it is there (:if={@confirming}); it takes the focus in, keeps it inside (Phoenix.Component.focus_wrap/1), closes with Esc or the close button, and gives the focus back to what opened it. The two buttons are the caller's, so the confirming one can say what it does ("Publish", not "OK").

The dialog before something that cannot be undone: stop monitoring a repository, delete a project. The red button stays locked until the person types the name of the thing, so it is never a reflex. A click outside does not close it, only Esc, Cancel and the close button do.

A table for lists that are worked on: sticky header, sortable columns (aria-sort, the server sorts and the table only draws the arrows), optional row selection with a tri-state header checkbox and a bulk bar that appears while something is selected, two densities and an empty state.

A month calendar that picks one day or a range, and the small chip that says how near a deadline is.

A thin line, or a line with a short word in the middle (the "OR" between a social button and a form). The word is in mono capitals and is given by the caller. A divider with a word is a separator for assistive technology, and the word is read out.

A panel at the side of the screen to edit without leaving it: a profile, a task, an item of a contract. The default is floating (12 px from the edges, rounded, with a footer that stays); it can sit docked against the edge, come from the left, and on a phone it is a sheet that rises from the bottom, with a handle.

A button that opens a menu of actions or a choice (sort by deadline, by value, by risk). The menu opens under the button; ↑ ↓ Home End move between items, Enter or Space chooses, Esc closes and returns to the button, a click outside closes it (DevactionUI.Dropdown hook).

The place to drop a file: a dashed area with an icon, what it takes and the limits. It wraps a LiveView upload (allow_upload/3), so the same area takes a drop and a click, and highlights while a file is over it (DevactionUI.Dropzone hook). What went wrong with a file is shown under it, from errors.

A rich text editor for delivery notes, scope and long comments: a toolbar over a TipTap (ProseMirror) surface, the way the canvas draws it (.editor, .editor-toolbar, .editor-btn, .editor-content).

What a list or a screen shows when there is nothing in it yet. Every empty one says what the area is, why it is empty and the one next step.

A button that opens a menu of filter groups; a group opens a submenu of ticks, checkboxes or radios. Ticking does not close the menu. The menu is a form: it sends on Apply (phx-submit), or on every tick with phx-change, as %{"filters" => %{"status" => ["progress"], "who" => ["CA"]}}. A count on each group and on the button follows the ticks, except for an option marked default, which is what the filter already means.

Splits HEEx source into tokens a code block can colour: {kind, text} pairs whose texts, joined, are the source again. It reads the shapes of a template (tags, components, slots, attributes, strings, {} and <% %> expressions, comments), not the Elixir inside them, so it is cheap and never fails: what it does not know is plain :text.

A text field with what the canvas puts around it: an icon at the start, an action at the end (show the password, clear, copy) and add-ons on either side (R$, https://, .devaction.app). The frame carries the focus ring, the error and the disabled look, so the field inside stays a plain <input>.

A board of columns with cards that move: drag a card to another column or to another place in its own, or focus it and press Alt + arrow keys (←→ between columns, ↑↓ within one). The hook moves the card in the page at once and tells the LiveView, which stays the source of truth: it receives the event with %{"card" => id, "from" => column, "to" => column, "index" => position} and re-renders. To refuse a move, answer with {:reply, %{rejected: true}, socket} and the hook puts the card back where it was.

A key cap: ⌘K, ESC, ↵. Mono 11px with a thicker bottom edge, as the canvas draws the keyboard hints.

A dialog for a decision that interrupts: confirm, tell that something worked, fill a short form. To edit without losing the context use DevactionUI.Drawer; to ask before something that cannot be undone use danger_dialog/1.

A field for Brazilian money: the person types digits and the field shows R$ 1.234,56, the last two digits being the cents (DevactionUI.MoneyMask in assets/js/devaction_ui). What is sent is the text as shown; the server reads it with parse/1.

A number field with a minus and a plus, for a quantity that is changed a step at a time (hours, seats, days): the buttons are disabled at the limits, the field is a spinbutton that says its value and its limits, and typing in it works as well. The LiveView keeps the value: it learns of a press in event (with phx-value-step -1 or 1) and of typing in the field's own phx-change.

The head of a screen as the canvas lays it out: the breadcrumb above, then the title with its status tag and the actions at the end, an optional line of text, and the tabs under it. It only places the pieces; each one is the caller's (a DevactionUI.Breadcrumb, a DevactionUI.Tag, buttons, tabs).

Page links: previous, a window of numbers around the current one with the first and last page and an ellipsis where it skips, and next. path is a function from a page number to the URL (or patch target).

The canvas card: a soft wrap around a bordered white panel.

The meter under a new-password field: four bars that fill as the password meets the rules, a line saying how strong it is, and the rules with a mark for each that is met. The server computes it from the text (evaluate/2) and the field sends it on phx-change; nothing is kept in the browser.

A field of removable pills, for labels, e-mails, stacks: type and press Enter (or a comma) to add one, Backspace on an empty field removes the last, the × on a pill removes that one. Each pill is a hidden input named name[], so the form submits the list as it stands; the hook fires input on the box so phx-change hears every change. The browser owns the pills after mount (phx-update="ignore"): give it a new id to reset it from the server.

A panel anchored to a button, with an arrow: the trigger opens and closes it, Esc and a click outside close it, focus goes into it when it opens and back to the trigger when it closes. It sits under (or over) the trigger inside the same box, so it is for a card, a form or a menu, not for something that has to escape a scrolling container.

A progress bar (role="progressbar") with a value from 0 to 100 and the tone it fills with: ink by default, amber for what the AI did, green when it is done. :segmented shows the same value in segments pieces (the steps of a job), :thin is the 2 px line of the work in the background, and indeterminate is for what has no end in sight: it shimmers (still, under reduced motion) and gives no value.

A notice for a job that takes a while: it fills as the job advances (progress, 0 to 100), with a ring of light while it runs. When the LiveView sets state to :done it waits a moment (1.6 s, paused while the pointer or the focus is on it), leaves, and tells the LiveView (on_dismiss, with phx-value-id) to drop it. An :error never leaves by itself: the person reads it and closes it.

A control to pick one of a few options that change the view at once (list or board, week or month): a pill with a segment per option. It is a group of toggle buttons with aria-pressed; the caller keeps the value and learns of a choice through event (phx-value-value). A segment can have an icon, a count, or only an icon (the label is then read by assistive technology). full stretches it to the width of its box.

The side menu of an app: 236 px wide, or 64 px when collapsed, where each item keeps its icon and shows its name in a tooltip on hover and on keyboard focus. The current item has aria-current="page". The caller keeps whether it is collapsed (collapsed) and gets the press of the toggle in on_toggle; it is hidden on small screens, where the app has its own navigation.

A field to sign: draw with the mouse, the finger or the pen, or type the name; clear and start again. It writes what was signed into hidden inputs for the form: the method, the typed name or the SVG of the stroke (DevactionUI.Signature hook, exportSvg). The SVG is the drawing only, in its own coordinates, so it can be kept and drawn back at any size. A drawing is accepted by the server only after safe_path?/1.

A placeholder for what is loading: a bar (:line), a round shape (:circle) or a block (:block), with a shimmer that stops under prefers-reduced-motion. Size it with class (h-4 w-40).

A slider for a value inside a range, and a range slider for two (a from and a to). Both are native <input type="range">, so the keyboard (arrows, Home, End, Page keys) and assistive technology work as the browser does them; the look is the canvas's. The value is shown beside the track and is the LiveView's, sent by the form's phx-change.

A spinning ring for work in progress. The label is read by screen readers and never drawn.

A number with its name: the mono caption over a large value, optionally with a small trend tag beside it. highlight is the inverted version, white on ink, for the one number a screen is about. The value is the slot, so it can carry a unit (7<span>/12</span>).

The steps of an onboarding or of a creation wizard, with the state of each: :done, :current, :error and :todo. Horizontal, each step is a bar over a dot, a label and a line of help; vertical, it is a checklist with a rail. The current step carries aria-current="step", and the state is also written (not only coloured) for assistive technology.

A row of steps with connectors: done (ink, with a check), the current one (amber ring) and those still to come (a thin outline). current counts from 1; the labels are the caller's text.

A switch for what takes effect at once, with no save button (for a choice that only counts after saving, use a checkbox). It is a <button role="switch">: aria-checked says the state, the keyboard toggles it and the caller does the work in its own phx-click. While the change is being kept, saving makes the thumb pulse and the control ignore presses; saved_note/1 is the "saved now" the caller shows after.

Tabs that switch panels without a round trip: variant: :boxed is the pill group, :line the underlined row. A small hook moves the selection with ← → Home End (roving tabindex, aria-selected) and shows the panel of the selected tab. Each tab may carry a count.

The small mono label of the canvas: EM RISCO, AGUARDANDO, EM DIA, APRENDIZADO, PLANEJADO.

A text area as the canvas draws it: it grows with its text (field-sizing: content, up to 12 lines), has an optional footer for actions (attach, mention, improve with the AI) and a counter that turns red near the limit. The label and the help text are the caller's; this is the field.

Things that happened, in order, on a thin line: each item has a dot in a tone, a title, a time and optional text.

A timer of hours for a task: a small caption of its state, the time in large mono digits, what is being timed, and two round buttons: start or pause, and stop and log. The LiveView owns the truth (it is not lost on a reload): the seconds already counted (base) and, while it runs, the moment it started (started_at, in seconds since the epoch). The browser only draws the passing seconds between two updates (DevactionUI.Timer hook); elapsed/3 is what the server counts when it logs.

Notices that pile up without covering the screen: while there is more than one they sit as a stack of layers with a count, and a click (or the button) opens them into a list with "Collapse" and "Clear". Each toast closes by itself after duration milliseconds (0 keeps it), pausing while the pointer or the focus is in the group or the list is open. Closing one, or clearing all, is sent to the LiveView (on_dismiss, on_clear), which is the source of truth: it removes the toast from what it renders.

The five tones of the canvas (:risk, :warn, :ok, :info, :neutral) as class strings, so every component that tints something (a callout, a finding, an alert, a progress bar) takes the same colors from the same tokens and flips with the theme.

A short hint on hover and on keyboard focus, drawn by CSS only. The trigger is the slot; text is what it says, tied to the trigger with aria-describedby. It is for a name or a short explanation, never for something the person must read to continue.