🤖 I have created a release *beep* *boop* --- ## [4.9.0](https://github.com/unraid/api/compare/v4.8.0...v4.9.0) (2025-07-08) ### Features * add graphql resource for API plugins ([#1420](https://github.com/unraid/api/issues/1420)) ([642a220](642a220c3a)) * add management page for API keys ([#1408](https://github.com/unraid/api/issues/1408)) ([0788756](0788756b91)) * add rclone ([#1362](https://github.com/unraid/api/issues/1362)) ([5517e75](5517e7506b)) * API key management ([#1407](https://github.com/unraid/api/issues/1407)) ([d37dc3b](d37dc3bce2)) * api plugin management via CLI ([#1416](https://github.com/unraid/api/issues/1416)) ([3dcbfbe](3dcbfbe489)) * build out docker components ([#1427](https://github.com/unraid/api/issues/1427)) ([711cc9a](711cc9ac92)) * docker and info resolver issues ([#1423](https://github.com/unraid/api/issues/1423)) ([9901039](9901039a38)) * fix shading in UPC to be less severe ([#1438](https://github.com/unraid/api/issues/1438)) ([b7c2407](b7c2407840)) * info resolver cleanup ([#1425](https://github.com/unraid/api/issues/1425)) ([1b279bb](1b279bbab3)) * initial codeql setup ([#1390](https://github.com/unraid/api/issues/1390)) ([2ade7eb](2ade7eb527)) * initialize claude code in codebse ([#1418](https://github.com/unraid/api/issues/1418)) ([b6c4ee6](b6c4ee6eb4)) * move api key fetching to use api key service ([#1439](https://github.com/unraid/api/issues/1439)) ([86bea56](86bea56272)) * move to cron v4 ([#1428](https://github.com/unraid/api/issues/1428)) ([b8035c2](b8035c207a)) * move to iframe for changelog ([#1388](https://github.com/unraid/api/issues/1388)) ([fcd6fbc](fcd6fbcdd4)) * native slackware package ([#1381](https://github.com/unraid/api/issues/1381)) ([4f63b4c](4f63b4cf3b)) * send active unraid theme to docs ([#1400](https://github.com/unraid/api/issues/1400)) ([f71943b](f71943b62b)) * slightly better watch mode ([#1398](https://github.com/unraid/api/issues/1398)) ([881f1e0](881f1e0960)) * upgrade nuxt-custom-elements ([#1461](https://github.com/unraid/api/issues/1461)) ([345e83b](345e83bfb0)) * use bigint instead of long ([#1403](https://github.com/unraid/api/issues/1403)) ([574d572](574d572d65)) ### Bug Fixes * activation indicator removed ([5edfd82](5edfd823b8)) * alignment of settings on ManagementAccess settings page ([#1421](https://github.com/unraid/api/issues/1421)) ([70c790f](70c790ff89)) * allow rclone to fail to initialize ([#1453](https://github.com/unraid/api/issues/1453)) ([7c6f02a](7c6f02a5cb)) * always download 7.1 versioned files for patching ([edc0d15](edc0d1578b)) * api `pnpm type-check` ([#1442](https://github.com/unraid/api/issues/1442)) ([3122bdb](3122bdb953)) * **api:** connect config `email` validation ([#1454](https://github.com/unraid/api/issues/1454)) ([b9a1b9b](b9a1b9b087)) * backport unraid/webgui[#2269](https://github.com/unraid/api/issues/2269) rc.nginx update ([#1436](https://github.com/unraid/api/issues/1436)) ([a7ef06e](a7ef06ea25)) * bigint ([e54d27a](e54d27aede)) * config migration from `myservers.cfg` ([#1440](https://github.com/unraid/api/issues/1440)) ([c4c9984](c4c99843c7)) * **connect:** fatal race-condition in websocket disposal ([#1462](https://github.com/unraid/api/issues/1462)) ([0ec0de9](0ec0de982f)) * **connect:** mothership connection ([#1464](https://github.com/unraid/api/issues/1464)) ([7be8bc8](7be8bc84d3)) * console hidden ([9b85e00](9b85e009b8)) * debounce is too long ([#1426](https://github.com/unraid/api/issues/1426)) ([f12d231](f12d231e63)) * delete legacy connect keys and ensure description ([22fe91c](22fe91cd56)) * **deps:** pin dependencies ([#1465](https://github.com/unraid/api/issues/1465)) ([ba75a40](ba75a409a4)) * **deps:** pin dependencies ([#1470](https://github.com/unraid/api/issues/1470)) ([412b329](412b32996d)) * **deps:** storybook v9 ([#1476](https://github.com/unraid/api/issues/1476)) ([45bb49b](45bb49bcd6)) * **deps:** update all non-major dependencies ([#1366](https://github.com/unraid/api/issues/1366)) ([291ee47](291ee475fb)) * **deps:** update all non-major dependencies ([#1379](https://github.com/unraid/api/issues/1379)) ([8f70326](8f70326d0f)) * **deps:** update all non-major dependencies ([#1389](https://github.com/unraid/api/issues/1389)) ([cb43f95](cb43f95233)) * **deps:** update all non-major dependencies ([#1399](https://github.com/unraid/api/issues/1399)) ([68df344](68df344a4b)) * **deps:** update dependency @types/diff to v8 ([#1393](https://github.com/unraid/api/issues/1393)) ([00da27d](00da27d04f)) * **deps:** update dependency cache-manager to v7 ([#1413](https://github.com/unraid/api/issues/1413)) ([9492c2a](9492c2ae6a)) * **deps:** update dependency commander to v14 ([#1394](https://github.com/unraid/api/issues/1394)) ([106ea09](106ea09399)) * **deps:** update dependency diff to v8 ([#1386](https://github.com/unraid/api/issues/1386)) ([e580f64](e580f646a5)) * **deps:** update dependency dotenv to v17 ([#1474](https://github.com/unraid/api/issues/1474)) ([d613bfa](d613bfa041)) * **deps:** update dependency lucide-vue-next to ^0.509.0 ([#1383](https://github.com/unraid/api/issues/1383)) ([469333a](469333acd4)) * **deps:** update dependency marked to v16 ([#1444](https://github.com/unraid/api/issues/1444)) ([453a5b2](453a5b2c95)) * **deps:** update dependency shadcn-vue to v2 ([#1302](https://github.com/unraid/api/issues/1302)) ([26ecf77](26ecf779e6)) * **deps:** update dependency vue-sonner to v2 ([#1401](https://github.com/unraid/api/issues/1401)) ([53ca414](53ca41404f)) * disable file changes on Unraid 7.2 ([#1382](https://github.com/unraid/api/issues/1382)) ([02de89d](02de89d130)) * do not start API with doinst.sh ([7d88b33](7d88b3393c)) * do not uninstall fully on 7.2 ([#1484](https://github.com/unraid/api/issues/1484)) ([2263881](22638811a9)) * drop console with terser ([a87d455](a87d455bac)) * error logs from `cloud` query when connect is not installed ([#1450](https://github.com/unraid/api/issues/1450)) ([719f460](719f460016)) * flash backup integration with Unraid Connect config ([#1448](https://github.com/unraid/api/issues/1448)) ([038c582](038c582aed)) * header padding regression ([#1477](https://github.com/unraid/api/issues/1477)) ([e791cc6](e791cc680d)) * incorrect state merging in redux store ([#1437](https://github.com/unraid/api/issues/1437)) ([17b7428](17b7428779)) * lanip copy button not present ([#1459](https://github.com/unraid/api/issues/1459)) ([a280786](a2807864ac)) * move to bigint scalar ([b625227](b625227913)) * node_modules dir removed on plugin update ([#1406](https://github.com/unraid/api/issues/1406)) ([7b005cb](7b005cbbf6)) * omit Connect actions in UPC when plugin is not installed ([#1417](https://github.com/unraid/api/issues/1417)) ([8c8a527](8c8a5276b4)) * parsing of `ssoEnabled` in state.php ([#1455](https://github.com/unraid/api/issues/1455)) ([f542c8e](f542c8e0bd)) * pin ranges ([#1460](https://github.com/unraid/api/issues/1460)) ([f88400e](f88400eea8)) * pr plugin promotion workflow ([#1456](https://github.com/unraid/api/issues/1456)) ([13bd9bb](13bd9bb567)) * proper fallback if missing paths config modules ([7067e9e](7067e9e3dd)) * rc.unraid-api now cleans up older dependencies ([#1404](https://github.com/unraid/api/issues/1404)) ([83076bb](83076bb940)) * remote access lifecycle during boot & shutdown ([#1422](https://github.com/unraid/api/issues/1422)) ([7bc583b](7bc583b186)) * sign out correctly on error ([#1452](https://github.com/unraid/api/issues/1452)) ([d08fc94](d08fc94afb)) * simplify usb listing ([#1402](https://github.com/unraid/api/issues/1402)) ([5355115](5355115af2)) * theme issues when sent from graph ([#1424](https://github.com/unraid/api/issues/1424)) ([75ad838](75ad8381bd)) * **ui:** notifications positioning regression ([#1445](https://github.com/unraid/api/issues/1445)) ([f73e5e0](f73e5e0058)) * use some instead of every for connect detection ([9ce2fee](9ce2fee380)) ### Reverts * revert package.json dependency updates from commit711cc9afor api and packages/* ([94420e4](94420e4d45)) --- This PR was generated with [Release Please](https://github.com/googleapis/release-please). See [documentation](https://github.com/googleapis/release-please#release-please). Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Unraid UI
A Vue 3 component library providing a set of reusable, accessible UI components for Unraid development.
Features
- ⚡️ Built with Vue 3 and TypeScript
- 🎭 Storybook documentation
- ✅ Tested components
- 🎪 Built on top of TailwindCSS and Shadcn/UI
Installation
Make sure you have the peer dependencies installed:
npm install vue@^3.3.0 tailwindcss@^3.0.0
Setup
1. Add CSS
Import the component library styles in your main entry file:
import '@unraid/ui/style.css';
2. Configure TailwindCSS
Create a tailwind.config.ts file with the following configuration:
import tailwindConfig from '@unraid/ui/tailwind.config.ts';
import type { Config } from 'tailwindcss';
export default {
presets: [tailwindConfig],
content: [
// ... your content paths
'./components/**/*.{js,vue,ts}',
'./layouts/**/*.vue',
'./pages/**/*.vue',
],
theme: {
extend: {
// your theme extensions
},
},
} satisfies Partial<Config>;
This configuration:
- Uses the Unraid UI library's Tailwind config as a preset
- Properly types your configuration with TypeScript
- Allows you to extend the base theme while maintaining all Unraid UI defaults
Usage
<script setup lang="ts">
import { Button } from '@unraid/ui';
</script>
<template>
<Button variant="primary"> Click me </Button>
</template>
Development
Local Development
Install dependencies:
npm install
Start Storybook development server:
npm run storybook
This will start Storybook at http://localhost:6006
Building
npm run build
Testing
Run tests:
npm run test
Run tests with UI:
npm run test:ui
Generate coverage report:
npm run coverage
Type Checking
npm run typecheck
Scripts
dev: Start development serverbuild: Build for productionpreview: Preview production buildtest: Run teststest:ui: Run tests with UIcoverage: Generate test coverageclean: Remove build artifactstypecheck: Run type checkingstorybook: Start Storybook development serverbuild-storybook: Build Storybook for production
License
Component Development
Installing Shadcn Components
- Install a new component using the Shadcn CLI:
npx shadcn-vue@latest add [component-name]
-
The component will be installed in the root components folder. Move it to the appropriate subfolder based on its type:
- Form components →
src/components/form/ - Layout components →
src/components/layout/ - Common components →
src/components/common/ - Brand components →
src/components/brand/
- Form components →
-
Update any imports in your codebase to reflect the new component location.
Component Variants Pattern
We use the class-variance-authority (CVA) package to manage component variants. Each component that supports variants should follow this pattern:
- Create a variants file (e.g.,
button.variants.ts):
import { cva } from 'class-variance-authority';
export const buttonVariants = cva('base-classes-here', {
variants: {
variant: {
primary: 'variant-specific-classes',
secondary: 'variant-specific-classes',
// ... other variants
},
size: {
sm: 'size-specific-classes',
md: 'size-specific-classes',
lg: 'size-specific-classes',
},
},
defaultVariants: {
variant: 'primary',
size: 'md',
},
});
- Use the variants in your component (e.g.,
Button.vue):
<script setup lang="ts">
import { computed } from "vue";
import { buttonVariants } from "./button.variants";
import { cn } from "@/lib/utils";
export interface ButtonProps {
variant?: "primary" | "secondary" | /* other variants */;
size?: "sm" | "md" | "lg";
class?: string;
}
const props = withDefaults(defineProps<ButtonProps>(), {
variant: "primary",
size: "md",
});
const buttonClass = computed(() => {
return cn(
buttonVariants({ variant: props.variant, size: props.size }),
props.class
);
});
</script>
<template>
<button :class="buttonClass">
<slot />
</button>
</template>
Storybook Development
We use Storybook for component development and documentation. To start the Storybook development server:
npm run storybook
This will start Storybook at http://localhost:6006
When creating stories for your components:
- Place story files in the
storiesdirectory - Name your story files as
ComponentName.stories.ts - Include examples of all variants and states
- Add documentation using JSDoc comments
Example story file:
import type { Meta, StoryObj } from '@storybook/vue3';
import { Button } from '../src/components/common/button';
const meta = {
title: 'Components/Button',
component: Button,
tags: ['autodocs'],
argTypes: {
variant: {
control: 'select',
options: ['primary', 'secondary', 'outline'],
},
size: {
control: 'select',
options: ['sm', 'md', 'lg'],
},
},
} satisfies Meta<typeof Button>;
export default meta;
type Story = StoryObj<typeof meta>;
export const Primary: Story = {
args: {
variant: 'primary',
size: 'md',
},
};
export const Secondary: Story = {
args: {
variant: 'secondary',
size: 'md',
},
};