Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.skyvexsoftware.com/llms.txt

Use this file to discover all available pages before exploring further.

The UI module is a single React component exported as the default export from src/ui/index.tsx. The shell renders it in the main content area when a pilot selects your plugin in the sidebar.

What the Shell Provides

The shell injects several libraries at runtime — you don’t need to bundle them, and the build system externalises them automatically. Import them directly:
  • react, react-dom
  • @tanstack/react-query, @tanstack/react-router
  • @skyvexsoftware/stratos-sdk (hooks, UI components, types, helpers)
  • sonner (toast notifications)
  • socket.io-client
  • maplibre-gl, react-map-gl/maplibre
Anything else you import — lucide-react, your own utilities, third-party libraries — will be bundled into your plugin’s ui/index.js.

Using usePluginContext()

The primary way to access shell state from any component in your plugin:
import { usePluginContext } from "@skyvexsoftware/stratos-sdk";

function MyComponent() {
  const ctx = usePluginContext();

  // ctx.pluginId       — your plugin's ID string
  // ctx.auth           — { isAuthenticated, token, user }
  // ctx.airline        — { id, name, icao, logo_light, logo_dark } or null
  // ctx.config         — { get<T>(key, default?) }
  // ctx.navigation     — navigate within/between plugins
  // ctx.toast          — { success(), error(), info(), warning() }
  // ctx.logger         — { info(), warn(), error(), debug() }
}
usePluginContext() can be called in any component in your tree — not just the root. The context is provided by the PluginShellProvider that wraps your entire plugin.

Auth

const { auth } = usePluginContext();

if (!auth.isAuthenticated || !auth.user) {
  return <p>Please log in to use this plugin.</p>;
}

// auth.user: PluginPilotUser — pilot profile (firstName, lastName, pilotID, rank, avatar, etc.)
// auth.token: string | null  — VA auth token for API calls
You can also use the convenience hook:
import { useShellAuth } from "@skyvexsoftware/stratos-sdk";

const { isAuthenticated, token, user } = useShellAuth();

Calling the VA’s API

For HTTP calls to the bound airline’s API, use useVaApi(). It returns a pre-configured axios instance that attaches the bearer, sets the airline’s base_url, and transparently refreshes the access token on 401:
import { useVaApi } from "@skyvexsoftware/stratos-sdk";

const va = useVaApi();
const { data } = await va.get("/pilot/verify");
This is the renderer-side equivalent of ctx.airline.createClient() in background modules.

Config

Read plugin settings declared in your manifest’s availableSettings. Config access in the UI module is synchronous — values are pre-loaded when your plugin mounts:
const { config } = usePluginContext();

const welcomeMsg = config.get<string>("welcome_message", "Hello!");
const links = config.get<QuickLink[]>("quick_links", []);
The second argument to get() is the default value, returned when the setting has not been explicitly set.
Note: config.get() in the UI context reads airline-scoped plugin configuration — settings managed by the VA admin on the Skyvex platform. User-scoped settings (personal preferences like unit system, display toggles, and refresh intervals) are managed through the shell’s settings UI and are not accessible via this API.
Navigate within your plugin or jump to other plugins and shell routes:
const { navigation } = usePluginContext();

// Within your plugin (appends to /plugins/my-plugin/)
navigation.navigateTo("roster");
navigation.navigateTo("roster/pilot-123");

// To another plugin
navigation.navigateToPlugin("flight-tracking", "history");

// To a shell page
navigation.navigateToShell("/settings");

// Read the current path
const path = navigation.getCurrentPath();
For multi-page plugins, see the Routing page.

Toast Notifications

const { toast } = usePluginContext();

toast.success("Flight booked successfully");
toast.error("Failed to connect to VA server");
toast.info("New PIREP available");
toast.warning("Low fuel warning");
Or use the dedicated hook:
import { useShellToast } from "@skyvexsoftware/stratos-sdk";

const { success, error, info, warning } = useShellToast();

Styling

Plugins share the shell’s Tailwind CSS theme. Use standard Tailwind utility classes:
<div className="bg-card rounded-lg border p-6">
  <h2 className="text-lg font-semibold">Section Title</h2>
  <p className="text-muted-foreground mt-2 text-sm">Supporting text</p>
</div>
Key design tokens available via Tailwind classes:
ClassUsage
bg-cardCard and panel backgrounds
bg-mutedSubtle, secondary backgrounds
text-muted-foregroundSecondary and supporting text
borderStandard dividers and outlines
bg-primary / text-primaryAccent colour (configured per-airline)
bg-backgroundPage-level background
text-foregroundPrimary text
The shell switches between light and dark Tailwind themes automatically — your components inherit the correct variables without any additional work.

SDK UI Components

Import pre-built shadcn/ui components from the SDK to stay consistent with the shell’s design:
import {
  Button,
  Card,
  CardHeader,
  CardTitle,
  CardContent,
  Badge,
  Input,
  Label,
  Select,
  SelectTrigger,
  SelectValue,
  SelectContent,
  SelectItem,
  Tabs,
  TabsList,
  TabsTrigger,
  TabsContent,
  Dialog,
  DialogTrigger,
  DialogContent,
  DialogHeader,
  DialogTitle,
  Separator,
  Switch,
  Slider,
} from "@skyvexsoftware/stratos-sdk";
See UI Components for the full reference.

Minimal Example

import { usePluginContext, Card, CardContent } from "@skyvexsoftware/stratos-sdk";

export default function MyPlugin() {
  const { auth, airline, toast } = usePluginContext();

  if (!auth.isAuthenticated || !auth.user) {
    return (
      <div className="flex h-full items-center justify-center">
        <p className="text-muted-foreground">Please log in to continue.</p>
      </div>
    );
  }

  return (
    <div className="p-6 space-y-4">
      <h1 className="text-2xl font-bold">
        Welcome, {auth.user.firstName}
      </h1>

      <Card>
        <CardContent className="p-4">
          <p className="text-muted-foreground text-sm">
            Connected to {airline?.name ?? "your VA"}
          </p>
        </CardContent>
      </Card>

      <button
        className="text-sm text-primary underline"
        onClick={() => toast.success("Hello from My Plugin!")}
      >
        Send toast
      </button>
    </div>
  );
}