Skip to content

aivi.portal

Types for working with desktop portals.

Portals are the standard Linux desktop way to ask the system for privileged actions such as opening a file chooser, opening a URI, or taking a screenshot. They are especially useful for sandboxed apps.

This module exports the result and error types used by the built-in desktop portal sources.

Portal work stays under provider-backed @source variants rather than a separate task-first portal handle family.

Import

aivi
use aivi.portal (
    PortalError
    PortalFileFilter
    PortalFileSelection
    PortalUriResult
    PortalScreenshotResult
    PortalTask
)

Overview

ItemTypeDescription
PortalErrortypeThings that can go wrong while talking to a portal
PortalFileFilterrecordOne file chooser filter
PortalFileSelectiontypeResult of choosing files
PortalUriResulttypeResult of asking the desktop to open a URI
PortalScreenshotResulttypeResult of requesting a screenshot
PortalTask ATask PortalError AGeneric portal task alias
portal.openFileSignal (Result PortalError PortalFileSelection)Built-in file chooser source
portal.openUriSignal (Result PortalError PortalUriResult)Built-in URI-opening source
portal.screenshotSignal (Result PortalError PortalScreenshotResult)Built-in screenshot source

Types

PortalError

aivi
type PortalError =
  | PortalUnavailable
  | UserCancelled
  | PortalPermissionDenied Text
  | PortalDecodeFailed Text

These variants describe why a portal request could not complete.

  • PortalUnavailable — no usable portal backend is available
  • UserCancelled — the request was cancelled before a usable result was produced
  • PortalPermissionDenied — the desktop rejected the request
  • PortalDecodeFailed — the portal replied, but the data could not be decoded cleanly

PortalFileFilter

aivi
type PortalFileFilter = {
    name: Text,
    patterns: List Text
}

One filter option for a file chooser.

  • name — label shown to the user
  • patterns — filename patterns such as "*.aivi" or "*.png"
aivi
use aivi.portal (PortalFileFilter)

value aiviFiles : PortalFileFilter = {
    name: "AIVI Files",
    patterns: ["*.aivi"]
}

PortalFileSelection

aivi
type PortalFileSelection =
  | SingleFile Text
  | MultipleFiles (List Text)
  | SelectionCancelled

Result of a file chooser operation.

  • SingleFile — one selected path or URI
  • MultipleFiles — several selected paths or URIs
  • SelectionCancelled — chooser closed without a selection
aivi
use aivi.portal (
    PortalFileSelection
    SingleFile
    MultipleFiles
    SelectionCancelled
)

type PortalFileSelection -> Text
func selectionSummary = selection => selection
 ||> SingleFile path     -> path
 ||> MultipleFiles paths -> "Several files selected"
 ||> SelectionCancelled  -> "No file selected"

PortalUriResult

aivi
type PortalUriResult =
  | UriOpened Text
  | UriOpenCancelled
  | UriOpenFailed Text

Result of asking the desktop to open a URI.

  • UriOpened — the request was accepted for the given URI
  • UriOpenCancelled — the user or system cancelled the request
  • UriOpenFailed — the request failed with a message

PortalScreenshotResult

aivi
type PortalScreenshotResult =
  | ScreenshotBytes Bytes
  | ScreenshotCancelled

Result of a screenshot request.

  • ScreenshotBytes — screenshot bytes were returned
  • ScreenshotCancelled — the request was cancelled

PortalTask

aivi
type PortalTask A = (Task PortalError A)

Generic alias for portal-related tasks.

Built-in source shapes

These source variants are implemented today:

aivi
@source portal.openFile {
    title: "Open attachment"
    multiple: true
    filters: [{ name: "AIVI Files", patterns: ["*.aivi"] }]
}
signal openedFile : Signal (Result PortalError PortalFileSelection)

@source portal.openUri "https://example.com" with { ask: true }
signal uriResult : Signal (Result PortalError PortalUriResult)

@source portal.screenshot with { interactive: true }
signal screenshot : Signal (Result PortalError PortalScreenshotResult)

portal.openFile

Form: @source portal.openFile config

config is currently a record with these supported fields:

FieldTypeMeaning
titleTextDialog title. Default: "Open File"
acceptLabelTextCustom accept button label
modalBoolModal hint. Default: True
multipleBoolAllow multiple selection
directoryBoolPick folders instead of files
currentFolderTextSuggested starting folder path
filtersList PortalFileFilterGlob-based chooser filters

portal.openFile returns:

  • Ok (SingleFile uri) for one selected file
  • Ok (MultipleFiles uris) for multi-select
  • Ok SelectionCancelled if chooser is cancelled
  • Err PortalUnavailable | PortalPermissionDenied | PortalDecodeFailed for runtime/protocol failures

portal.openUri

Form: @source portal.openUri uri

Supported options:

OptionTypeMeaning
askBoolAsk desktop to prompt for app choice
writableBoolRequest writable handoff where supported
activationTokenTextForward activation token to launched app
refreshOnSignal AStandard request retrigger
activeWhenSignal BoolStandard lifecycle gate

portal.openUri returns Ok (UriOpened uri), Ok UriOpenCancelled, or Ok (UriOpenFailed message).

portal.screenshot

Form: @source portal.screenshot

Supported options:

OptionTypeMeaning
interactiveBoolAsk portal backend for interactive capture UI
modalBoolModal hint
refreshOnSignal AStandard request retrigger
activeWhenSignal BoolStandard lifecycle gate

portal.screenshot reads the portal-provided file URI and returns Ok (ScreenshotBytes bytes) or Ok ScreenshotCancelled.

(c) 2026 by Andreas Herd