@skylane/api
v0.2.0
Published
Widget component API for building Skylane widgets
Readme
@skylane/api
React component API for building Skylane widgets.
Install with:
npm install @skylane/apiThe default authoring model is component-first. Reach for Card, Field, List, EmptyState, Toolbar, and DropdownMenu before you drop to raw primitives like RoundedRect or Circle.
Quick Start
import {
Button,
Card,
CardContent,
CardDescription,
CardTitle,
DropdownMenu,
DropdownMenuItem,
DropdownMenuTriggerButton,
Field,
Input,
Overlay,
Section,
usePreference,
useLocalStorage,
} from "@skylane/api";
export default function Widget({ environment }) {
const [draft, setDraft] = useLocalStorage("draft", "");
const [mailbox] = usePreference("mailbox");
return (
<Section spacing="md">
<Card>
<Overlay placement="top-end" inset="sm">
<DropdownMenu trigger={<DropdownMenuTriggerButton symbol="ellipsis" appearance="overlay" />}>
<DropdownMenuItem>Edit</DropdownMenuItem>
</DropdownMenu>
</Overlay>
<CardContent>
<CardTitle>Hello from Skylane</CardTitle>
<CardDescription>{`Span ${environment.span} • ${mailbox ?? "Inbox"} • Draft ${draft.length}`}</CardDescription>
</CardContent>
</Card>
<Field>
<Input
value={draft}
placeholder="Capture a note"
onValueChange={setDraft}
/>
</Field>
<Button title="Clear" variant="secondary" onClick={() => setDraft("")} />
</Section>
);
}Primary UI Components
Card,CardHeader,CardTitle,CardDescription,CardContent,CardFooterSection,SectionHeader,SectionTitle,SectionDescriptionList,ListItem,ListItemTitle,ListItemDescription,ListItemActionOverlay,Field,Label,Description,EmptyState,Badge,Toolbar,ToolbarButtonDropdownMenu,DropdownMenuTriggerButton,DropdownMenuItem,DropdownMenuCheckboxItem,DropdownMenuLoadingItem,DropdownMenuErrorItem,DropdownMenuSeparator
Primitives And Hooks
Stack,Inline,SpacerText,Icon,ImageButton,Row,IconButton,Checkbox,InputScrollView,Divider,Circle,RoundedRect,Camera,MenuLocalStorageuseLocalStorage,usePreference,useCameras,useAudio,useMedia,useEvents,useEventCalendars,useTheme,usePromise,useFetchopenURL,connectTCP
Theme, Preferences, And Host Data
Widgets choose a manifest theme, and standard components resolve their styling from that theme automatically.
Use useTheme() only when you need advanced customization or bespoke composition. Most widgets should not need manual color assignments for standard controls or surfaces.
When you do need useTheme(), treat it as a compact semantic palette: primary is the strong CTA color, accent is the softer accent surface, and secondary / muted are neutral translucent layers.
The full token list and theme-family mapping live in widget-theme-mapping.md.
Use Overlay as a direct child of Card, Camera, Section, or other container components when you need layered affordances such as settings buttons or status badges.
Card, CardHeader, CardContent, CardFooter, Section, Field, List, and Toolbar accept semantic inset tokens such as none, sm, and lg so normal widgets rarely need raw padding objects.
When a bespoke mock needs custom control geometry, Button and IconButton support light-touch sizing overrides such as size, width, height, and cornerRadius. Button also supports fontSize and shape="pill", and IconButton supports iconSize.
For the common menu case, prefer DropdownMenu trigger={<DropdownMenuTriggerButton ... />}>...</DropdownMenu>. The explicit DropdownMenuTrigger / DropdownMenuContent compounds remain available for advanced composition.
React-style callback aliases are preferred:
Button,Row,IconButton,ListItem, and menu items supportonClickCheckboxandDropdownMenuCheckboxItemsupportonCheckedChangeInputsupportsonValueChangeandonSubmitValue
Use the host data APIs in four patterns:
usePreference("name")for manifest-backed configuration valuesuseCameras()for host-backed resources with selection stateuseAudio()for host-backed bundled audio playbackuseMedia()for host-backed now-playing state and transport controlsuseEvents(query)anduseEventCalendars()for EventKit-backed calendar dataconnectTCP(options)for host-mediated TLS TCP streamsusePromise()only for advanced custom async flows
useCameras() follows the shared resource shape: items, value, setValue, isLoading, isPending, error, refresh.
useAudio() follows the same pattern and returns playbackState, players, isLoading, isPending, error, refresh, plus play(), pause(), togglePlayPause(), stop(), setVolume(), pauseAll(), resumeAll(), and stopAll(). Audio sources must be widget-relative bundled assets such as assets/rain.wav.
useMedia() follows the same pattern and returns media state plus play(), pause(), togglePlayPause(), nextTrack(), previousTrack(), and openSourceApp(). Media updates are pushed from the host, action methods send transport commands without locally overwriting state, and refresh() is available for manual re-fetches when a widget wants an explicit sync point.
useEvents(query) returns a query-scoped event snapshot with authorizationStatus, accessLevel, items, isLoading, isPending, error, refresh, requestAccess(), openEvent(), and openSourceApp(). useEventCalendars() returns the same shared status fields plus normalized calendar metadata for building filters and legends. Both refetch when the host invalidates calendar data.
connectTCP({ host, port, serverName, timeoutMs }) opens a TLS-wrapped TCP connection for protocol clients such as IMAP. It returns a socket with write(body), read({ maxBytes, timeoutMs, signal }), and close().
import { Text, useAudio, useCameras, useMedia, usePreference } from "@skylane/api";
export default function Widget() {
const [mailbox] = usePreference("mailbox");
const audio = useAudio();
const cameras = useCameras();
const media = useMedia();
return (
<Text variant="body">
{mailbox ?? "Inbox"} • {cameras.value ?? "No Camera"} • {audio.playbackState} • {media.item?.title ?? "Nothing Playing"}
</Text>
);
}Learn By Example
Image Notes
Local widget images live under your package assets/ directory and can be referenced with paths like src="assets/cover.png".
Image supports local package assets, remote image URLs, and host-managed asset sources such as media.artwork?.src values like skylane-asset://image/<token>. contentMode="fill" is the default, and contentMode="fit" keeps the full image visible inside its frame.
Remote image notes:
- widgets use
https://URLs only - remote images are fetched by the host, not inside the widget runtime
- custom headers, cookies, and auth are not supported yet
