Compare commits
6 Commits
0be8ff5d80
...
main
Author | SHA1 | Date | |
---|---|---|---|
f569989fef | |||
f743f67454 | |||
fc2e48c6b0 | |||
6351c038b5 | |||
dbf190597b | |||
e1c0e7f633 |
155
.kiro/specs/workflow-drag-drop-fix/design.md
Normal file
@@ -0,0 +1,155 @@
|
||||
# Design Document
|
||||
|
||||
## Overview
|
||||
|
||||
The drag and drop functionality issue stems from incorrect vuedraggable configuration between the right panel (node library) and left panel (workflow). The current implementation has mismatched group configurations and improper clone handling that prevents nodes from being successfully transferred from the library to the workflow.
|
||||
|
||||
## Architecture
|
||||
|
||||
The fix involves correcting the vuedraggable configuration to properly support:
|
||||
1. **Clone-based dragging** from right panel to left panel
|
||||
2. **Proper group configuration** to allow cross-panel transfers
|
||||
3. **Unique ID generation** for cloned nodes
|
||||
4. **State management** for workflow updates
|
||||
|
||||
## Components and Interfaces
|
||||
|
||||
### Vuedraggable Configuration
|
||||
|
||||
#### Right Panel (Node Library)
|
||||
- **Group**: `{ name: 'workflow-nodes', pull: 'clone', put: false }`
|
||||
- **Clone function**: Custom clone function that generates unique IDs
|
||||
- **Sort**: `false` (prevent reordering in library)
|
||||
- **List**: Static node definitions (read-only)
|
||||
|
||||
#### Left Panel (Workflow)
|
||||
- **Group**: `{ name: 'workflow-nodes', pull: true, put: true }`
|
||||
- **Sort**: `true` (allow reordering)
|
||||
- **List**: Reactive workflow nodes array
|
||||
- **Animation**: 200ms for smooth transitions
|
||||
|
||||
### Node Cloning Logic
|
||||
|
||||
```typescript
|
||||
const cloneNode = (original: FlowNode): FlowNode => {
|
||||
return {
|
||||
...original,
|
||||
id: `${original.id}-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`,
|
||||
config: original.config ? { ...original.config } : undefined
|
||||
};
|
||||
};
|
||||
```
|
||||
|
||||
### Event Handling
|
||||
|
||||
#### Workflow Change Handler
|
||||
- Triggered when nodes are added, removed, or reordered
|
||||
- Updates reactive state
|
||||
- Logs changes for debugging
|
||||
- Validates workflow integrity
|
||||
|
||||
## Data Models
|
||||
|
||||
### FlowNode Interface (Existing)
|
||||
```typescript
|
||||
interface FlowNode {
|
||||
id: string // Unique identifier
|
||||
name: string // Display name
|
||||
type: string // Node type category
|
||||
taskId: string // Task execution identifier
|
||||
icon: string // Icon name
|
||||
backgroundColor: string
|
||||
shape: "square" | "circle" | "rounded"
|
||||
textColor: string
|
||||
category: "控制类" | "设备类" | "媒体类" | "灯光类" | "延时类"
|
||||
config?: Record<string, any>
|
||||
}
|
||||
```
|
||||
|
||||
### Workflow State
|
||||
```typescript
|
||||
const workflowNodes = ref<FlowNode[]>([]);
|
||||
```
|
||||
|
||||
## Error Handling
|
||||
|
||||
### Invalid Drop Operations
|
||||
- Validate node structure before adding to workflow
|
||||
- Handle malformed node data gracefully
|
||||
- Provide user feedback for failed operations
|
||||
|
||||
### ID Collision Prevention
|
||||
- Generate unique IDs using timestamp and random string
|
||||
- Validate uniqueness before adding to workflow
|
||||
- Handle edge cases where ID generation might fail
|
||||
|
||||
### Configuration Preservation
|
||||
- Deep clone node configurations to prevent reference issues
|
||||
- Validate configuration structure
|
||||
- Handle missing or invalid configuration data
|
||||
|
||||
## Testing Strategy
|
||||
|
||||
### Unit Tests
|
||||
1. **Clone Function Tests**
|
||||
- Verify unique ID generation
|
||||
- Confirm property preservation
|
||||
- Test configuration deep cloning
|
||||
|
||||
2. **Drag and Drop Tests**
|
||||
- Mock drag events
|
||||
- Verify node addition to workflow
|
||||
- Test reordering functionality
|
||||
|
||||
### Integration Tests
|
||||
1. **Cross-Panel Transfer**
|
||||
- Test dragging from right to left panel
|
||||
- Verify node appears in workflow
|
||||
- Confirm original node remains in library
|
||||
|
||||
2. **Workflow Management**
|
||||
- Test node reordering
|
||||
- Verify workflow state updates
|
||||
- Test node removal functionality
|
||||
|
||||
### Visual Tests
|
||||
1. **Drag Feedback**
|
||||
- Verify visual drag indicators
|
||||
- Test drop zone highlighting
|
||||
- Confirm cursor changes during drag
|
||||
|
||||
2. **Node Rendering**
|
||||
- Test node appearance in workflow
|
||||
- Verify icon and color preservation
|
||||
- Test configuration display
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
### Key Changes Required
|
||||
|
||||
1. **Fix Group Configuration**
|
||||
- Ensure consistent group names between panels
|
||||
- Configure proper pull/put permissions
|
||||
- Set up clone functionality for right panel
|
||||
|
||||
2. **Improve Clone Function**
|
||||
- Generate more robust unique IDs
|
||||
- Ensure deep cloning of configurations
|
||||
- Handle edge cases in node structure
|
||||
|
||||
3. **Update Event Handlers**
|
||||
- Properly handle workflow change events
|
||||
- Add validation for dropped nodes
|
||||
- Improve error handling and user feedback
|
||||
|
||||
4. **Visual Enhancements**
|
||||
- Add drag state indicators
|
||||
- Improve drop zone visual feedback
|
||||
- Enhance cursor states during operations
|
||||
|
||||
### Performance Considerations
|
||||
|
||||
- Use efficient ID generation to avoid blocking
|
||||
- Minimize re-renders during drag operations
|
||||
- Optimize large workflow handling
|
||||
- Cache node definitions for better performance
|
50
.kiro/specs/workflow-drag-drop-fix/requirements.md
Normal file
@@ -0,0 +1,50 @@
|
||||
# Requirements Document
|
||||
|
||||
## Introduction
|
||||
|
||||
This feature addresses the drag and drop functionality issue in the existing workflow visualization page where nodes from the right panel (node library) cannot be properly dragged to the left panel (task workflow). The current implementation has configuration issues with vuedraggable that prevent proper cloning and dropping of nodes between panels.
|
||||
|
||||
## Requirements
|
||||
|
||||
### Requirement 1
|
||||
|
||||
**User Story:** As a user, I want to drag nodes from the right panel node library to the left panel task workflow, so that I can create a visual workflow sequence.
|
||||
|
||||
#### Acceptance Criteria
|
||||
|
||||
1. WHEN I drag a node from the right panel THEN the system SHALL create a visual clone that follows my cursor
|
||||
2. WHEN I drop a cloned node onto the left panel THEN the system SHALL add the node to the workflow list
|
||||
3. WHEN I drop a cloned node onto the left panel THEN the system SHALL preserve all node properties (name, icon, colors, configuration)
|
||||
4. WHEN I drop a cloned node onto the left panel THEN the system SHALL generate a unique ID for the new instance
|
||||
5. IF the original node has editable configuration THEN the cloned node SHALL maintain the editable configuration capability
|
||||
|
||||
### Requirement 2
|
||||
|
||||
**User Story:** As a user, I want to reorder nodes within the left panel workflow, so that I can adjust the execution sequence of my tasks.
|
||||
|
||||
#### Acceptance Criteria
|
||||
|
||||
1. WHEN I drag a node within the left panel THEN the system SHALL allow reordering of workflow nodes
|
||||
2. WHEN I reorder nodes in the left panel THEN the system SHALL maintain all node properties and configurations
|
||||
3. WHEN I reorder nodes in the left panel THEN the system SHALL update the workflow sequence immediately
|
||||
|
||||
### Requirement 3
|
||||
|
||||
**User Story:** As a user, I want the right panel nodes to remain unchanged when dragging, so that I can reuse the same node type multiple times.
|
||||
|
||||
#### Acceptance Criteria
|
||||
|
||||
1. WHEN I drag a node from the right panel THEN the original node SHALL remain in the right panel
|
||||
2. WHEN I drag a node from the right panel THEN the system SHALL create a copy rather than moving the original
|
||||
3. WHEN I drag multiple instances of the same node type THEN each instance SHALL have a unique identifier
|
||||
|
||||
### Requirement 4
|
||||
|
||||
**User Story:** As a user, I want visual feedback during drag operations, so that I understand where I can drop nodes and what will happen.
|
||||
|
||||
#### Acceptance Criteria
|
||||
|
||||
1. WHEN I start dragging a node THEN the system SHALL provide visual feedback indicating drag state
|
||||
2. WHEN I hover over a valid drop zone THEN the system SHALL highlight the drop zone
|
||||
3. WHEN I hover over an invalid drop zone THEN the system SHALL indicate the zone is not droppable
|
||||
4. WHEN dragging is in progress THEN the cursor SHALL change to indicate drag operation
|
105
.kiro/specs/workflow-drag-drop-fix/tasks.md
Normal file
@@ -0,0 +1,105 @@
|
||||
# Implementation Plan
|
||||
|
||||
- [x] 1. Fix vuedraggable group configuration for cross-panel transfers
|
||||
|
||||
- Update right panel draggable configuration to use proper group settings with clone functionality
|
||||
- Update left panel draggable configuration to accept nodes from right panel
|
||||
- Ensure group names are consistent between both panels
|
||||
- _Requirements: 1.1, 1.2, 3.1, 3.2_
|
||||
|
||||
- [x] 2. Improve node cloning function for unique ID generation
|
||||
|
||||
- Enhance cloneNode function to generate more robust unique IDs using timestamp and random string
|
||||
- Implement deep cloning of node configurations to prevent reference issues
|
||||
- Add validation to ensure cloned nodes maintain all required properties
|
||||
- _Requirements: 1.4, 3.3_
|
||||
|
||||
- [x] 3. Fix draggable list binding and event handling
|
||||
|
||||
- Correct the list binding for right panel draggable to prevent modification of original nodes
|
||||
- Update workflow change handler to properly process added nodes
|
||||
- Add validation for dropped nodes to ensure data integrity
|
||||
- _Requirements: 1.2, 1.3, 2.2_
|
||||
|
||||
- [x] 4. Add visual feedback and drag state indicators
|
||||
|
||||
- Implement drag state visual feedback with appropriate cursor changes
|
||||
- Add drop zone highlighting for better user experience
|
||||
- Enhance drag operation visual indicators
|
||||
- _Requirements: 4.1, 4.2, 4.3, 4.4_
|
||||
|
||||
- [x] 5. Test and validate drag and drop functionality
|
||||
- Test dragging nodes from right panel to left panel
|
||||
- Verify node reordering within left panel works correctly
|
||||
- Validate that original nodes remain unchanged in right panel
|
||||
- Test configuration preservation and editing functionality
|
||||
- _Requirements: 1.1, 1.2, 1.3, 1.5, 2.1, 2.2, 3.1, 3.2, 3.3_
|
||||
|
||||
- [x] 6. Fix workflow node drag and drop reordering
|
||||
- Enable proper drag and drop functionality for reordering nodes within the workflow panel
|
||||
- Configure vuedraggable with correct options for internal sorting
|
||||
- Add proper event handlers for drag operations
|
||||
- Remove unused drag event handlers to fix linting issues
|
||||
- _Requirements: 1.2, 1.3, 2.2_
|
||||
|
||||
- [x] 7. Implement independent scrolling for left and right panels
|
||||
- Separate scroll containers for left workflow panel and right node library
|
||||
- Ensure left panel scrolling doesn't affect right panel and vice versa
|
||||
- Maintain proper layout and responsive behavior
|
||||
- Fix overflow handling for both panels
|
||||
- _Requirements: 4.1, 4.2_
|
||||
|
||||
- [x] 8. Fix drag and drop sorting functionality
|
||||
- Configure vuedraggable with proper options for smooth sorting
|
||||
- Add force-fallback option to ensure consistent behavior
|
||||
- Improve drag handle visibility and interaction
|
||||
- Fix drag event handling for proper reordering
|
||||
- _Requirements: 1.2, 1.3, 2.2_
|
||||
|
||||
- [x] 9. Remove left panel collapse functionality
|
||||
- Remove collapse/expand button and related functionality
|
||||
- Simplify left panel layout to fixed width
|
||||
- Clean up unused state variables and imports
|
||||
- _Requirements: 4.1_
|
||||
|
||||
- [x] 10. Implement desktop application UX
|
||||
- Disable text selection throughout the application
|
||||
- Prevent web-like drag selection behavior
|
||||
- Maintain text selection only for input fields
|
||||
- Add proper cursor states for drag operations
|
||||
- _Requirements: 4.1, 4.2, 4.3_
|
||||
|
||||
- [x] 11. Fix button interaction conflicts with drag handle
|
||||
- Separate drag handle area from button interaction area
|
||||
- Ensure delete and edit buttons are clickable and not blocked by drag area
|
||||
- Add proper z-index and pointer-events handling
|
||||
- Improve visual feedback for buttons and drag handle
|
||||
- _Requirements: 1.3, 2.2, 4.3_
|
||||
|
||||
- [x] 12. Implement Tauri-based file operations for import/export
|
||||
- Configure Tauri permissions for fs and dialog plugins
|
||||
- Replace web-based file operations with native Tauri APIs
|
||||
- Implement save dialog for workflow export
|
||||
- Implement open dialog for workflow import
|
||||
- Add proper error handling and user feedback
|
||||
- _Requirements: 2.1, 2.2, 3.1, 3.2_
|
||||
|
||||
- [x] 13. Enhance workflow execution and JSON output
|
||||
- Improve startTask function to output detailed JSON to console
|
||||
- Add metadata like creation time and version to exported workflows
|
||||
- Implement proper workflow validation during import
|
||||
- Add comprehensive logging for debugging and monitoring
|
||||
- _Requirements: 1.5, 2.1, 2.2_
|
||||
|
||||
- [x] 14. Fix Tauri plugin configuration and dependencies
|
||||
- Correct Tauri 2.0 plugin configuration format in tauri.conf.json
|
||||
- Add dialog plugin dependency to Cargo.toml
|
||||
- Initialize dialog plugin in lib.rs
|
||||
- Remove invalid configuration fields that caused startup errors
|
||||
- _Requirements: 2.1, 3.1_
|
||||
- [x] 8. Fix Tauri 2.0 permissions configuration structure
|
||||
- Move permissions to app section in tauri.conf.json (not root level)
|
||||
- Add dialog plugin to plugins section
|
||||
- Resolve "Additional properties are not allowed" configuration error
|
||||
- Ensure proper Tauri 2.0 configuration format compliance
|
||||
- _Requirements: 2.1, 3.1_
|
@@ -1,8 +1,8 @@
|
||||
export default defineAppConfig({
|
||||
app: {
|
||||
name: "Nuxtor",
|
||||
author: "Nicola Spadari",
|
||||
repo: "https://github.com/NicolaSpadari/nuxtor",
|
||||
name: "PlayTool-Demo",
|
||||
author: "Estel",
|
||||
repo: "https://github.com/estel-li",
|
||||
tauriSite: "https://tauri.app",
|
||||
nuxtSite: "https://nuxt.com",
|
||||
nuxtUiSite: "https://ui.nuxt.dev"
|
||||
|
@@ -1,10 +0,0 @@
|
||||
import type { NodeDefinition } from "~/types/flow";
|
||||
|
||||
export default {
|
||||
type: "delay",
|
||||
label: "延时",
|
||||
category: "控制",
|
||||
inputs: [{ id: "in", label: "In", type: "flow" }],
|
||||
outputs: [{ id: "out", label: "Out", type: "flow" }],
|
||||
defaultConfig: { milliseconds: 1000 }
|
||||
} as NodeDefinition;
|
@@ -1,81 +0,0 @@
|
||||
<template>
|
||||
<div class="delay-node" :style="{ borderColor: node.data?.color || '#ff6b35' }">
|
||||
<div class="node-header">
|
||||
<svg class="node-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor">
|
||||
<circle cx="12" cy="12" r="10" stroke-width="2" />
|
||||
<path d="M12 6v6l4 2" stroke-width="2" stroke-linecap="round" />
|
||||
</svg>
|
||||
<span class="node-title">延时</span>
|
||||
</div>
|
||||
<div class="node-content">
|
||||
<span class="delay-time">{{ node.data?.config?.milliseconds || 1000 }}ms</span>
|
||||
</div>
|
||||
<Handle
|
||||
id="in"
|
||||
type="target"
|
||||
:position="Position.Top"
|
||||
class="handle"
|
||||
/>
|
||||
<Handle
|
||||
id="out"
|
||||
type="source"
|
||||
:position="Position.Bottom"
|
||||
class="handle"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { Handle, Position } from "@vue-flow/core";
|
||||
|
||||
const _props = defineProps<{
|
||||
node: any
|
||||
}>();
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.delay-node {
|
||||
background: white;
|
||||
border: 2px solid #ff6b35;
|
||||
border-radius: 8px;
|
||||
padding: 12px;
|
||||
min-width: 120px;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.node-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.node-icon {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
color: #ff6b35;
|
||||
}
|
||||
|
||||
.node-title {
|
||||
font-weight: 600;
|
||||
font-size: 14px;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.node-content {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.delay-time {
|
||||
font-size: 12px;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.handle {
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
background: #fff;
|
||||
border: 2px solid #ff6b35;
|
||||
border-radius: 50%;
|
||||
}
|
||||
</style>
|
@@ -1,10 +0,0 @@
|
||||
import type { NodeDefinition } from "~/types/flow";
|
||||
|
||||
export default {
|
||||
type: "log",
|
||||
label: "日志",
|
||||
category: "工具",
|
||||
inputs: [{ id: "in", label: "In", type: "flow" }],
|
||||
outputs: [{ id: "out", label: "Out", type: "flow" }],
|
||||
defaultConfig: { message: "Hello World" }
|
||||
} as NodeDefinition;
|
@@ -1,84 +0,0 @@
|
||||
<template>
|
||||
<div class="log-node" :style="{ borderColor: node.data?.color || '#10b981' }">
|
||||
<div class="node-header">
|
||||
<svg class="node-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor">
|
||||
<path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z" stroke-width="2" />
|
||||
<path d="M14 2v6h6" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" />
|
||||
<path d="M16 13H8" stroke-width="2" stroke-linecap="round" />
|
||||
<path d="M16 17H8" stroke-width="2" stroke-linecap="round" />
|
||||
<path d="M10 9H8" stroke-width="2" stroke-linecap="round" />
|
||||
</svg>
|
||||
<span class="node-title">日志</span>
|
||||
</div>
|
||||
<div class="node-content">
|
||||
<span class="log-message">{{ node.data?.config?.message || '输出日志' }}</span>
|
||||
</div>
|
||||
<Handle
|
||||
id="in"
|
||||
type="target"
|
||||
:position="Position.Top"
|
||||
class="handle"
|
||||
/>
|
||||
<Handle
|
||||
id="out"
|
||||
type="source"
|
||||
:position="Position.Bottom"
|
||||
class="handle"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { Handle, Position } from "@vue-flow/core";
|
||||
|
||||
const _props = defineProps<{
|
||||
node: any
|
||||
}>();
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.log-node {
|
||||
background: white;
|
||||
border: 2px solid #10b981;
|
||||
border-radius: 8px;
|
||||
padding: 12px;
|
||||
min-width: 120px;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.node-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.node-icon {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
color: #10b981;
|
||||
}
|
||||
|
||||
.node-title {
|
||||
font-weight: 600;
|
||||
font-size: 14px;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.node-content {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.log-message {
|
||||
font-size: 12px;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.handle {
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
background: #fff;
|
||||
border: 2px solid #10b981;
|
||||
border-radius: 50%;
|
||||
}
|
||||
</style>
|
@@ -1,20 +0,0 @@
|
||||
import type { NodeDefinition } from "~/types/flow";
|
||||
|
||||
const nodeModules = import.meta.glob("./*/index.ts", { eager: true });
|
||||
|
||||
export const flowNodes: NodeDefinition[] = Object.values(nodeModules).map(
|
||||
(module: any) => module.default
|
||||
);
|
||||
|
||||
export const getNodeDefinitionsByCategory = () => {
|
||||
const categories: Record<string, NodeDefinition[]> = {};
|
||||
|
||||
flowNodes.forEach((node) => {
|
||||
if (!categories[node.category]) {
|
||||
categories[node.category] = [];
|
||||
}
|
||||
categories[node.category].push(node);
|
||||
});
|
||||
|
||||
return categories;
|
||||
};
|
597
app/components/flow-nodes/WorkflowEditor.vue
Normal file
@@ -0,0 +1,597 @@
|
||||
<template>
|
||||
<div class="h-screen flex flex-col bg-gray-50 select-none">
|
||||
<!-- 顶部工具栏 -->
|
||||
<div class="bg-white border-b border-gray-200 px-4 py-3">
|
||||
<div class="flex items-center space-x-4">
|
||||
<button
|
||||
class="flex items-center space-x-2 px-3 py-2 text-sm font-medium text-gray-700 bg-gray-100 rounded-md hover:bg-gray-200"
|
||||
@click="goBack"
|
||||
>
|
||||
<ArrowLeft class="w-4 h-4" />
|
||||
<span>返回</span>
|
||||
</button>
|
||||
|
||||
<div class="flex-1" />
|
||||
|
||||
<div class="flex items-center space-x-3">
|
||||
<button
|
||||
class="flex items-center space-x-2 px-4 py-2 text-sm font-medium text-white bg-green-600 rounded-md hover:bg-green-700"
|
||||
@click="startTask"
|
||||
>
|
||||
<Play class="w-4 h-4" />
|
||||
<span>开始任务</span>
|
||||
</button>
|
||||
|
||||
<button
|
||||
class="flex items-center space-x-2 px-4 py-2 text-sm font-medium text-white bg-blue-600 rounded-md hover:bg-blue-700"
|
||||
@click="saveTask"
|
||||
>
|
||||
<Save class="w-4 h-4" />
|
||||
<span>保存任务</span>
|
||||
</button>
|
||||
|
||||
<button
|
||||
class="flex items-center space-x-2 px-4 py-2 text-sm font-medium text-white bg-purple-600 rounded-md hover:bg-purple-700"
|
||||
@click="exportTask"
|
||||
>
|
||||
<Download class="w-4 h-4" />
|
||||
<span>导出任务</span>
|
||||
</button>
|
||||
|
||||
<button
|
||||
class="flex items-center space-x-2 px-4 py-2 text-sm font-medium text-white bg-indigo-600 rounded-md hover:bg-indigo-700"
|
||||
@click="importTask"
|
||||
>
|
||||
<Upload class="w-4 h-4" />
|
||||
<span>导入任务</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 主内容区域 -->
|
||||
<div class="flex-1 flex">
|
||||
<!-- 左侧工作流面板 -->
|
||||
<div class="bg-white shadow-lg overflow-hidden flex flex-col w-280px">
|
||||
<div
|
||||
class="p-4 border-b border-gray-200 flex justify-between items-center flex-shrink-0"
|
||||
>
|
||||
<h2 class="text-lg font-semibold text-gray-800">
|
||||
任务流
|
||||
</h2>
|
||||
<button
|
||||
v-if="workflowNodes.length > 0"
|
||||
class="text-xs text-red-600 hover:text-red-800"
|
||||
@click="clearWorkflow"
|
||||
>
|
||||
清空
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="flex-1 overflow-y-auto p-4">
|
||||
<draggable
|
||||
v-model="workflowNodes"
|
||||
:animation="200"
|
||||
class="space-y-2 min-h-full"
|
||||
item-key="id"
|
||||
handle=".drag-handle"
|
||||
ghost-class="sortable-ghost"
|
||||
chosen-class="sortable-chosen"
|
||||
drag-class="sortable-drag"
|
||||
:force-fallback="true"
|
||||
@change="onWorkflowChange"
|
||||
>
|
||||
<template #item="{ element, index }">
|
||||
<div
|
||||
class="p-3 border rounded-lg hover:shadow-md transition-shadow"
|
||||
:style="{
|
||||
backgroundColor: element.backgroundColor,
|
||||
color: element.textColor
|
||||
}"
|
||||
>
|
||||
<div class="flex items-center justify-between">
|
||||
<div class="flex items-center space-x-2 flex-1 drag-handle cursor-move">
|
||||
<div
|
||||
class="flex flex-col space-y-0.5 opacity-60 hover:opacity-100 transition-opacity"
|
||||
>
|
||||
<div class="w-1.5 h-1.5 bg-current rounded-full" />
|
||||
<div class="w-1.5 h-1.5 bg-current rounded-full" />
|
||||
<div class="w-1.5 h-1.5 bg-current rounded-full" />
|
||||
</div>
|
||||
<component
|
||||
:is="getIconComponent(element.icon)"
|
||||
class="w-4 h-4"
|
||||
/>
|
||||
<span class="text-sm font-medium">{{ element.name }}</span>
|
||||
</div>
|
||||
|
||||
<div class="flex items-center space-x-1 flex-shrink-0">
|
||||
<button
|
||||
v-if="element.config?.editable"
|
||||
class="p-1 rounded hover:bg-white/20 cursor-pointer z-10 relative"
|
||||
@click.stop="editNodeConfig(element)"
|
||||
>
|
||||
<Settings class="w-3 h-3" />
|
||||
</button>
|
||||
<button
|
||||
class="p-1 rounded hover:bg-white/20 cursor-pointer z-10 relative"
|
||||
@click.stop="removeNode(index)"
|
||||
>
|
||||
<X class="w-3 h-3" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
v-if="element.config?.duration"
|
||||
class="mt-2 text-xs opacity-80 ml-6"
|
||||
>
|
||||
延时: {{ element.config.duration }}ms
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</draggable>
|
||||
|
||||
<div
|
||||
v-if="workflowNodes.length === 0"
|
||||
class="text-center py-8 text-gray-400 flex flex-col items-center justify-center min-h-[200px]"
|
||||
>
|
||||
<Move class="w-8 h-8 mx-auto mb-2" />
|
||||
<p class="text-sm">
|
||||
从右侧点击节点添加到此处
|
||||
</p>
|
||||
<p class="text-xs mt-1 opacity-75">
|
||||
添加后可拖拽排序
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 右侧节点库 -->
|
||||
<div class="flex-1 flex flex-col bg-gray-50 min-h-0">
|
||||
<div class="p-6 pb-4 bg-gray-50 flex-shrink-0">
|
||||
<h1 class="text-2xl font-bold text-gray-900">
|
||||
节点库
|
||||
</h1>
|
||||
<p class="text-gray-600 mt-1">
|
||||
点击节点添加到左侧任务流
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- 节点库 - 独立滚动区域 -->
|
||||
<div class="flex-1 overflow-y-auto px-6 pb-6 min-h-0">
|
||||
<div class="space-y-6">
|
||||
<div v-for="(nodes, category) in categorizedNodes" :key="category">
|
||||
<h3 class="text-lg font-semibold text-gray-800 mb-3">
|
||||
{{ category }}
|
||||
</h3>
|
||||
<div class="grid grid-cols-4 gap-4">
|
||||
<button
|
||||
v-for="element in nodes"
|
||||
:key="element.id"
|
||||
class="p-4 border rounded-lg cursor-pointer hover:shadow-lg transition-all hover:scale-105 text-center"
|
||||
:style="{
|
||||
backgroundColor: element.backgroundColor,
|
||||
color: element.textColor
|
||||
}"
|
||||
@click="addNodeToWorkflow(element)"
|
||||
>
|
||||
<component
|
||||
:is="getIconComponent(element.icon)"
|
||||
class="w-8 h-8 mx-auto mb-2"
|
||||
/>
|
||||
<p class="text-sm font-medium">
|
||||
{{ element.name }}
|
||||
</p>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 配置编辑对话框 -->
|
||||
<div
|
||||
v-if="editingNode"
|
||||
class="fixed inset-0 bg-black/50 flex items-center justify-center z-50"
|
||||
@click="cancelEdit"
|
||||
>
|
||||
<div class="bg-white rounded-lg p-6 w-80 max-w-sm" @click.stop>
|
||||
<h3 class="text-lg font-semibold mb-4">
|
||||
编辑配置
|
||||
</h3>
|
||||
|
||||
<div v-if="editingNode.config?.editable" class="mb-4">
|
||||
<label class="block text-sm font-medium text-gray-700 mb-2">
|
||||
延时时间 (毫秒)
|
||||
</label>
|
||||
<input
|
||||
v-model.number="editingNode.config.duration"
|
||||
type="number"
|
||||
min="100"
|
||||
max="60000"
|
||||
step="100"
|
||||
class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
|
||||
>
|
||||
</div>
|
||||
|
||||
<div class="flex justify-end space-x-2">
|
||||
<button
|
||||
class="px-4 py-2 text-sm text-gray-600 border border-gray-300 rounded-md hover:bg-gray-50"
|
||||
@click="cancelEdit"
|
||||
>
|
||||
取消
|
||||
</button>
|
||||
<button
|
||||
class="px-4 py-2 text-sm text-white bg-blue-600 rounded-md hover:bg-blue-700"
|
||||
@click="saveEdit"
|
||||
>
|
||||
保存
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import type { FlowNode } from "~/components/flow-nodes/nodes";
|
||||
import { open, save } from "@tauri-apps/plugin-dialog";
|
||||
import { readTextFile, writeTextFile } from "@tauri-apps/plugin-fs";
|
||||
import {
|
||||
ArrowLeft,
|
||||
Clock,
|
||||
Computer,
|
||||
Download,
|
||||
Monitor,
|
||||
Move,
|
||||
Pause,
|
||||
Play,
|
||||
Play as PlayIcon,
|
||||
PlaySquare,
|
||||
Power,
|
||||
Save,
|
||||
Settings,
|
||||
SkipBack,
|
||||
SkipForward,
|
||||
Square,
|
||||
Upload,
|
||||
Volume2,
|
||||
VolumeX,
|
||||
X
|
||||
} from "lucide-vue-next";
|
||||
import { computed, ref } from "vue";
|
||||
import { useRouter } from "vue-router";
|
||||
import draggable from "vuedraggable";
|
||||
import {
|
||||
getNodesByCategory,
|
||||
nodeDefinitions
|
||||
} from "~/components/flow-nodes/nodes";
|
||||
// import { toast } from "#build/ui";
|
||||
const toast = useToast();
|
||||
|
||||
const router = useRouter();
|
||||
const workflowNodes = ref<FlowNode[]>([]);
|
||||
|
||||
const editingNode = ref<FlowNode | null>(null);
|
||||
|
||||
const categorizedNodes = computed(() => getNodesByCategory());
|
||||
|
||||
const getIconComponent = (iconName: string) => {
|
||||
const iconMap: Record<string, any> = {
|
||||
power: Power,
|
||||
computer: Computer,
|
||||
monitor: Monitor,
|
||||
play: PlayIcon,
|
||||
pause: Pause,
|
||||
square: Square,
|
||||
"skip-back": SkipBack,
|
||||
"skip-forward": SkipForward,
|
||||
"volume-2": Volume2,
|
||||
"volume-x": VolumeX,
|
||||
"play-square": PlaySquare,
|
||||
clock: Clock
|
||||
};
|
||||
return iconMap[iconName] || Square;
|
||||
};
|
||||
|
||||
const cloneNode = (original: FlowNode): FlowNode => {
|
||||
// 生成更健壮的唯一ID
|
||||
const timestamp = Date.now();
|
||||
const randomString = Math.random().toString(36).substring(2, 11);
|
||||
const uniqueId = `${original.id}-${timestamp}-${randomString}`;
|
||||
|
||||
return {
|
||||
...original,
|
||||
id: uniqueId,
|
||||
config: original.config ? { ...original.config } : undefined
|
||||
};
|
||||
};
|
||||
|
||||
// 点击添加节点到工作流
|
||||
const addNodeToWorkflow = (node: FlowNode) => {
|
||||
const clonedNode = cloneNode(node);
|
||||
workflowNodes.value.push(clonedNode);
|
||||
console.log("节点已添加到工作流:", clonedNode.name);
|
||||
};
|
||||
|
||||
const removeNode = (index: number) => {
|
||||
workflowNodes.value.splice(index, 1);
|
||||
};
|
||||
|
||||
const clearWorkflow = () => {
|
||||
workflowNodes.value = [];
|
||||
};
|
||||
|
||||
const onWorkflowChange = (event: any) => {
|
||||
console.log("工作流变化事件:", event);
|
||||
|
||||
// 验证工作流完整性
|
||||
if (event.added) {
|
||||
const addedNode = event.added.element;
|
||||
console.log("添加的节点数据:", addedNode);
|
||||
|
||||
// 验证添加的节点具有所有必需属性
|
||||
if (!addedNode.id || !addedNode.name || !addedNode.type) {
|
||||
console.error("无效节点添加到工作流:", addedNode);
|
||||
return;
|
||||
}
|
||||
console.log("节点已成功添加到工作流:", addedNode.name);
|
||||
}
|
||||
|
||||
if (event.moved) {
|
||||
console.log("工作流节点已重新排序");
|
||||
}
|
||||
|
||||
if (event.removed) {
|
||||
console.log("节点已从工作流中移除");
|
||||
}
|
||||
|
||||
console.log("工作流已更新,当前节点数量:", workflowNodes.value.length);
|
||||
console.log("当前工作流节点:", workflowNodes.value);
|
||||
};
|
||||
|
||||
const editNodeConfig = (node: FlowNode) => {
|
||||
editingNode.value = { ...node };
|
||||
};
|
||||
|
||||
const cancelEdit = () => {
|
||||
editingNode.value = null;
|
||||
};
|
||||
|
||||
const saveEdit = () => {
|
||||
if (editingNode.value) {
|
||||
const index = workflowNodes.value.findIndex(
|
||||
(n) => n.id === editingNode.value!.id
|
||||
);
|
||||
if (index !== -1) {
|
||||
workflowNodes.value[index] = { ...editingNode.value };
|
||||
}
|
||||
cancelEdit();
|
||||
}
|
||||
};
|
||||
|
||||
const goBack = () => {
|
||||
router.push("/");
|
||||
};
|
||||
|
||||
const startTask = () => {
|
||||
if (workflowNodes.value.length === 0) {
|
||||
console.log("请先添加任务节点");
|
||||
toast.add({
|
||||
title: "任务异常",
|
||||
description: "当前任务清单并无任何节点,请先添加节点。",
|
||||
color: "warning"
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
const taskData = {
|
||||
name: `任务_${new Date().toLocaleTimeString()}`,
|
||||
createdAt: new Date().toISOString(),
|
||||
nodes: workflowNodes.value.map((node) => ({
|
||||
id: node.id,
|
||||
name: node.name,
|
||||
type: node.type,
|
||||
taskId: node.taskId,
|
||||
config: node.config || {}
|
||||
}))
|
||||
};
|
||||
|
||||
console.log("=== 工作流执行 ===");
|
||||
console.log("任务名称:", taskData.name);
|
||||
console.log("创建时间:", taskData.createdAt);
|
||||
console.log("节点数量:", taskData.nodes.length);
|
||||
console.log("完整JSON数据:");
|
||||
console.log(JSON.stringify(taskData, null, 2));
|
||||
console.log("=== 执行完成 ===");
|
||||
toast.add({
|
||||
title: "任务执行成功",
|
||||
description: `总共执行了 ${taskData.nodes.length} 个节点。`,
|
||||
color: "success"
|
||||
});
|
||||
};
|
||||
|
||||
const saveTask = async () => {
|
||||
if (workflowNodes.value.length === 0) {
|
||||
console.log("工作流为空,无法保存");
|
||||
return;
|
||||
}
|
||||
|
||||
const taskData = {
|
||||
name: `任务_${new Date().toLocaleTimeString()}`,
|
||||
createdAt: new Date().toISOString(),
|
||||
version: "1.0",
|
||||
nodes: workflowNodes.value.map((node) => ({
|
||||
id: node.id,
|
||||
name: node.name,
|
||||
type: node.type,
|
||||
taskId: node.taskId,
|
||||
config: node.config || {}
|
||||
}))
|
||||
};
|
||||
|
||||
try {
|
||||
const filePath = await save({
|
||||
filters: [{
|
||||
name: "JSON",
|
||||
extensions: ["json"]
|
||||
}],
|
||||
defaultPath: `workflow_${Date.now()}.json`
|
||||
});
|
||||
|
||||
if (filePath) {
|
||||
await writeTextFile(filePath, JSON.stringify(taskData, null, 2));
|
||||
console.log("工作流已保存到:", filePath);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("保存工作流失败:", error);
|
||||
}
|
||||
};
|
||||
|
||||
const exportTask = async () => {
|
||||
await saveTask();
|
||||
};
|
||||
|
||||
const importTask = async () => {
|
||||
try {
|
||||
const selected = await open({
|
||||
multiple: false,
|
||||
filters: [{
|
||||
name: "JSON",
|
||||
extensions: ["json"]
|
||||
}]
|
||||
});
|
||||
|
||||
if (selected) {
|
||||
const content = await readTextFile(selected as string);
|
||||
const data = JSON.parse(content);
|
||||
|
||||
if (data.nodes && Array.isArray(data.nodes)) {
|
||||
// 验证并重建节点
|
||||
const importedNodes = data.nodes.map((node: any) => {
|
||||
// 提取原始节点ID(去除时间戳后缀)
|
||||
const originalId = node.id.replace(/-\d+-[a-z0-9]+$/, "");
|
||||
const definition = nodeDefinitions[originalId];
|
||||
|
||||
if (!definition) {
|
||||
console.warn(`未找到节点定义: ${originalId}`);
|
||||
return null;
|
||||
}
|
||||
|
||||
return {
|
||||
...definition,
|
||||
id: node.id, // 保持导入的ID
|
||||
config: node.config || definition.config || {}
|
||||
};
|
||||
}).filter(Boolean); // 过滤掉null值
|
||||
|
||||
workflowNodes.value = importedNodes as FlowNode[];
|
||||
console.log(`成功导入 ${importedNodes.length} 个节点`);
|
||||
console.log("导入的工作流数据:", data);
|
||||
} else {
|
||||
console.error("无效的工作流文件格式");
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("导入工作流失败:", error);
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.w-280px {
|
||||
width: 280px;
|
||||
}
|
||||
|
||||
/* 禁用文本选择,提供桌面应用体验 */
|
||||
* {
|
||||
user-select: none;
|
||||
-webkit-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
}
|
||||
|
||||
/* 输入框仍然允许选择 */
|
||||
input,
|
||||
textarea {
|
||||
user-select: text;
|
||||
-webkit-user-select: text;
|
||||
-moz-user-select: text;
|
||||
-ms-user-select: text;
|
||||
}
|
||||
|
||||
/* 拖拽时的样式 */
|
||||
.sortable-ghost {
|
||||
opacity: 0.3;
|
||||
background: rgba(59, 130, 246, 0.1) !important;
|
||||
border: 2px dashed rgba(59, 130, 246, 0.5) !important;
|
||||
}
|
||||
|
||||
.sortable-chosen {
|
||||
transform: scale(1.02);
|
||||
box-shadow: 0 8px 25px rgba(0, 0, 0, 0.15);
|
||||
}
|
||||
|
||||
.sortable-drag {
|
||||
transform: rotate(2deg);
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
/* 拖拽手柄样式 */
|
||||
.drag-handle {
|
||||
cursor: grab;
|
||||
border-radius: 6px;
|
||||
padding: 4px;
|
||||
margin: -4px;
|
||||
}
|
||||
|
||||
.drag-handle:hover {
|
||||
background-color: rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
|
||||
.drag-handle:active {
|
||||
cursor: grabbing;
|
||||
}
|
||||
|
||||
/* 确保滚动容器独立 */
|
||||
.overflow-y-auto {
|
||||
scrollbar-width: thin;
|
||||
scrollbar-color: rgba(156, 163, 175, 0.5) transparent;
|
||||
}
|
||||
|
||||
.overflow-y-auto::-webkit-scrollbar {
|
||||
width: 6px;
|
||||
}
|
||||
|
||||
.overflow-y-auto::-webkit-scrollbar-track {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.overflow-y-auto::-webkit-scrollbar-thumb {
|
||||
background-color: rgba(156, 163, 175, 0.5);
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.overflow-y-auto::-webkit-scrollbar-thumb:hover {
|
||||
background-color: rgba(156, 163, 175, 0.7);
|
||||
}
|
||||
|
||||
/* 防止拖拽时出现默认的拖拽图像 */
|
||||
.drag-handle * {
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
/* 确保按钮可以正常点击 */
|
||||
button {
|
||||
pointer-events: auto !important;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
/* 按钮悬停效果 */
|
||||
button:hover {
|
||||
transform: scale(1.1);
|
||||
transition: transform 0.1s ease;
|
||||
}
|
||||
</style>
|
351
app/components/flow-nodes/nodes.ts
Normal file
@@ -0,0 +1,351 @@
|
||||
export interface FlowNode {
|
||||
id: string
|
||||
name: string
|
||||
type: string
|
||||
taskId: string
|
||||
icon: string
|
||||
backgroundColor: string
|
||||
shape: "square" | "circle" | "rounded"
|
||||
textColor: string
|
||||
category: "控制类" | "设备类" | "媒体类" | "灯光类" | "延时类"
|
||||
config?: Record<string, any>
|
||||
}
|
||||
|
||||
export const nodeDefinitions: Record<string, FlowNode> = {
|
||||
// 控制类
|
||||
"control-poweroff": {
|
||||
id: "control-poweroff",
|
||||
name: "一键关机",
|
||||
type: "control",
|
||||
taskId: "power_off_all",
|
||||
icon: "power",
|
||||
backgroundColor: "#ef4444",
|
||||
shape: "rounded",
|
||||
textColor: "#ffffff",
|
||||
category: "控制类"
|
||||
},
|
||||
"control-poweron": {
|
||||
id: "control-poweron",
|
||||
name: "一键开机",
|
||||
type: "control",
|
||||
taskId: "power_on_all",
|
||||
icon: "power",
|
||||
backgroundColor: "#22c55e",
|
||||
shape: "rounded",
|
||||
textColor: "#ffffff",
|
||||
category: "控制类"
|
||||
},
|
||||
|
||||
// 设备类
|
||||
"device-poweroff-host1": {
|
||||
id: "device-poweroff-host1",
|
||||
name: "关 [!] 主机1",
|
||||
type: "device",
|
||||
taskId: "power_off_host1",
|
||||
icon: "computer",
|
||||
backgroundColor: "#ef4444",
|
||||
shape: "square",
|
||||
textColor: "#ffffff",
|
||||
category: "设备类"
|
||||
},
|
||||
"device-poweron-host1": {
|
||||
id: "device-poweron-host1",
|
||||
name: "开 [!] 主机1",
|
||||
type: "device",
|
||||
taskId: "power_on_host1",
|
||||
icon: "computer",
|
||||
backgroundColor: "#22c55e",
|
||||
shape: "square",
|
||||
textColor: "#ffffff",
|
||||
category: "设备类"
|
||||
},
|
||||
"device-poweroff-host2": {
|
||||
id: "device-poweroff-host2",
|
||||
name: "关 [!] 主机2",
|
||||
type: "device",
|
||||
taskId: "power_off_host2",
|
||||
icon: "computer",
|
||||
backgroundColor: "#ef4444",
|
||||
shape: "square",
|
||||
textColor: "#ffffff",
|
||||
category: "设备类"
|
||||
},
|
||||
"device-poweron-host2": {
|
||||
id: "device-poweron-host2",
|
||||
name: "开 [!] 主机2",
|
||||
type: "device",
|
||||
taskId: "power_on_host2",
|
||||
icon: "computer",
|
||||
backgroundColor: "#22c55e",
|
||||
shape: "square",
|
||||
textColor: "#ffffff",
|
||||
category: "设备类"
|
||||
},
|
||||
"device-poweron-projector1": {
|
||||
id: "device-poweron-projector1",
|
||||
name: "开 [!] 投影1",
|
||||
type: "device",
|
||||
taskId: "power_on_projector1",
|
||||
icon: "monitor",
|
||||
backgroundColor: "#22c55e",
|
||||
shape: "square",
|
||||
textColor: "#ffffff",
|
||||
category: "设备类"
|
||||
},
|
||||
"device-poweroff-projector1": {
|
||||
id: "device-poweroff-projector1",
|
||||
name: "关 [!] 投影1",
|
||||
type: "device",
|
||||
taskId: "power_off_projector1",
|
||||
icon: "monitor",
|
||||
backgroundColor: "#ef4444",
|
||||
shape: "square",
|
||||
textColor: "#ffffff",
|
||||
category: "设备类"
|
||||
},
|
||||
"device-poweron-projector2": {
|
||||
id: "device-poweron-projector2",
|
||||
name: "开 [!] 投影2",
|
||||
type: "device",
|
||||
taskId: "power_on_projector2",
|
||||
icon: "monitor",
|
||||
backgroundColor: "#22c55e",
|
||||
shape: "square",
|
||||
textColor: "#ffffff",
|
||||
category: "设备类"
|
||||
},
|
||||
"device-poweroff-projector2": {
|
||||
id: "device-poweroff-projector2",
|
||||
name: "关 [!] 投影2",
|
||||
type: "device",
|
||||
taskId: "power_off_projector2",
|
||||
icon: "monitor",
|
||||
backgroundColor: "#ef4444",
|
||||
shape: "square",
|
||||
textColor: "#ffffff",
|
||||
category: "设备类"
|
||||
},
|
||||
"device-poweron-projector3": {
|
||||
id: "device-poweron-projector3",
|
||||
name: "开 [!] 投影3",
|
||||
type: "device",
|
||||
taskId: "power_on_projector3",
|
||||
icon: "monitor",
|
||||
backgroundColor: "#22c55e",
|
||||
shape: "square",
|
||||
textColor: "#ffffff",
|
||||
category: "设备类"
|
||||
},
|
||||
"device-poweroff-projector3": {
|
||||
id: "device-poweroff-projector3",
|
||||
name: "关 [!] 投影3",
|
||||
type: "device",
|
||||
taskId: "power_off_projector3",
|
||||
icon: "monitor",
|
||||
backgroundColor: "#ef4444",
|
||||
shape: "square",
|
||||
textColor: "#ffffff",
|
||||
category: "设备类"
|
||||
},
|
||||
|
||||
// 媒体类
|
||||
"media-button3": {
|
||||
id: "media-button3",
|
||||
name: "BUTTON3",
|
||||
type: "media",
|
||||
taskId: "media_button3",
|
||||
icon: "square",
|
||||
backgroundColor: "#3b82f6",
|
||||
shape: "rounded",
|
||||
textColor: "#ffffff",
|
||||
category: "媒体类"
|
||||
},
|
||||
"media-play": {
|
||||
id: "media-play",
|
||||
name: "播放",
|
||||
type: "media",
|
||||
taskId: "media_play",
|
||||
icon: "play",
|
||||
backgroundColor: "#22c55e",
|
||||
shape: "circle",
|
||||
textColor: "#ffffff",
|
||||
category: "媒体类"
|
||||
},
|
||||
"media-pause": {
|
||||
id: "media-pause",
|
||||
name: "暂停",
|
||||
type: "media",
|
||||
taskId: "media_pause",
|
||||
icon: "pause",
|
||||
backgroundColor: "#f97316",
|
||||
shape: "circle",
|
||||
textColor: "#ffffff",
|
||||
category: "媒体类"
|
||||
},
|
||||
"media-stop": {
|
||||
id: "media-stop",
|
||||
name: "停止",
|
||||
type: "media",
|
||||
taskId: "media_stop",
|
||||
icon: "square",
|
||||
backgroundColor: "#ef4444",
|
||||
shape: "circle",
|
||||
textColor: "#ffffff",
|
||||
category: "媒体类"
|
||||
},
|
||||
"media-prev": {
|
||||
id: "media-prev",
|
||||
name: "上一曲",
|
||||
type: "media",
|
||||
taskId: "media_prev",
|
||||
icon: "skip-back",
|
||||
backgroundColor: "#8b5cf6",
|
||||
shape: "circle",
|
||||
textColor: "#ffffff",
|
||||
category: "媒体类"
|
||||
},
|
||||
"media-next": {
|
||||
id: "media-next",
|
||||
name: "下一曲",
|
||||
type: "media",
|
||||
taskId: "media_next",
|
||||
icon: "skip-forward",
|
||||
backgroundColor: "#8b5cf6",
|
||||
shape: "circle",
|
||||
textColor: "#ffffff",
|
||||
category: "媒体类"
|
||||
},
|
||||
"media-volume-down": {
|
||||
id: "media-volume-down",
|
||||
name: "音量减",
|
||||
type: "media",
|
||||
taskId: "media_volume_down",
|
||||
icon: "volume-2",
|
||||
backgroundColor: "#06b6d4",
|
||||
shape: "circle",
|
||||
textColor: "#ffffff",
|
||||
category: "媒体类"
|
||||
},
|
||||
"media-volume-up": {
|
||||
id: "media-volume-up",
|
||||
name: "音量加",
|
||||
type: "media",
|
||||
taskId: "media_volume_up",
|
||||
icon: "volume-2",
|
||||
backgroundColor: "#06b6d4",
|
||||
shape: "circle",
|
||||
textColor: "#ffffff",
|
||||
category: "媒体类"
|
||||
},
|
||||
"media-mute": {
|
||||
id: "media-mute",
|
||||
name: "静音",
|
||||
type: "media",
|
||||
taskId: "media_mute",
|
||||
icon: "volume-x",
|
||||
backgroundColor: "#f97316",
|
||||
shape: "circle",
|
||||
textColor: "#ffffff",
|
||||
category: "媒体类"
|
||||
},
|
||||
"media-video1": {
|
||||
id: "media-video1",
|
||||
name: "视频一",
|
||||
type: "media",
|
||||
taskId: "media_video1",
|
||||
icon: "play-square",
|
||||
backgroundColor: "#6366f1",
|
||||
shape: "rounded",
|
||||
textColor: "#ffffff",
|
||||
category: "媒体类"
|
||||
},
|
||||
"media-video2": {
|
||||
id: "media-video2",
|
||||
name: "视频二",
|
||||
type: "media",
|
||||
taskId: "media_video2",
|
||||
icon: "play-square",
|
||||
backgroundColor: "#6366f1",
|
||||
shape: "rounded",
|
||||
textColor: "#ffffff",
|
||||
category: "媒体类"
|
||||
},
|
||||
"media-video3": {
|
||||
id: "media-video3",
|
||||
name: "视频三",
|
||||
type: "media",
|
||||
taskId: "media_video3",
|
||||
icon: "play-square",
|
||||
backgroundColor: "#6366f1",
|
||||
shape: "rounded",
|
||||
textColor: "#ffffff",
|
||||
category: "媒体类"
|
||||
},
|
||||
"media-button1": {
|
||||
id: "media-button1",
|
||||
name: "BUTTON1",
|
||||
type: "media",
|
||||
taskId: "media_button1",
|
||||
icon: "square",
|
||||
backgroundColor: "#3b82f6",
|
||||
shape: "rounded",
|
||||
textColor: "#ffffff",
|
||||
category: "媒体类"
|
||||
},
|
||||
|
||||
// 延时类
|
||||
"delay-2s": {
|
||||
id: "delay-2s",
|
||||
name: "延时2秒",
|
||||
type: "delay",
|
||||
taskId: "delay_2s",
|
||||
icon: "clock",
|
||||
backgroundColor: "#f59e0b",
|
||||
shape: "rounded",
|
||||
textColor: "#ffffff",
|
||||
category: "延时类",
|
||||
config: { duration: 2000 }
|
||||
},
|
||||
"delay-5s": {
|
||||
id: "delay-5s",
|
||||
name: "延时5秒",
|
||||
type: "delay",
|
||||
taskId: "delay_5s",
|
||||
icon: "clock",
|
||||
backgroundColor: "#f59e0b",
|
||||
shape: "rounded",
|
||||
textColor: "#ffffff",
|
||||
category: "延时类",
|
||||
config: { duration: 5000 }
|
||||
},
|
||||
"delay-custom": {
|
||||
id: "delay-custom",
|
||||
name: "自定义延时时间",
|
||||
type: "delay",
|
||||
taskId: "delay_custom",
|
||||
icon: "clock",
|
||||
backgroundColor: "#f59e0b",
|
||||
shape: "rounded",
|
||||
textColor: "#ffffff",
|
||||
category: "延时类",
|
||||
config: { duration: 1000, editable: true }
|
||||
}
|
||||
};
|
||||
|
||||
export const getNodesByCategory = () => {
|
||||
const categories = {
|
||||
控制类: [] as FlowNode[],
|
||||
设备类: [] as FlowNode[],
|
||||
媒体类: [] as FlowNode[],
|
||||
灯光类: [] as FlowNode[],
|
||||
延时类: [] as FlowNode[]
|
||||
};
|
||||
|
||||
Object.values(nodeDefinitions).forEach((node) => {
|
||||
if (categories[node.category]) {
|
||||
categories[node.category].push(node);
|
||||
}
|
||||
});
|
||||
|
||||
return categories;
|
||||
};
|
@@ -1,9 +1,6 @@
|
||||
<template>
|
||||
<div>
|
||||
<SiteNavbar class="fixed w-full" />
|
||||
<SiteSidebar />
|
||||
|
||||
<div class="relative overflow-hidden px-6 lg:px-8">
|
||||
<div>
|
||||
<DesignTopBlob />
|
||||
<DesignBottomBlob />
|
||||
|
||||
|
@@ -3,50 +3,11 @@
|
||||
<div class="grid size-full place-content-center gap-y-8">
|
||||
<SvgoLogo :filled="true" :font-controlled="false" class="mx-auto size-40" />
|
||||
|
||||
<div class="flex flex-col items-center gap-y-3">
|
||||
<h1 class="animate-pulse text-3xl sm:text-4xl text-pretty font-bold font-heading md:mb-5">
|
||||
{{ app.name.toUpperCase() }}
|
||||
</h1>
|
||||
<p class="leading-7 text-pretty">
|
||||
Powered by
|
||||
</p>
|
||||
|
||||
<div class="flex flex-wrap justify-center gap-1 md:gap-3">
|
||||
<UButton
|
||||
variant="ghost"
|
||||
size="xl"
|
||||
:to="app.nuxtSite"
|
||||
target="_blank"
|
||||
external
|
||||
>
|
||||
Nuxt 4
|
||||
</UButton>
|
||||
<UButton
|
||||
variant="ghost"
|
||||
size="xl"
|
||||
:to="app.tauriSite"
|
||||
target="_blank"
|
||||
external
|
||||
>
|
||||
Tauri 2
|
||||
</UButton>
|
||||
<UButton
|
||||
variant="ghost"
|
||||
size="xl"
|
||||
:to="app.nuxtUiSite"
|
||||
target="_blank"
|
||||
external
|
||||
>
|
||||
NuxtUI 3
|
||||
</UButton>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex justify-center">
|
||||
<UButton
|
||||
to="/workflows/flow"
|
||||
to="/workflows"
|
||||
>
|
||||
进入工作流
|
||||
开始演示
|
||||
</UButton>
|
||||
</div>
|
||||
</div>
|
||||
|
@@ -1,154 +0,0 @@
|
||||
<template>
|
||||
<div class="h-screen flex flex-col">
|
||||
<!-- Toolbar -->
|
||||
<div class="h-12 bg-gray-100 border-b border-gray-200 flex items-center px-4 gap-3">
|
||||
<UButton @click="saveFlow">
|
||||
保存
|
||||
</UButton>
|
||||
<UButton @click="importFlow">
|
||||
导入
|
||||
</UButton>
|
||||
<UButton color="primary" @click="executeNodes">
|
||||
执行
|
||||
</UButton>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-1 overflow-hidden">
|
||||
<!-- Node Panel -->
|
||||
<div class="w-80 bg-white border-r border-gray-200 overflow-y-auto p-4">
|
||||
<h2 class="text-lg font-semibold mb-4">
|
||||
节点库
|
||||
</h2>
|
||||
|
||||
<div v-for="(nodes, category) in nodeCategories" :key="category" class="mb-6">
|
||||
<h3 class="text-sm font-medium text-gray-700 mb-2">
|
||||
{{ category }}
|
||||
</h3>
|
||||
<div class="space-y-2">
|
||||
<div
|
||||
v-for="node in nodes"
|
||||
:key="node.type"
|
||||
class="p-3 border border-gray-200 rounded-md cursor-move bg-white hover:bg-gray-50 transition-colors"
|
||||
draggable="true"
|
||||
@dragstart="(e) => onDragStart(e, node)"
|
||||
>
|
||||
<div class="flex items-center gap-2">
|
||||
<div class="w-4 h-4 bg-blue-100 rounded" />
|
||||
<span class="text-sm font-medium">{{ node.label }}</span>
|
||||
</div>
|
||||
<div class="text-xs text-gray-500 mt-1">
|
||||
{{ node.inputs.length }}输入 / {{ node.outputs.length }}输出
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Flow Canvas -->
|
||||
<div class="flex-1 relative">
|
||||
<VueFlow
|
||||
v-model:nodes="nodes"
|
||||
v-model:edges="edges"
|
||||
:node-types="nodeTypes"
|
||||
:fit-view-on-init="true"
|
||||
@dragover.prevent
|
||||
@drop="onDrop"
|
||||
@node-double-click="(event: NodeMouseEvent) => onNodeDoubleClick(event)"
|
||||
>
|
||||
<Background />
|
||||
<Controls />
|
||||
<MiniMap />
|
||||
</VueFlow>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import type { NodeMouseEvent } from "@vue-flow/core";
|
||||
import type { Component } from "vue";
|
||||
import type { FlowNode } from "~/types/flow";
|
||||
import { Background } from "@vue-flow/background";
|
||||
import { Controls } from "@vue-flow/controls";
|
||||
import { useVueFlow, VueFlow } from "@vue-flow/core";
|
||||
import { MiniMap } from "@vue-flow/minimap";
|
||||
|
||||
import { v4 as uuidv4 } from "uuid";
|
||||
import { computed, defineAsyncComponent, ref } from "vue";
|
||||
import { getNodeDefinitionsByCategory } from "~/components/Flow/Nodes";
|
||||
|
||||
definePageMeta({
|
||||
layout: "flow"
|
||||
});
|
||||
|
||||
const nodeCategories = ref(getNodeDefinitionsByCategory());
|
||||
const { nodes, edges, addNodes, project } = useVueFlow();
|
||||
|
||||
// 注册节点类型
|
||||
const nodeTypes: Record<string, Component> = {
|
||||
delay: defineAsyncComponent(() => import("~/components/Flow/Nodes/DelayNode.vue")),
|
||||
log: defineAsyncComponent(() => import("~/components/Flow/Nodes/LogNode.vue"))
|
||||
};
|
||||
|
||||
const allNodeDefinitions = computed(() => {
|
||||
const categories = nodeCategories.value;
|
||||
return Object.values(categories).flat();
|
||||
});
|
||||
|
||||
const onDragStart = (event: DragEvent, node: any) => {
|
||||
event.dataTransfer?.setData("application/vueflow", node.type);
|
||||
event.dataTransfer!.effectAllowed = "move";
|
||||
};
|
||||
|
||||
const onDrop = (event: DragEvent) => {
|
||||
const nodeType = event.dataTransfer?.getData("application/vueflow");
|
||||
if (!nodeType) return;
|
||||
|
||||
const position = project({ x: event.clientX, y: event.clientY });
|
||||
|
||||
const nodeDefinition = allNodeDefinitions.value.find((n) => n.type === nodeType);
|
||||
if (!nodeDefinition) return;
|
||||
|
||||
const newNode: FlowNode = {
|
||||
id: uuidv4(),
|
||||
type: nodeType,
|
||||
position,
|
||||
data: {
|
||||
config: { ...nodeDefinition.defaultConfig }
|
||||
}
|
||||
};
|
||||
|
||||
addNodes([newNode]);
|
||||
};
|
||||
|
||||
const onNodeDoubleClick = (event: NodeMouseEvent) => {
|
||||
console.log("编辑节点:", event.node);
|
||||
// 这里可以打开右侧抽屉或弹窗编辑节点配置
|
||||
};
|
||||
|
||||
const saveFlow = () => {
|
||||
const flowData = {
|
||||
nodes: nodes.value,
|
||||
edges: edges.value
|
||||
};
|
||||
console.log("保存", flowData);
|
||||
};
|
||||
|
||||
const importFlow = () => {
|
||||
console.log("导入");
|
||||
// 这里可以实现文件选择器读取 JSON 并还原画布
|
||||
};
|
||||
|
||||
const executeNodes = () => {
|
||||
const flowData = {
|
||||
nodes: nodes.value,
|
||||
edges: edges.value
|
||||
};
|
||||
console.log("执行", flowData);
|
||||
};
|
||||
</script>
|
||||
|
||||
<style>
|
||||
@import '@vue-flow/core/dist/style.css';
|
||||
@import '@vue-flow/core/dist/theme-default.css';
|
||||
</style>
|
11
app/pages/workflows/index.vue
Normal file
@@ -0,0 +1,11 @@
|
||||
<template>
|
||||
<WorkflowEditor />
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import WorkflowEditor from "~/components/flow-nodes/WorkflowEditor.vue";
|
||||
|
||||
definePageMeta({
|
||||
layout: "flow"
|
||||
});
|
||||
</script>
|
@@ -1,32 +0,0 @@
|
||||
export interface NodeDefinition {
|
||||
type: string
|
||||
label: string
|
||||
category: string
|
||||
inputs: PortDefinition[]
|
||||
outputs: PortDefinition[]
|
||||
defaultConfig: Record<string, any>
|
||||
}
|
||||
|
||||
export interface PortDefinition {
|
||||
id: string
|
||||
label: string
|
||||
type: "flow" | "data"
|
||||
}
|
||||
|
||||
export interface FlowNode {
|
||||
id: string
|
||||
type: string
|
||||
position: { x: number, y: number }
|
||||
data: {
|
||||
config: Record<string, any>
|
||||
color?: string
|
||||
}
|
||||
}
|
||||
|
||||
export interface FlowEdge {
|
||||
id: string
|
||||
source: string
|
||||
target: string
|
||||
sourceHandle?: string
|
||||
targetHandle?: string
|
||||
}
|
@@ -25,20 +25,17 @@
|
||||
"dependencies": {
|
||||
"@nuxt/ui-pro": "^3.2.0",
|
||||
"@tauri-apps/api": "^2.6.0",
|
||||
"@tauri-apps/plugin-dialog": "~2",
|
||||
"@tauri-apps/plugin-fs": "^2.4.0",
|
||||
"@tauri-apps/plugin-notification": "^2.3.0",
|
||||
"@tauri-apps/plugin-os": "^2.3.0",
|
||||
"@tauri-apps/plugin-shell": "^2.3.0",
|
||||
"@tauri-apps/plugin-store": "^2.3.0",
|
||||
"@types/uuid": "^10.0.0",
|
||||
"@vue-flow/background": "^1.3.2",
|
||||
"@vue-flow/controls": "^1.1.3",
|
||||
"@vue-flow/core": "^1.46.0",
|
||||
"@vue-flow/minimap": "^1.5.4",
|
||||
"lucide-vue-next": "^0.540.0",
|
||||
"nuxt": "^4.0.0",
|
||||
"uuid": "^11.1.0",
|
||||
"vue": "^3.5.17",
|
||||
"vue-router": "^4.5.1",
|
||||
"vuedraggable": "^4.1.0",
|
||||
"zod": "^4.0.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
214
pnpm-lock.yaml
generated
@@ -13,10 +13,13 @@ importers:
|
||||
dependencies:
|
||||
'@nuxt/ui-pro':
|
||||
specifier: ^3.2.0
|
||||
version: 3.3.2(@babel/parser@7.28.0)(@netlify/blobs@9.1.2)(db0@0.3.2)(embla-carousel@8.6.0)(ioredis@5.6.1)(jwt-decode@4.0.0)(magicast@0.3.5)(tslite@5.7.3)(vite@7.0.4(@types/node@24.0.13)(jiti@2.4.2)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.0))(vue-router@4.5.1(vue@3.5.17(tslite@5.7.3)))(vue@3.5.17(tslite@5.7.3))(zod@4.0.5)
|
||||
version: 3.3.2(@babel/parser@7.28.0)(@netlify/blobs@9.1.2)(db0@0.3.2)(embla-carousel@8.6.0)(ioredis@5.6.1)(jwt-decode@4.0.0)(magicast@0.3.5)(sortablejs@1.14.0)(tslite@5.7.3)(vite@7.0.4(@types/node@24.0.13)(jiti@2.4.2)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.0))(vue-router@4.5.1(vue@3.5.17(tslite@5.7.3)))(vue@3.5.17(tslite@5.7.3))(zod@4.0.5)
|
||||
'@tauri-apps/api':
|
||||
specifier: ^2.6.0
|
||||
version: 2.6.0
|
||||
'@tauri-apps/plugin-dialog':
|
||||
specifier: ~2
|
||||
version: 2.3.3
|
||||
'@tauri-apps/plugin-fs':
|
||||
specifier: ^2.4.0
|
||||
version: 2.4.0
|
||||
@@ -32,33 +35,21 @@ importers:
|
||||
'@tauri-apps/plugin-store':
|
||||
specifier: ^2.3.0
|
||||
version: 2.3.0
|
||||
'@types/uuid':
|
||||
specifier: ^10.0.0
|
||||
version: 10.0.0
|
||||
'@vue-flow/background':
|
||||
specifier: ^1.3.2
|
||||
version: 1.3.2(@vue-flow/core@1.46.0(vue@3.5.17(tslite@5.7.3)))(vue@3.5.17(tslite@5.7.3))
|
||||
'@vue-flow/controls':
|
||||
specifier: ^1.1.3
|
||||
version: 1.1.3(@vue-flow/core@1.46.0(vue@3.5.17(tslite@5.7.3)))(vue@3.5.17(tslite@5.7.3))
|
||||
'@vue-flow/core':
|
||||
specifier: ^1.46.0
|
||||
version: 1.46.0(vue@3.5.17(tslite@5.7.3))
|
||||
'@vue-flow/minimap':
|
||||
specifier: ^1.5.4
|
||||
version: 1.5.4(@vue-flow/core@1.46.0(vue@3.5.17(tslite@5.7.3)))(vue@3.5.17(tslite@5.7.3))
|
||||
lucide-vue-next:
|
||||
specifier: ^0.540.0
|
||||
version: 0.540.0(vue@3.5.17(tslite@5.7.3))
|
||||
nuxt:
|
||||
specifier: ^4.0.0
|
||||
version: 4.0.0(@netlify/blobs@9.1.2)(@parcel/watcher@2.5.1)(@types/node@24.0.13)(@vue/compiler-sfc@3.5.17)(db0@0.3.2)(eslint@9.31.0(jiti@2.4.2))(ioredis@5.6.1)(lightningcss@1.30.1)(magicast@0.3.5)(optionator@0.9.4)(rollup@4.45.0)(terser@5.43.1)(tslite@5.7.3)(vite@7.0.4(@types/node@24.0.13)(jiti@2.4.2)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.0))(yaml@2.8.0)
|
||||
uuid:
|
||||
specifier: ^11.1.0
|
||||
version: 11.1.0
|
||||
vue:
|
||||
specifier: ^3.5.17
|
||||
version: 3.5.17(tslite@5.7.3)
|
||||
vue-router:
|
||||
specifier: ^4.5.1
|
||||
version: 4.5.1(vue@3.5.17(tslite@5.7.3))
|
||||
vuedraggable:
|
||||
specifier: ^4.1.0
|
||||
version: 4.1.0(vue@3.5.17(tslite@5.7.3))
|
||||
zod:
|
||||
specifier: ^4.0.5
|
||||
version: 4.0.5
|
||||
@@ -1766,6 +1757,9 @@ packages:
|
||||
'@tauri-apps/api@2.6.0':
|
||||
resolution: {integrity: sha512-hRNcdercfgpzgFrMXWwNDBN0B7vNzOzRepy6ZAmhxi5mDLVPNrTpo9MGg2tN/F7JRugj4d2aF7E1rtPXAHaetg==}
|
||||
|
||||
'@tauri-apps/api@2.8.0':
|
||||
resolution: {integrity: sha512-ga7zdhbS2GXOMTIZRT0mYjKJtR9fivsXzsyq5U3vjDL0s6DTMwYRm0UHNjzTY5dh4+LSC68Sm/7WEiimbQNYlw==}
|
||||
|
||||
'@tauri-apps/cli-darwin-arm64@2.6.2':
|
||||
resolution: {integrity: sha512-YlvT+Yb7u2HplyN2Cf/nBplCQARC/I4uedlYHlgtxg6rV7xbo9BvG1jLOo29IFhqA2rOp5w1LtgvVGwsOf2kxw==}
|
||||
engines: {node: '>= 10'}
|
||||
@@ -1842,6 +1836,9 @@ packages:
|
||||
engines: {node: '>= 10'}
|
||||
hasBin: true
|
||||
|
||||
'@tauri-apps/plugin-dialog@2.3.3':
|
||||
resolution: {integrity: sha512-cWXB9QJDbLIA0v7I5QY183awazBEQNPhp19iPvrMZoJRX8SbFkhWFx1/q7zy7xGpXXzxz29qtq6z21Ho7W5Iew==}
|
||||
|
||||
'@tauri-apps/plugin-fs@2.4.0':
|
||||
resolution: {integrity: sha512-Sp8AdDcbyXyk6LD6Pmdx44SH3LPeNAvxR2TFfq/8CwqzfO1yOyV+RzT8fov0NNN7d9nvW7O7MtMAptJ42YXA5g==}
|
||||
|
||||
@@ -1898,9 +1895,6 @@ packages:
|
||||
'@types/unist@3.0.3':
|
||||
resolution: {integrity: sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==}
|
||||
|
||||
'@types/uuid@10.0.0':
|
||||
resolution: {integrity: sha512-7gqG38EyHgyP1S+7+xomFtL+ZNHcKv6DwNaCZmJmo1vgMugyF3TCnXVg4t1uk89mLNwnLtnY3TpOpCOyp1/xHQ==}
|
||||
|
||||
'@types/web-bluetooth@0.0.20':
|
||||
resolution: {integrity: sha512-g9gZnnXVq7gM7v3tJCWV/qw7w+KeOlSHAhgF9RytFyifW6AF61hdT2ucrYhPq9hLs5JIryeupHV3qGk95dH9ow==}
|
||||
|
||||
@@ -2069,29 +2063,6 @@ packages:
|
||||
vitest:
|
||||
optional: true
|
||||
|
||||
'@vue-flow/background@1.3.2':
|
||||
resolution: {integrity: sha512-eJPhDcLj1wEo45bBoqTXw1uhl0yK2RaQGnEINqvvBsAFKh/camHJd5NPmOdS1w+M9lggc9igUewxaEd3iCQX2w==}
|
||||
peerDependencies:
|
||||
'@vue-flow/core': ^1.23.0
|
||||
vue: ^3.3.0
|
||||
|
||||
'@vue-flow/controls@1.1.3':
|
||||
resolution: {integrity: sha512-XCf+G+jCvaWURdFlZmOjifZGw3XMhN5hHlfMGkWh9xot+9nH9gdTZtn+ldIJKtarg3B21iyHU8JjKDhYcB6JMw==}
|
||||
peerDependencies:
|
||||
'@vue-flow/core': ^1.23.0
|
||||
vue: ^3.3.0
|
||||
|
||||
'@vue-flow/core@1.46.0':
|
||||
resolution: {integrity: sha512-vNIeFcbHuDgl1PSjABL/p+PHlTZYwt1NfZ+htjYlZtqzPfCSkP9URk/TY9ahORBWd/UvgxaNY+AQQoV2O49rtA==}
|
||||
peerDependencies:
|
||||
vue: ^3.3.0
|
||||
|
||||
'@vue-flow/minimap@1.5.4':
|
||||
resolution: {integrity: sha512-l4C+XTAXnRxsRpUdN7cAVFBennC1sVRzq4bDSpVK+ag7tdMczAnhFYGgbLkUw3v3sY6gokyWwMl8CDonp8eB2g==}
|
||||
peerDependencies:
|
||||
'@vue-flow/core': ^1.23.0
|
||||
vue: ^3.3.0
|
||||
|
||||
'@vue-macros/common@3.0.0-beta.15':
|
||||
resolution: {integrity: sha512-DMgq/rIh1H20WYNWU7krIbEfJRYDDhy7ix64GlT4AVUJZZWCZ5pxiYVJR3A3GmWQPkn7Pg7i3oIiGqu4JGC65w==}
|
||||
engines: {node: '>=20.18.0'}
|
||||
@@ -2742,44 +2713,6 @@ packages:
|
||||
csstype@3.1.3:
|
||||
resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==}
|
||||
|
||||
d3-color@3.1.0:
|
||||
resolution: {integrity: sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==}
|
||||
engines: {node: '>=12'}
|
||||
|
||||
d3-dispatch@3.0.1:
|
||||
resolution: {integrity: sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg==}
|
||||
engines: {node: '>=12'}
|
||||
|
||||
d3-drag@3.0.0:
|
||||
resolution: {integrity: sha512-pWbUJLdETVA8lQNJecMxoXfH6x+mO2UQo8rSmZ+QqxcbyA3hfeprFgIT//HW2nlHChWeIIMwS2Fq+gEARkhTkg==}
|
||||
engines: {node: '>=12'}
|
||||
|
||||
d3-ease@3.0.1:
|
||||
resolution: {integrity: sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==}
|
||||
engines: {node: '>=12'}
|
||||
|
||||
d3-interpolate@3.0.1:
|
||||
resolution: {integrity: sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==}
|
||||
engines: {node: '>=12'}
|
||||
|
||||
d3-selection@3.0.0:
|
||||
resolution: {integrity: sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==}
|
||||
engines: {node: '>=12'}
|
||||
|
||||
d3-timer@3.0.1:
|
||||
resolution: {integrity: sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==}
|
||||
engines: {node: '>=12'}
|
||||
|
||||
d3-transition@3.0.1:
|
||||
resolution: {integrity: sha512-ApKvfjsSR6tg06xrL434C0WydLr7JewBB3V+/39RMHsaXTOG0zmt/OAXeng5M5LBm0ojmxJrpomQVZ1aPvBL4w==}
|
||||
engines: {node: '>=12'}
|
||||
peerDependencies:
|
||||
d3-selection: 2 - 3
|
||||
|
||||
d3-zoom@3.0.0:
|
||||
resolution: {integrity: sha512-b8AmV3kfQaqWAuacbPuNbL6vahnOJflOhexLzMMNLga62+/nh0JzvJ0aO/5a5MVgUFGS7Hu1P9P03o3fJkDCyw==}
|
||||
engines: {node: '>=12'}
|
||||
|
||||
data-uri-to-buffer@4.0.1:
|
||||
resolution: {integrity: sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==}
|
||||
engines: {node: '>= 12'}
|
||||
@@ -4006,6 +3939,11 @@ packages:
|
||||
lru-cache@5.1.1:
|
||||
resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==}
|
||||
|
||||
lucide-vue-next@0.540.0:
|
||||
resolution: {integrity: sha512-H7qhKVNKLyoFMo05pWcGSWBiLPiI3zJmWV65SuXWHlrIGIcvDer10xAyWcRJ0KLzIH5k5+yi7AGw/Xi1VF8Pbw==}
|
||||
peerDependencies:
|
||||
vue: '>=3.0.1'
|
||||
|
||||
luxon@3.7.1:
|
||||
resolution: {integrity: sha512-RkRWjA926cTvz5rAb1BqyWkKbbjzCGchDUIKMCUvNi17j6f6j8uHGDV82Aqcqtzd+icoYpELmG3ksgGiFNNcNg==}
|
||||
engines: {node: '>=12'}
|
||||
@@ -5073,6 +5011,9 @@ packages:
|
||||
smob@1.5.0:
|
||||
resolution: {integrity: sha512-g6T+p7QO8npa+/hNx9ohv1E5pVCmWrVCUzUXJyLdMmftX6ER0oiWY/w9knEonLpnOp6b6FenKnMfR8gqwWdwig==}
|
||||
|
||||
sortablejs@1.14.0:
|
||||
resolution: {integrity: sha512-pBXvQCs5/33fdN1/39pPL0NZF20LeRbLQ5jtnheIPN9JQAaufGjKdWduZn4U7wCtVuzKhmRkI0DFYHYRbB2H1w==}
|
||||
|
||||
source-map-js@1.2.1:
|
||||
resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
@@ -5695,6 +5636,11 @@ packages:
|
||||
typescript:
|
||||
optional: true
|
||||
|
||||
vuedraggable@4.1.0:
|
||||
resolution: {integrity: sha512-FU5HCWBmsf20GpP3eudURW3WdWTKIbEIQxh9/8GE806hydR9qZqRRxRE3RjqX7PkuLuMQG/A7n3cfj9rCEchww==}
|
||||
peerDependencies:
|
||||
vue: ^3.0.1
|
||||
|
||||
web-streams-polyfill@3.3.3:
|
||||
resolution: {integrity: sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==}
|
||||
engines: {node: '>= 8'}
|
||||
@@ -5876,7 +5822,7 @@ snapshots:
|
||||
'@eslint-community/eslint-plugin-eslint-comments': 4.5.0(eslint@9.31.0(jiti@2.4.2))
|
||||
'@eslint/markdown': 7.0.0
|
||||
'@stylistic/eslint-plugin': 5.1.0(eslint@9.31.0(jiti@2.4.2))
|
||||
'@typescript-eslint/eslint-plugin': 8.37.0(@typescript-eslint/parser@8.37.0(eslint@9.31.0(jiti@2.4.2))(tslite@5.7.3))(eslint@9.31.0(jiti@2.4.2))(tslite@5.7.3)
|
||||
'@typescript-eslint/eslint-plugin': 8.37.0(@typescript-eslint/parser@8.36.0(eslint@9.31.0(jiti@2.4.2))(tslite@5.7.3))(eslint@9.31.0(jiti@2.4.2))(tslite@5.7.3)
|
||||
'@typescript-eslint/parser': 8.37.0(eslint@9.31.0(jiti@2.4.2))(tslite@5.7.3)
|
||||
'@vitest/eslint-plugin': 1.3.4(eslint@9.31.0(jiti@2.4.2))(tslite@5.7.3)
|
||||
ansis: 4.1.0
|
||||
@@ -7010,12 +6956,12 @@ snapshots:
|
||||
transitivePeerDependencies:
|
||||
- magicast
|
||||
|
||||
'@nuxt/ui-pro@3.3.2(@babel/parser@7.28.0)(@netlify/blobs@9.1.2)(db0@0.3.2)(embla-carousel@8.6.0)(ioredis@5.6.1)(jwt-decode@4.0.0)(magicast@0.3.5)(tslite@5.7.3)(vite@7.0.4(@types/node@24.0.13)(jiti@2.4.2)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.0))(vue-router@4.5.1(vue@3.5.17(tslite@5.7.3)))(vue@3.5.17(tslite@5.7.3))(zod@4.0.5)':
|
||||
'@nuxt/ui-pro@3.3.2(@babel/parser@7.28.0)(@netlify/blobs@9.1.2)(db0@0.3.2)(embla-carousel@8.6.0)(ioredis@5.6.1)(jwt-decode@4.0.0)(magicast@0.3.5)(sortablejs@1.14.0)(tslite@5.7.3)(vite@7.0.4(@types/node@24.0.13)(jiti@2.4.2)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.0))(vue-router@4.5.1(vue@3.5.17(tslite@5.7.3)))(vue@3.5.17(tslite@5.7.3))(zod@4.0.5)':
|
||||
dependencies:
|
||||
'@ai-sdk/vue': 1.2.12(vue@3.5.17(tslite@5.7.3))(zod@4.0.5)
|
||||
'@nuxt/kit': 4.0.3(magicast@0.3.5)
|
||||
'@nuxt/schema': 4.0.3
|
||||
'@nuxt/ui': 3.3.2(@babel/parser@7.28.0)(@netlify/blobs@9.1.2)(db0@0.3.2)(embla-carousel@8.6.0)(ioredis@5.6.1)(jwt-decode@4.0.0)(magicast@0.3.5)(tslite@5.7.3)(vite@7.0.4(@types/node@24.0.13)(jiti@2.4.2)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.0))(vue-router@4.5.1(vue@3.5.17(tslite@5.7.3)))(vue@3.5.17(tslite@5.7.3))(zod@4.0.5)
|
||||
'@nuxt/ui': 3.3.2(@babel/parser@7.28.0)(@netlify/blobs@9.1.2)(db0@0.3.2)(embla-carousel@8.6.0)(ioredis@5.6.1)(jwt-decode@4.0.0)(magicast@0.3.5)(sortablejs@1.14.0)(tslite@5.7.3)(vite@7.0.4(@types/node@24.0.13)(jiti@2.4.2)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.0))(vue-router@4.5.1(vue@3.5.17(tslite@5.7.3)))(vue@3.5.17(tslite@5.7.3))(zod@4.0.5)
|
||||
'@standard-schema/spec': 1.0.0
|
||||
'@vueuse/core': 13.7.0(vue@3.5.17(tslite@5.7.3))
|
||||
consola: 3.4.2
|
||||
@@ -7078,7 +7024,7 @@ snapshots:
|
||||
- vue
|
||||
- vue-router
|
||||
|
||||
'@nuxt/ui@3.3.2(@babel/parser@7.28.0)(@netlify/blobs@9.1.2)(db0@0.3.2)(embla-carousel@8.6.0)(ioredis@5.6.1)(jwt-decode@4.0.0)(magicast@0.3.5)(tslite@5.7.3)(vite@7.0.4(@types/node@24.0.13)(jiti@2.4.2)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.0))(vue-router@4.5.1(vue@3.5.17(tslite@5.7.3)))(vue@3.5.17(tslite@5.7.3))(zod@4.0.5)':
|
||||
'@nuxt/ui@3.3.2(@babel/parser@7.28.0)(@netlify/blobs@9.1.2)(db0@0.3.2)(embla-carousel@8.6.0)(ioredis@5.6.1)(jwt-decode@4.0.0)(magicast@0.3.5)(sortablejs@1.14.0)(tslite@5.7.3)(vite@7.0.4(@types/node@24.0.13)(jiti@2.4.2)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.0))(vue-router@4.5.1(vue@3.5.17(tslite@5.7.3)))(vue@3.5.17(tslite@5.7.3))(zod@4.0.5)':
|
||||
dependencies:
|
||||
'@iconify/vue': 5.0.0(vue@3.5.17(tslite@5.7.3))
|
||||
'@internationalized/date': 3.8.2
|
||||
@@ -7094,7 +7040,7 @@ snapshots:
|
||||
'@tanstack/vue-table': 8.21.3(vue@3.5.17(tslite@5.7.3))
|
||||
'@unhead/vue': 2.0.14(vue@3.5.17(tslite@5.7.3))
|
||||
'@vueuse/core': 13.7.0(vue@3.5.17(tslite@5.7.3))
|
||||
'@vueuse/integrations': 13.7.0(fuse.js@7.1.0)(jwt-decode@4.0.0)(vue@3.5.17(tslite@5.7.3))
|
||||
'@vueuse/integrations': 13.7.0(fuse.js@7.1.0)(jwt-decode@4.0.0)(sortablejs@1.14.0)(vue@3.5.17(tslite@5.7.3))
|
||||
colortranslator: 5.0.0
|
||||
consola: 3.4.2
|
||||
defu: 6.1.4
|
||||
@@ -7727,6 +7673,8 @@ snapshots:
|
||||
|
||||
'@tauri-apps/api@2.6.0': {}
|
||||
|
||||
'@tauri-apps/api@2.8.0': {}
|
||||
|
||||
'@tauri-apps/cli-darwin-arm64@2.6.2':
|
||||
optional: true
|
||||
|
||||
@@ -7774,6 +7722,10 @@ snapshots:
|
||||
'@tauri-apps/cli-win32-ia32-msvc': 2.6.2
|
||||
'@tauri-apps/cli-win32-x64-msvc': 2.6.2
|
||||
|
||||
'@tauri-apps/plugin-dialog@2.3.3':
|
||||
dependencies:
|
||||
'@tauri-apps/api': 2.8.0
|
||||
|
||||
'@tauri-apps/plugin-fs@2.4.0':
|
||||
dependencies:
|
||||
'@tauri-apps/api': 2.6.0
|
||||
@@ -7832,8 +7784,6 @@ snapshots:
|
||||
|
||||
'@types/unist@3.0.3': {}
|
||||
|
||||
'@types/uuid@10.0.0': {}
|
||||
|
||||
'@types/web-bluetooth@0.0.20': {}
|
||||
|
||||
'@types/web-bluetooth@0.0.21': {}
|
||||
@@ -7860,10 +7810,10 @@ snapshots:
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@typescript-eslint/eslint-plugin@8.37.0(@typescript-eslint/parser@8.37.0(eslint@9.31.0(jiti@2.4.2))(tslite@5.7.3))(eslint@9.31.0(jiti@2.4.2))(tslite@5.7.3)':
|
||||
'@typescript-eslint/eslint-plugin@8.37.0(@typescript-eslint/parser@8.36.0(eslint@9.31.0(jiti@2.4.2))(tslite@5.7.3))(eslint@9.31.0(jiti@2.4.2))(tslite@5.7.3)':
|
||||
dependencies:
|
||||
'@eslint-community/regexpp': 4.12.1
|
||||
'@typescript-eslint/parser': 8.37.0(eslint@9.31.0(jiti@2.4.2))(tslite@5.7.3)
|
||||
'@typescript-eslint/parser': 8.36.0(eslint@9.31.0(jiti@2.4.2))(tslite@5.7.3)
|
||||
'@typescript-eslint/scope-manager': 8.37.0
|
||||
'@typescript-eslint/type-utils': 8.37.0(eslint@9.31.0(jiti@2.4.2))(tslite@5.7.3)
|
||||
'@typescript-eslint/utils': 8.37.0(eslint@9.31.0(jiti@2.4.2))(tslite@5.7.3)
|
||||
@@ -8085,34 +8035,6 @@ snapshots:
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@vue-flow/background@1.3.2(@vue-flow/core@1.46.0(vue@3.5.17(tslite@5.7.3)))(vue@3.5.17(tslite@5.7.3))':
|
||||
dependencies:
|
||||
'@vue-flow/core': 1.46.0(vue@3.5.17(tslite@5.7.3))
|
||||
vue: 3.5.17(tslite@5.7.3)
|
||||
|
||||
'@vue-flow/controls@1.1.3(@vue-flow/core@1.46.0(vue@3.5.17(tslite@5.7.3)))(vue@3.5.17(tslite@5.7.3))':
|
||||
dependencies:
|
||||
'@vue-flow/core': 1.46.0(vue@3.5.17(tslite@5.7.3))
|
||||
vue: 3.5.17(tslite@5.7.3)
|
||||
|
||||
'@vue-flow/core@1.46.0(vue@3.5.17(tslite@5.7.3))':
|
||||
dependencies:
|
||||
'@vueuse/core': 10.11.1(vue@3.5.17(tslite@5.7.3))
|
||||
d3-drag: 3.0.0
|
||||
d3-interpolate: 3.0.1
|
||||
d3-selection: 3.0.0
|
||||
d3-zoom: 3.0.0
|
||||
vue: 3.5.17(tslite@5.7.3)
|
||||
transitivePeerDependencies:
|
||||
- '@vue/composition-api'
|
||||
|
||||
'@vue-flow/minimap@1.5.4(@vue-flow/core@1.46.0(vue@3.5.17(tslite@5.7.3)))(vue@3.5.17(tslite@5.7.3))':
|
||||
dependencies:
|
||||
'@vue-flow/core': 1.46.0(vue@3.5.17(tslite@5.7.3))
|
||||
d3-selection: 3.0.0
|
||||
d3-zoom: 3.0.0
|
||||
vue: 3.5.17(tslite@5.7.3)
|
||||
|
||||
'@vue-macros/common@3.0.0-beta.15(vue@3.5.17(tslite@5.7.3))':
|
||||
dependencies:
|
||||
'@vue/compiler-sfc': 3.5.17
|
||||
@@ -8269,7 +8191,7 @@ snapshots:
|
||||
'@vueuse/shared': 13.7.0(vue@3.5.17(tslite@5.7.3))
|
||||
vue: 3.5.17(tslite@5.7.3)
|
||||
|
||||
'@vueuse/integrations@13.7.0(fuse.js@7.1.0)(jwt-decode@4.0.0)(vue@3.5.17(tslite@5.7.3))':
|
||||
'@vueuse/integrations@13.7.0(fuse.js@7.1.0)(jwt-decode@4.0.0)(sortablejs@1.14.0)(vue@3.5.17(tslite@5.7.3))':
|
||||
dependencies:
|
||||
'@vueuse/core': 13.7.0(vue@3.5.17(tslite@5.7.3))
|
||||
'@vueuse/shared': 13.7.0(vue@3.5.17(tslite@5.7.3))
|
||||
@@ -8277,6 +8199,7 @@ snapshots:
|
||||
optionalDependencies:
|
||||
fuse.js: 7.1.0
|
||||
jwt-decode: 4.0.0
|
||||
sortablejs: 1.14.0
|
||||
|
||||
'@vueuse/metadata@10.11.1': {}
|
||||
|
||||
@@ -8856,42 +8779,6 @@ snapshots:
|
||||
|
||||
csstype@3.1.3: {}
|
||||
|
||||
d3-color@3.1.0: {}
|
||||
|
||||
d3-dispatch@3.0.1: {}
|
||||
|
||||
d3-drag@3.0.0:
|
||||
dependencies:
|
||||
d3-dispatch: 3.0.1
|
||||
d3-selection: 3.0.0
|
||||
|
||||
d3-ease@3.0.1: {}
|
||||
|
||||
d3-interpolate@3.0.1:
|
||||
dependencies:
|
||||
d3-color: 3.1.0
|
||||
|
||||
d3-selection@3.0.0: {}
|
||||
|
||||
d3-timer@3.0.1: {}
|
||||
|
||||
d3-transition@3.0.1(d3-selection@3.0.0):
|
||||
dependencies:
|
||||
d3-color: 3.1.0
|
||||
d3-dispatch: 3.0.1
|
||||
d3-ease: 3.0.1
|
||||
d3-interpolate: 3.0.1
|
||||
d3-selection: 3.0.0
|
||||
d3-timer: 3.0.1
|
||||
|
||||
d3-zoom@3.0.0:
|
||||
dependencies:
|
||||
d3-dispatch: 3.0.1
|
||||
d3-drag: 3.0.0
|
||||
d3-interpolate: 3.0.1
|
||||
d3-selection: 3.0.0
|
||||
d3-transition: 3.0.1(d3-selection@3.0.0)
|
||||
|
||||
data-uri-to-buffer@4.0.1: {}
|
||||
|
||||
db0@0.3.2: {}
|
||||
@@ -9361,7 +9248,7 @@ snapshots:
|
||||
dependencies:
|
||||
eslint: 9.31.0(jiti@2.4.2)
|
||||
optionalDependencies:
|
||||
'@typescript-eslint/eslint-plugin': 8.37.0(@typescript-eslint/parser@8.37.0(eslint@9.31.0(jiti@2.4.2))(tslite@5.7.3))(eslint@9.31.0(jiti@2.4.2))(tslite@5.7.3)
|
||||
'@typescript-eslint/eslint-plugin': 8.37.0(@typescript-eslint/parser@8.36.0(eslint@9.31.0(jiti@2.4.2))(tslite@5.7.3))(eslint@9.31.0(jiti@2.4.2))(tslite@5.7.3)
|
||||
|
||||
eslint-plugin-vue@10.3.0(@typescript-eslint/parser@8.36.0(eslint@9.31.0(jiti@2.4.2))(tslite@5.7.3))(eslint@9.31.0(jiti@2.4.2))(vue-eslint-parser@10.2.0(eslint@9.31.0(jiti@2.4.2))):
|
||||
dependencies:
|
||||
@@ -10147,6 +10034,10 @@ snapshots:
|
||||
dependencies:
|
||||
yallist: 3.1.1
|
||||
|
||||
lucide-vue-next@0.540.0(vue@3.5.17(tslite@5.7.3)):
|
||||
dependencies:
|
||||
vue: 3.5.17(tslite@5.7.3)
|
||||
|
||||
luxon@3.7.1: {}
|
||||
|
||||
magic-regexp@0.10.0:
|
||||
@@ -11644,6 +11535,8 @@ snapshots:
|
||||
|
||||
smob@1.5.0: {}
|
||||
|
||||
sortablejs@1.14.0: {}
|
||||
|
||||
source-map-js@1.2.1: {}
|
||||
|
||||
source-map-support@0.5.21:
|
||||
@@ -12275,6 +12168,11 @@ snapshots:
|
||||
optionalDependencies:
|
||||
typescript: tslite@5.7.3
|
||||
|
||||
vuedraggable@4.1.0(vue@3.5.17(tslite@5.7.3)):
|
||||
dependencies:
|
||||
sortablejs: 1.14.0
|
||||
vue: 3.5.17(tslite@5.7.3)
|
||||
|
||||
web-streams-polyfill@3.3.3: {}
|
||||
|
||||
webidl-conversions@3.0.1: {}
|
||||
|
181
src-tauri/Cargo.lock
generated
@@ -62,6 +62,27 @@ version = "1.0.98"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487"
|
||||
|
||||
[[package]]
|
||||
name = "ashpd"
|
||||
version = "0.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6cbdf310d77fd3aaee6ea2093db7011dc2d35d2eb3481e5607f1f8d942ed99df"
|
||||
dependencies = [
|
||||
"enumflags2",
|
||||
"futures-channel",
|
||||
"futures-util",
|
||||
"rand 0.9.2",
|
||||
"raw-window-handle",
|
||||
"serde",
|
||||
"serde_repr",
|
||||
"tokio",
|
||||
"url",
|
||||
"wayland-backend",
|
||||
"wayland-client",
|
||||
"wayland-protocols",
|
||||
"zbus",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "async-broadcast"
|
||||
version = "0.7.2"
|
||||
@@ -745,6 +766,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "89a09f22a6c6069a18470eb92d2298acf25463f14256d24778e1230d789a2aec"
|
||||
dependencies = [
|
||||
"bitflags 2.9.1",
|
||||
"block2 0.6.1",
|
||||
"libc",
|
||||
"objc2 0.6.1",
|
||||
]
|
||||
|
||||
@@ -759,6 +782,15 @@ dependencies = [
|
||||
"syn 2.0.104",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dlib"
|
||||
version = "0.5.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "330c60081dcc4c72131f8eb70510f1ac07223e5d4163db481a04a0befcffa412"
|
||||
dependencies = [
|
||||
"libloading",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dlopen2"
|
||||
version = "0.7.0"
|
||||
@@ -782,6 +814,12 @@ dependencies = [
|
||||
"syn 2.0.104",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "downcast-rs"
|
||||
version = "1.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "75b325c5dbd37f80359721ad39aca5a29fb04c89279657cffdda8736d0c0b9d2"
|
||||
|
||||
[[package]]
|
||||
name = "dpi"
|
||||
version = "0.1.2"
|
||||
@@ -2180,6 +2218,7 @@ dependencies = [
|
||||
"serde_json",
|
||||
"tauri",
|
||||
"tauri-build",
|
||||
"tauri-plugin-dialog",
|
||||
"tauri-plugin-fs",
|
||||
"tauri-plugin-notification",
|
||||
"tauri-plugin-os",
|
||||
@@ -2891,6 +2930,16 @@ dependencies = [
|
||||
"rand_core 0.6.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand"
|
||||
version = "0.9.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1"
|
||||
dependencies = [
|
||||
"rand_chacha 0.9.0",
|
||||
"rand_core 0.9.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_chacha"
|
||||
version = "0.2.2"
|
||||
@@ -2911,6 +2960,16 @@ dependencies = [
|
||||
"rand_core 0.6.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_chacha"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb"
|
||||
dependencies = [
|
||||
"ppv-lite86",
|
||||
"rand_core 0.9.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_core"
|
||||
version = "0.5.1"
|
||||
@@ -2929,6 +2988,15 @@ dependencies = [
|
||||
"getrandom 0.2.16",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_core"
|
||||
version = "0.9.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38"
|
||||
dependencies = [
|
||||
"getrandom 0.3.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_hc"
|
||||
version = "0.2.0"
|
||||
@@ -3057,6 +3125,31 @@ dependencies = [
|
||||
"web-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rfd"
|
||||
version = "0.15.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ef2bee61e6cffa4635c72d7d81a84294e28f0930db0ddcb0f66d10244674ebed"
|
||||
dependencies = [
|
||||
"ashpd",
|
||||
"block2 0.6.1",
|
||||
"dispatch2",
|
||||
"glib-sys",
|
||||
"gobject-sys",
|
||||
"gtk-sys",
|
||||
"js-sys",
|
||||
"log",
|
||||
"objc2 0.6.1",
|
||||
"objc2-app-kit",
|
||||
"objc2-core-foundation",
|
||||
"objc2-foundation 0.3.1",
|
||||
"raw-window-handle",
|
||||
"wasm-bindgen",
|
||||
"wasm-bindgen-futures",
|
||||
"web-sys",
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustc-demangle"
|
||||
version = "0.1.25"
|
||||
@@ -3157,6 +3250,12 @@ dependencies = [
|
||||
"syn 2.0.104",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "scoped-tls"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294"
|
||||
|
||||
[[package]]
|
||||
name = "scopeguard"
|
||||
version = "1.2.0"
|
||||
@@ -3799,6 +3898,24 @@ dependencies = [
|
||||
"walkdir",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tauri-plugin-dialog"
|
||||
version = "2.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1aefb14219b492afb30b12647b5b1247cadd2c0603467310c36e0f7ae1698c28"
|
||||
dependencies = [
|
||||
"log",
|
||||
"raw-window-handle",
|
||||
"rfd",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"tauri",
|
||||
"tauri-plugin",
|
||||
"tauri-plugin-fs",
|
||||
"thiserror 2.0.12",
|
||||
"url",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tauri-plugin-fs"
|
||||
version = "2.4.0"
|
||||
@@ -4122,9 +4239,11 @@ dependencies = [
|
||||
"libc",
|
||||
"mio",
|
||||
"pin-project-lite",
|
||||
"signal-hook-registry",
|
||||
"slab",
|
||||
"socket2",
|
||||
"tokio-macros",
|
||||
"tracing",
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
@@ -4638,6 +4757,66 @@ dependencies = [
|
||||
"web-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wayland-backend"
|
||||
version = "0.3.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "673a33c33048a5ade91a6b139580fa174e19fb0d23f396dca9fa15f2e1e49b35"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"downcast-rs",
|
||||
"rustix",
|
||||
"scoped-tls",
|
||||
"smallvec",
|
||||
"wayland-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wayland-client"
|
||||
version = "0.31.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c66a47e840dc20793f2264eb4b3e4ecb4b75d91c0dd4af04b456128e0bdd449d"
|
||||
dependencies = [
|
||||
"bitflags 2.9.1",
|
||||
"rustix",
|
||||
"wayland-backend",
|
||||
"wayland-scanner",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wayland-protocols"
|
||||
version = "0.32.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "efa790ed75fbfd71283bd2521a1cfdc022aabcc28bdcff00851f9e4ae88d9901"
|
||||
dependencies = [
|
||||
"bitflags 2.9.1",
|
||||
"wayland-backend",
|
||||
"wayland-client",
|
||||
"wayland-scanner",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wayland-scanner"
|
||||
version = "0.31.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "54cb1e9dc49da91950bdfd8b848c49330536d9d1fb03d4bfec8cae50caa50ae3"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quick-xml 0.37.5",
|
||||
"quote",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wayland-sys"
|
||||
version = "0.31.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "34949b42822155826b41db8e5d0c1be3a2bd296c747577a43a3e6daefc296142"
|
||||
dependencies = [
|
||||
"dlib",
|
||||
"log",
|
||||
"pkg-config",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "web-sys"
|
||||
version = "0.3.77"
|
||||
@@ -5271,6 +5450,7 @@ dependencies = [
|
||||
"ordered-stream",
|
||||
"serde",
|
||||
"serde_repr",
|
||||
"tokio",
|
||||
"tracing",
|
||||
"uds_windows",
|
||||
"windows-sys 0.59.0",
|
||||
@@ -5390,6 +5570,7 @@ dependencies = [
|
||||
"endi",
|
||||
"enumflags2",
|
||||
"serde",
|
||||
"url",
|
||||
"winnow 0.7.12",
|
||||
"zvariant_derive",
|
||||
"zvariant_utils",
|
||||
|
@@ -24,6 +24,7 @@ tauri-plugin-shell = "2.3.0"
|
||||
tauri-plugin-notification = "2.3.0"
|
||||
tauri-plugin-os = "2.3.0"
|
||||
tauri-plugin-fs = "2.4.0"
|
||||
tauri-plugin-dialog = "2"
|
||||
tauri-plugin-store = "2.3.0"
|
||||
serde_json = "1"
|
||||
|
||||
|
@@ -41,6 +41,9 @@
|
||||
"fs:allow-document-write",
|
||||
"store:default",
|
||||
"core:webview:allow-create-webview",
|
||||
"core:webview:allow-create-webview-window"
|
||||
"core:webview:allow-create-webview-window",
|
||||
"dialog:default",
|
||||
"dialog:default",
|
||||
"dialog:default"
|
||||
]
|
||||
}
|
||||
|
@@ -1 +1 @@
|
||||
{ "main": { "identifier": "main", "description": "Capabilities for the app window", "local": true, "windows": ["main", "secondary"], "permissions": ["core:path:default", "core:event:default", "core:window:default", "core:app:default", "core:resources:default", "core:menu:default", "core:tray:default", "shell:allow-open", { "identifier": "shell:allow-execute", "allow": [{ "args": ["-c", { "validator": "\\S+" }], "cmd": "sh", "name": "exec-sh", "sidecar": false }] }, "notification:default", "os:allow-platform", "os:allow-arch", "os:allow-family", "os:allow-version", "os:allow-locale", "fs:allow-document-read", "fs:allow-document-write", "store:default", "core:webview:allow-create-webview", "core:webview:allow-create-webview-window"] } }
|
||||
{"main":{"identifier":"main","description":"Capabilities for the app window","local":true,"windows":["main","secondary"],"permissions":["core:path:default","core:event:default","core:window:default","core:app:default","core:resources:default","core:menu:default","core:tray:default","shell:allow-open",{"identifier":"shell:allow-execute","allow":[{"args":["-c",{"validator":"\\S+"}],"cmd":"sh","name":"exec-sh","sidecar":false}]},"notification:default","os:allow-platform","os:allow-arch","os:allow-family","os:allow-version","os:allow-locale","fs:allow-document-read","fs:allow-document-write","store:default","core:webview:allow-create-webview","core:webview:allow-create-webview-window","dialog:default","dialog:default","dialog:default"]}}
|
Before Width: | Height: | Size: 8.6 KiB After Width: | Height: | Size: 14 KiB |
Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 14 KiB |
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 14 KiB |
Before Width: | Height: | Size: 2.7 KiB After Width: | Height: | Size: 14 KiB |
Before Width: | Height: | Size: 372 KiB After Width: | Height: | Size: 14 KiB |
Before Width: | Height: | Size: 30 KiB After Width: | Height: | Size: 128 KiB |
Before Width: | Height: | Size: 66 KiB After Width: | Height: | Size: 14 KiB |
@@ -31,6 +31,7 @@ pub fn run() {
|
||||
.plugin(tauri_plugin_notification::init())
|
||||
.plugin(tauri_plugin_os::init())
|
||||
.plugin(tauri_plugin_fs::init())
|
||||
.plugin(tauri_plugin_dialog::init())
|
||||
.plugin(tauri_plugin_store::Builder::new().build())
|
||||
.run(tauri::generate_context!())
|
||||
.expect("error while running tauri application");
|
||||
|
@@ -31,24 +31,25 @@
|
||||
"beforeDevCommand": "pnpm dev",
|
||||
"devUrl": "http://localhost:3000"
|
||||
},
|
||||
"productName": "Nuxtor",
|
||||
"version": "1.4.0",
|
||||
"productName": "PlayTool-Demo",
|
||||
"version": "1.0.0",
|
||||
"identifier": "com.nicolaspadari.nuxtor",
|
||||
"plugins": {},
|
||||
"app": {
|
||||
"windows": [
|
||||
{
|
||||
"title": "Nuxtor",
|
||||
"title": "PlayTool-Demo",
|
||||
"width": 1366,
|
||||
"height": 768,
|
||||
"minWidth": 375,
|
||||
"minHeight": 812,
|
||||
"resizable": false,
|
||||
"minWidth": 800,
|
||||
"minHeight": 600,
|
||||
"resizable": true,
|
||||
"fullscreen": false
|
||||
}
|
||||
],
|
||||
"security": {
|
||||
"csp": null
|
||||
}
|
||||
},
|
||||
"withGlobalTauri": false
|
||||
}
|
||||
}
|
||||
|
0
test-drag-drop.md
Normal file
@@ -1 +0,0 @@
|
||||
{"fileNames":[],"fileInfos":[],"root":[],"options":{"composite":true,"module":99,"target":99},"version":"5.7.3"}
|