@kiwiproject/vue-desktop
v1.0.1
Published
A Vue 3 + TypeScript desktop-style window manager library (plugin-based).
Downloads
4
Readme
Vue Desktop
A lightweight Vue 3 + TypeScript desktop-style window manager library with a plugin-based architecture.
Features
- Draggable & Resizable Windows — Move windows by title bar, resize from any edge or corner
- Window States — Minimize, maximize, restore with smooth transitions
- Focus Management — Click-to-focus, z-order stacking, Alt+Tab window switching
- Plugin Architecture — Extend functionality without bloating core
- Session Persistence — Save and restore window state across page reloads
- Keyboard Shortcuts — Configurable global shortcuts
- Window Snapping — Snap to edges, other windows, and grid
- Start Menu & Spotlight — Quick app launching and search
Installation
npm install @kiwiproject/vue-desktop
# or
pnpm add @kiwiproject/vue-desktopQuick Start
<template>
<div class="desktop-container">
<WindowHost />
<UISlot name="taskbar" />
</div>
</template>
<script setup lang="ts">
import { defineComponent, h } from 'vue'
import {
createDesktop,
provideDesktop,
WindowHost,
UISlot,
TaskbarPlugin
} from '@kiwiproject/vue-desktop'
import '@kiwiproject/vue-desktop/styles.css'
// Create a simple window component
const MyWindow = defineComponent({
props: { message: String },
setup(props) {
return () => h('div', props.message)
}
})
// Create desktop instance
const desktop = createDesktop()
// Install plugins
desktop.installPlugin(TaskbarPlugin)
// Provide to child components
provideDesktop(desktop)
// Create a window
desktop.createWindow({
type: 'my-window',
title: 'Hello World',
component: MyWindow,
props: { message: 'Welcome!' },
initialBounds: { x: 100, y: 100, width: 400, height: 300 }
})
</script>
<style>
.desktop-container {
width: 100vw;
height: 100vh;
position: relative;
}
</style>Core API
createDesktop()
Creates a new desktop instance that manages windows and plugins.
import { createDesktop } from '@kiwiproject/vue-desktop'
const desktop = createDesktop()provideDesktop(desktop) / useDesktop()
Vue provide/inject pattern for accessing the desktop instance in child components.
// In parent component
provideDesktop(desktop)
// In child component
const desktop = useDesktop()Desktop Instance Methods
// Window management
desktop.createWindow(definition) // Create a new window
desktop.closeWindow(id) // Close a window
desktop.getWindow(id) // Get window by ID
desktop.windows // Array of all windows
// Focus management
desktop.focusWindow(id) // Focus a window
desktop.getFocusedWindowId() // Get focused window ID
desktop.cycleFocus(reverse?) // Cycle through windows
// Window modes
desktop.minimizeWindow(id) // Minimize
desktop.maximizeWindow(id) // Maximize
desktop.restoreWindow(id) // Restore from min/max
desktop.getMode(id) // Get current mode
// Bounds
desktop.getBounds(id) // Get window bounds
desktop.updateBounds(id, bounds) // Update window bounds
// Plugins
desktop.installPlugin(plugin) // Install a plugin
desktop.uninstallPlugin(name) // Uninstall a plugin
desktop.hasPlugin(name) // Check if plugin installed
// UI Registry
desktop.registerUI(registration) // Register UI component
desktop.unregisterUI(id) // Unregister UI component
desktop.getUIForSlot(slot) // Get UIs for a slot
// Events
desktop.on(event, handler) // Subscribe to events
desktop.off(event, handler) // Unsubscribe from events
desktop.emit(event, payload) // Emit an eventWindow Definition
interface WindowDefinition {
id?: string // Auto-generated if not provided
type: string // Window type identifier
title: string // Window title
component: Component // Vue component to render
props?: Record<string, any> // Props for the component
icon?: string // Icon (emoji or URL)
singletonKey?: string // Unique key for singleton windows
initialBounds?: Partial<Bounds> // Initial position and size
constraints?: WindowConstraints // Min/max size constraints
behaviors?: WindowBehaviors // Draggable, resizable, etc.
meta?: Record<string, any> // Custom metadata
}
interface Bounds {
x: number
y: number
width: number
height: number
}
interface WindowConstraints {
minWidth?: number
minHeight?: number
maxWidth?: number
maxHeight?: number
}
interface WindowBehaviors {
draggable?: boolean // Default: true
resizable?: boolean // Default: true
closable?: boolean // Default: true
minimizable?: boolean // Default: true
maximizable?: boolean // Default: true
}Components
<WindowHost />
Renders all windows managed by the desktop instance. Place this inside your desktop container.
<template>
<div class="desktop">
<WindowHost />
</div>
</template><UISlot name="slot-name" />
Renders UI components registered to a specific slot by plugins.
<template>
<div class="desktop">
<WindowHost />
<UISlot name="taskbar" />
<UISlot name="overlay" />
</div>
</template>Plugins
TaskbarPlugin
Displays a taskbar showing all open windows with click-to-focus/minimize behavior.
import { TaskbarPlugin } from '@kiwiproject/vue-desktop'
desktop.installPlugin(TaskbarPlugin)The taskbar automatically appears at the bottom of the desktop. Requires <UISlot name="taskbar" />.
ShortcutsPlugin
Provides global keyboard shortcuts.
import { createShortcutsPlugin } from '@kiwiproject/vue-desktop'
// With default shortcuts
desktop.installPlugin(createShortcutsPlugin())
// Or customize
desktop.installPlugin(createShortcutsPlugin({
defaults: false // Disable default shortcuts
}))
// Register custom shortcuts
desktop.shortcuts.register({
id: 'my-shortcut',
keys: 'ctrl+shift+n',
description: 'Create new window',
handler: (desktop) => {
desktop.createWindow({ /* ... */ })
}
})Default shortcuts:
Ctrl+W— Close focused windowCtrl+M— Minimize focused windowCtrl+Shift+F— Toggle maximize
API:
desktop.shortcuts.register(definition) // Register shortcut
desktop.shortcuts.unregister(id) // Unregister shortcut
desktop.shortcuts.setEnabled(id, bool) // Enable/disable
desktop.shortcuts.getShortcuts() // List all shortcutsSnapPlugin
Enables window snapping to edges, other windows, and grid.
import { createSnapPlugin } from '@kiwiproject/vue-desktop'
desktop.installPlugin(createSnapPlugin({
edges: true, // Snap to viewport edges
windows: true, // Snap to other windows
grid: 20, // Snap to 20px grid (0 to disable)
threshold: 10, // Snap distance in pixels
getViewport: () => ({ // Custom viewport bounds
x: 0, y: 0,
width: window.innerWidth,
height: window.innerHeight - 48 // Account for taskbar
})
}))API:
desktop.snap.setEnabled(bool) // Enable/disable snapping
desktop.snap.isEnabled() // Check if enabled
desktop.snap.setOptions(options) // Update options
desktop.snap.getOptions() // Get current optionsPersistencePlugin
Saves and restores window state to localStorage (or custom storage).
import { createPersistencePlugin } from '@kiwiproject/vue-desktop'
desktop.installPlugin(createPersistencePlugin({
storageKey: 'my-app-desktop',
debounceMs: 500, // Debounce saves
persistSession: true, // Save open windows for restore
windowFactory: (info) => { // Factory for restoring windows
return {
type: info.type,
title: info.title,
component: getComponentByType(info.type),
props: info.props
}
}
}))
// Restore session on app start
onMounted(() => {
desktop.persistence.restoreSession()
})API:
desktop.persistence.save() // Force save
desktop.persistence.load() // Load state
desktop.persistence.clear() // Clear saved state
desktop.persistence.restoreSession() // Restore windows
desktop.persistence.getWindowState(id) // Get specific window state
desktop.persistence.setEnabled(bool) // Enable/disable
desktop.persistence.isEnabled() // Check if enabledCustom Storage Adapter:
import { createPersistencePlugin, createChainedAdapter } from '@kiwiproject/vue-desktop'
// Chain multiple adapters (first successful load wins, all get saves)
const adapter = createChainedAdapter(
myServerAdapter,
createLocalStorageAdapter('fallback-key')
)
desktop.installPlugin(createPersistencePlugin({
adapter
}))StartMenuPlugin
Adds a Start menu button to the taskbar for launching apps.
import { createStartMenuPlugin } from '@kiwiproject/vue-desktop'
desktop.installPlugin(createStartMenuPlugin({
buttonLabel: 'Start',
buttonIcon: '🪟',
apps: [
{
id: 'notepad',
label: 'Notepad',
icon: '📝',
category: 'Utilities',
shortcut: 'Ctrl+N',
factory: () => ({
type: 'notepad',
title: 'Notepad',
component: NotepadComponent,
singletonKey: 'notepad' // Only one instance
})
}
]
}))API:
desktop.startMenu.registerApp(app) // Register app
desktop.startMenu.unregisterApp(id) // Unregister app
desktop.startMenu.getApps() // Get all apps
desktop.startMenu.getAppsByCategory() // Get apps grouped by category
desktop.startMenu.open() // Open menu
desktop.startMenu.close() // Close menu
desktop.startMenu.toggle() // Toggle menu
desktop.startMenu.isOpen() // Check if openSpotlightPlugin
Adds a Spotlight-style search dialog (Cmd/Ctrl+K).
import { createSpotlightPlugin } from '@kiwiproject/vue-desktop'
desktop.installPlugin(createSpotlightPlugin({
placeholder: 'Search apps and windows...',
maxResults: 10
}))Requires <UISlot name="overlay" /> in your template.
Built-in providers:
- Apps from StartMenu plugin
- Open windows
Custom providers:
desktop.spotlight.registerProvider({
id: 'my-provider',
getItems: () => [
{
id: 'action-1',
label: 'Do Something',
icon: '⚡',
category: 'Actions',
description: 'Performs an action',
keywords: ['action', 'do'],
action: () => { /* ... */ }
}
]
})API:
desktop.spotlight.open() // Open spotlight
desktop.spotlight.close() // Close spotlight
desktop.spotlight.toggle() // Toggle spotlight
desktop.spotlight.isOpen() // Check if open
desktop.spotlight.search(query) // Programmatic search
desktop.spotlight.registerProvider(p) // Add custom provider
desktop.spotlight.unregisterProvider(id) // Remove provider
desktop.spotlight.getProviders() // List providersCreating Custom Plugins
Plugins are objects with a name and install function:
import type { Plugin, DesktopInstance } from '@kiwiproject/vue-desktop'
const MyPlugin: Plugin = {
name: 'my-plugin',
install(desktop: DesktopInstance) {
// Subscribe to events
desktop.on('window:created', (win) => {
console.log('Window created:', win.title)
})
// Register UI components
const unregisterUI = desktop.registerUI({
id: 'my-status-bar',
slot: 'overlay',
component: MyStatusBar,
order: 100
})
// Attach API to desktop instance
;(desktop as any).myPlugin = {
doSomething() { /* ... */ }
}
// Return cleanup function
return () => {
unregisterUI()
delete (desktop as any).myPlugin
}
}
}
// Usage
desktop.installPlugin(MyPlugin)Available events:
window:created— Window createdwindow:closed— Window closedwindow:focused— Window focusedwindow:blurred— Window lost focuswindow:moved— Window position changedwindow:resized— Window size changedwindow:minimized— Window minimizedwindow:maximized— Window maximizedwindow:restored— Window restored
Keyboard Shortcuts Reference
| Shortcut | Action |
|----------|--------|
| Cmd/Ctrl + K | Open Spotlight search |
| Alt + Tab | Switch between windows |
| Ctrl + W | Close focused window |
| Ctrl + M | Minimize focused window |
| Ctrl + Shift + F | Toggle maximize |
| Escape | Close dialogs |
Styling
Import the default styles:
import '@kiwiproject/vue-desktop/styles.css'All classes are prefixed with vd- for easy customization:
.vd-window-shell— Window container.vd-window-header— Title bar.vd-window-body— Content area.vd-window-focused— Focused window state.vd-taskbar— Taskbar container.vd-spotlight-overlay— Spotlight backdrop
TypeScript
Full TypeScript support with exported types:
import type {
// Core
Bounds,
WindowDefinition,
WindowConstraints,
WindowBehaviors,
WindowMode,
Plugin,
UIRegistration,
// Plugins
ShortcutDefinition,
SnapOptions,
StorageAdapter,
PersistedState,
StartMenuApp,
SpotlightItem,
SpotlightProvider,
// Extended desktop types
DesktopInstanceWithShortcuts,
DesktopInstanceWithSnap,
DesktopInstanceWithPersistence,
DesktopInstanceWithStartMenu,
DesktopInstanceWithSpotlight
} from '@kiwiproject/vue-desktop'Development
# Install dependencies
pnpm install
# Run demo
pnpm --filter ./examples/demo dev
# Run tests
pnpm test
# Build library
pnpm build
# Lint
pnpm lintLicense
MIT
