mirror of
https://github.com/aljazceru/goose.git
synced 2025-12-18 22:54:24 +01:00
remove testing ui v2 directory (#2385)
This commit is contained in:
78
.github/workflows/ui-v2.yml
vendored
78
.github/workflows/ui-v2.yml
vendored
@@ -1,78 +0,0 @@
|
||||
name: UI v2 CI
|
||||
|
||||
on:
|
||||
push:
|
||||
paths:
|
||||
- 'ui-v2/**'
|
||||
branches:
|
||||
- main
|
||||
pull_request:
|
||||
paths:
|
||||
- 'ui-v2/**'
|
||||
branches:
|
||||
- main
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
ui-v2-checks:
|
||||
name: Lint and Test UI v2
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout Code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 'lts/*'
|
||||
cache: 'npm'
|
||||
cache-dependency-path: ui-v2/package-lock.json
|
||||
|
||||
- name: Install Dependencies
|
||||
working-directory: ui-v2
|
||||
run: npm ci
|
||||
|
||||
- name: Run All Checks
|
||||
working-directory: ui-v2
|
||||
run: npm run check-all
|
||||
|
||||
- name: Run Unit Tests
|
||||
working-directory: ui-v2
|
||||
run: npm test
|
||||
|
||||
- name: Install Playwright Browsers
|
||||
working-directory: ui-v2
|
||||
run: npx playwright install --with-deps chromium
|
||||
|
||||
# - name: Configure Electron Sandbox
|
||||
# working-directory: ui-v2
|
||||
# run: |
|
||||
# sudo chown root:root node_modules/electron/dist/chrome-sandbox
|
||||
# sudo chmod 4755 node_modules/electron/dist/chrome-sandbox
|
||||
|
||||
- name: Run E2E Tests
|
||||
working-directory: ui-v2
|
||||
env:
|
||||
HEADLESS: true
|
||||
NODE_OPTIONS: "--loader ts-node/esm --max-old-space-size=4096"
|
||||
run: |
|
||||
# Increase system limits
|
||||
sudo sysctl -w vm.max_map_count=262144
|
||||
sudo sysctl -w fs.file-max=65535
|
||||
ulimit -n 65535
|
||||
|
||||
# Run tests with xvfb
|
||||
xvfb-run --auto-servernum --server-args="-screen 0 1280x960x24" -- npm run test:e2e:web:headless
|
||||
|
||||
# todo: fix electron tests not running in GH workflow
|
||||
# xvfb-run --auto-servernum --server-args="-screen 0 1280x960x24" -- npm run test:e2e:electron:headless
|
||||
|
||||
- name: Upload Test Results
|
||||
if: always()
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: test-results
|
||||
path: |
|
||||
ui-v2/playwright-report/
|
||||
ui-v2/test-results/
|
||||
retention-days: 30
|
||||
@@ -5,9 +5,3 @@ if git diff --cached --name-only | grep -q "^ui/desktop/"; then
|
||||
. "$(dirname -- "$0")/_/husky.sh"
|
||||
cd ui/desktop && npx lint-staged
|
||||
fi
|
||||
|
||||
# Only auto-format desktop TS code if relevant files are modified
|
||||
if git diff --cached --name-only | grep -q "^ui-v2/"; then
|
||||
. "$(dirname -- "$0")/_/husky.sh"
|
||||
cd ui-v2 && npx lint-staged
|
||||
fi
|
||||
32
ui-v2/.gitignore
vendored
32
ui-v2/.gitignore
vendored
@@ -1,32 +0,0 @@
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
pnpm-debug.log*
|
||||
lerna-debug.log*
|
||||
|
||||
node_modules
|
||||
dist
|
||||
dist-ssr
|
||||
dist-electron
|
||||
*.local
|
||||
|
||||
# Editor directories and files
|
||||
.vscode
|
||||
.idea
|
||||
.DS_Store
|
||||
|
||||
# Build output
|
||||
.vite/
|
||||
out/
|
||||
|
||||
# Generated JavaScript files in source
|
||||
electron/**/*.js
|
||||
!electron/**/*.config.js
|
||||
|
||||
# Playwright
|
||||
test-results/
|
||||
playwright-report/
|
||||
playwright/.cache/
|
||||
@@ -1 +0,0 @@
|
||||
registry=https://registry.npmjs.org/
|
||||
@@ -1 +0,0 @@
|
||||
v23
|
||||
@@ -1,9 +0,0 @@
|
||||
{
|
||||
"semi": true,
|
||||
"trailingComma": "es5",
|
||||
"singleQuote": true,
|
||||
"printWidth": 100,
|
||||
"tabWidth": 2,
|
||||
"useTabs": false,
|
||||
"endOfLine": "auto"
|
||||
}
|
||||
@@ -1,4 +0,0 @@
|
||||
node_modules
|
||||
dist
|
||||
.vite
|
||||
coverage
|
||||
@@ -1,12 +0,0 @@
|
||||
{
|
||||
"extends": ["stylelint-config-standard"],
|
||||
"rules": {
|
||||
"at-rule-no-unknown": [
|
||||
true,
|
||||
{
|
||||
"ignoreAtRules": ["tailwind", "apply", "variants", "responsive", "screen"]
|
||||
}
|
||||
],
|
||||
"no-descending-specificity": null
|
||||
}
|
||||
}
|
||||
197
ui-v2/README.md
197
ui-v2/README.md
@@ -1,197 +0,0 @@
|
||||
# codename goose ui v2
|
||||
|
||||
Your on-machine AI agent, automating tasks seamlessly.
|
||||
|
||||
## Development
|
||||
|
||||
### Getting Started
|
||||
|
||||
```bash
|
||||
# Install dependencies
|
||||
npm install
|
||||
|
||||
# Start both web and electron development servers
|
||||
npm start
|
||||
|
||||
# Start web server only
|
||||
npm run start:web
|
||||
|
||||
# Start electron only
|
||||
npm run start:electron
|
||||
```
|
||||
|
||||
### Building and Packaging
|
||||
|
||||
```bash
|
||||
# Build web application
|
||||
npm run build:web
|
||||
|
||||
# Build electron application
|
||||
npm run build:electron
|
||||
|
||||
# Package electron app
|
||||
npm run package
|
||||
|
||||
# Create distributable
|
||||
npm run make
|
||||
|
||||
# Preview web build
|
||||
npm run preview:web
|
||||
```
|
||||
|
||||
### Quality and Testing
|
||||
|
||||
```bash
|
||||
# Run tests
|
||||
npm test
|
||||
|
||||
# Run tests with UI
|
||||
npm run test:ui
|
||||
|
||||
# Generate test coverage
|
||||
npm run test:coverage
|
||||
|
||||
# End-to-End Testing
|
||||
npm run test:e2e # Run all e2e tests headlessly
|
||||
npm run test:e2e:ui # Run e2e tests with UI mode for both web and electron
|
||||
|
||||
npm run test:e2e:web # Run web e2e tests with browser visible
|
||||
npm run test:e2e:web:headless # Run web e2e tests headlessly
|
||||
npm run test:e2e:web:ui # Run web e2e tests with Playwright UI mode
|
||||
|
||||
npm run test:e2e:electron # Run electron e2e tests with window visible
|
||||
npm run test:e2e:electron:headless # Run electron e2e tests headlessly
|
||||
npm run test:e2e:electron:ui # Run electron e2e tests with Playwright UI mode
|
||||
|
||||
# Type checking
|
||||
npm run typecheck # Check all TypeScript files
|
||||
npm run tsc:web # Check web TypeScript files
|
||||
npm run tsc:electron # Check electron TypeScript files
|
||||
|
||||
# Linting
|
||||
npm run lint # Run all linting
|
||||
npm run lint:fix # Fix linting issues
|
||||
npm run lint:style # Check CSS
|
||||
npm run lint:style:fix # Fix CSS issues
|
||||
|
||||
# Code formatting
|
||||
npm run prettier # Check formatting
|
||||
npm run prettier:fix # Fix formatting
|
||||
npm run format # Fix all formatting (prettier + style)
|
||||
|
||||
# Run all checks (types, lint, format)
|
||||
npm run check-all
|
||||
```
|
||||
|
||||
## Project Structure
|
||||
|
||||
```
|
||||
├── electron/ # Electron main process files
|
||||
│ ├── main.ts # Main process entry
|
||||
│ └── preload.ts # Preload script
|
||||
├── src/
|
||||
│ ├── components/ # React components
|
||||
│ ├── services/
|
||||
│ │ └── platform/ # Platform abstraction layer
|
||||
│ │ ├── web/ # Web implementation
|
||||
│ │ ├── electron/ # Electron implementation
|
||||
│ │ ├── IPlatformService.ts
|
||||
│ │ └── index.ts
|
||||
│ ├── test/ # Test setup and configurations
|
||||
│ │ ├── e2e/ # End-to-end test files
|
||||
│ │ │ ├── electron/ # Electron-specific e2e tests
|
||||
│ │ │ │ └── electron.spec.ts
|
||||
│ │ │ └── web/ # Web-specific e2e tests
|
||||
│ │ │ └── web.spec.ts
|
||||
│ │ ├── setup.ts
|
||||
│ │ └── types.d.ts
|
||||
│ ├── App.tsx
|
||||
│ ├── electron.tsx # Electron renderer entry
|
||||
│ └── web.tsx # Web entry
|
||||
├── electron.html # Electron HTML template
|
||||
├── index.html # Web HTML template
|
||||
├── playwright.config.ts # Playwright e2e test configuration
|
||||
├── vite.config.ts # Vite config for web
|
||||
├── vite.main.config.ts # Vite config for electron main
|
||||
├── vite.preload.config.ts # Vite config for preload script
|
||||
├── vite.renderer.config.ts # Vite config for electron renderer
|
||||
├── tsconfig.json # TypeScript config for web
|
||||
├── tsconfig.electron.json # TypeScript config for electron
|
||||
└── forge.config.ts # Electron Forge config
|
||||
```
|
||||
|
||||
## Architecture
|
||||
|
||||
The application follows a platform-agnostic architecture that allows it to run seamlessly in both web browsers and Electron environments. Here's a detailed breakdown of the key architectural components:
|
||||
|
||||
### Platform Abstraction Layer
|
||||
|
||||
The core of the architecture is built around a platform abstraction layer that provides a consistent interface for platform-specific functionality:
|
||||
|
||||
```typescript
|
||||
// Platform Service Interface
|
||||
export interface IPlatformService {
|
||||
copyToClipboard(text: string): Promise<void>;
|
||||
// Additional platform-specific operations can be added here
|
||||
}
|
||||
```
|
||||
|
||||
This is implemented through two concrete classes:
|
||||
|
||||
- `WebPlatformService`: Implements functionality for web browsers using Web APIs
|
||||
- `ElectronPlatformService`: Implements functionality for Electron using IPC
|
||||
|
||||
### Platform Service Pattern
|
||||
|
||||
The application uses a dependency injection pattern for platform services:
|
||||
|
||||
1. **Service Interface**: `IPlatformService` defines the contract for platform-specific operations
|
||||
2. **Platform Detection**: The app automatically detects the running environment and initializes the appropriate service
|
||||
3. **Unified Access**: Components access platform features through a single `platformService` instance
|
||||
|
||||
Example usage in components:
|
||||
|
||||
```typescript
|
||||
import { platformService } from '@platform';
|
||||
|
||||
// Platform-agnostic code
|
||||
await platformService.copyToClipboard(text);
|
||||
```
|
||||
|
||||
### Electron Integration
|
||||
|
||||
For Electron-specific functionality, the architecture includes:
|
||||
|
||||
1. **Preload Script**: Safely exposes Electron APIs to the renderer process
|
||||
|
||||
```typescript
|
||||
// Type definitions for Electron APIs
|
||||
declare global {
|
||||
interface Window {
|
||||
electronAPI: {
|
||||
copyToClipboard: (text: string) => Promise<void>;
|
||||
};
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
2. **IPC Communication**: Typed handlers for main process communication
|
||||
|
||||
```typescript
|
||||
// Electron implementation
|
||||
export class ElectronPlatformService implements IPlatformService {
|
||||
async copyToClipboard(text: string): Promise<void> {
|
||||
return window.electronAPI.copyToClipboard(text);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Build System
|
||||
|
||||
The project uses a sophisticated build system with multiple configurations:
|
||||
|
||||
1. **Web Build**: Vite-based build for web deployment
|
||||
2. **Electron Build**:
|
||||
- Main Process: Separate Vite config for Electron main process
|
||||
- Renderer Process: Specialized config for Electron renderer
|
||||
- Preload Scripts: Dedicated build configuration for preload scripts
|
||||
@@ -1,17 +0,0 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<meta
|
||||
http-equiv="Content-Security-Policy"
|
||||
content="default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'"
|
||||
/>
|
||||
<link href="/src/index.css" rel="stylesheet" />
|
||||
<title>Goose v2</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
<script type="module" src="/src/electron.tsx"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,153 +0,0 @@
|
||||
import * as fs from 'fs/promises';
|
||||
import * as path from 'path';
|
||||
|
||||
import { app, BrowserWindow, ipcMain, clipboard, dialog, session } from 'electron';
|
||||
import type {
|
||||
IpcMainInvokeEvent,
|
||||
OnHeadersReceivedListenerDetails,
|
||||
HeadersReceivedResponse,
|
||||
} from 'electron';
|
||||
|
||||
const isDevelopment = !app.isPackaged;
|
||||
const isHeadless = process.env.HEADLESS === 'true';
|
||||
|
||||
// Enable sandbox before app is ready
|
||||
app.enableSandbox();
|
||||
|
||||
if (isHeadless) {
|
||||
app.disableHardwareAcceleration();
|
||||
}
|
||||
|
||||
async function createWindow() {
|
||||
// Handle different preload paths for dev and prod
|
||||
const preloadPath = isDevelopment
|
||||
? path.join(app.getAppPath(), '.vite/build/preload/preload.js')
|
||||
: path.join(__dirname, 'preload.js');
|
||||
|
||||
// Create the browser window with headless options when needed
|
||||
const mainWindow = new BrowserWindow({
|
||||
width: 1200,
|
||||
height: 800,
|
||||
...(isHeadless
|
||||
? {
|
||||
show: false,
|
||||
webPreferences: {
|
||||
nodeIntegration: false,
|
||||
contextIsolation: true,
|
||||
webSecurity: true,
|
||||
preload: preloadPath,
|
||||
sandbox: true,
|
||||
offscreen: true,
|
||||
},
|
||||
}
|
||||
: {
|
||||
webPreferences: {
|
||||
nodeIntegration: false,
|
||||
contextIsolation: true,
|
||||
webSecurity: true,
|
||||
preload: preloadPath,
|
||||
sandbox: true,
|
||||
},
|
||||
}),
|
||||
});
|
||||
|
||||
// Set up CSP
|
||||
session.defaultSession.webRequest.onHeadersReceived(
|
||||
(
|
||||
details: OnHeadersReceivedListenerDetails,
|
||||
callback: (response: HeadersReceivedResponse) => void
|
||||
) => {
|
||||
callback({
|
||||
responseHeaders: {
|
||||
...details.responseHeaders,
|
||||
'Content-Security-Policy': [
|
||||
isDevelopment
|
||||
? `
|
||||
default-src 'self';
|
||||
script-src 'self' 'unsafe-inline';
|
||||
style-src 'self' 'unsafe-inline';
|
||||
connect-src 'self' ws://localhost:3001 http://localhost:3001;
|
||||
img-src 'self' data: https:;
|
||||
font-src 'self' data:;
|
||||
`
|
||||
.replace(/\s+/g, ' ')
|
||||
.trim()
|
||||
: `
|
||||
default-src 'self';
|
||||
script-src 'self';
|
||||
style-src 'self' 'unsafe-inline';
|
||||
img-src 'self' data: https:;
|
||||
font-src 'self' data:;
|
||||
`
|
||||
.replace(/\s+/g, ' ')
|
||||
.trim(),
|
||||
],
|
||||
},
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
// Set up IPC handlers
|
||||
ipcMain.handle('clipboard-copy', async (_: IpcMainInvokeEvent, text: string) => {
|
||||
clipboard.writeText(text);
|
||||
});
|
||||
|
||||
ipcMain.handle('clipboard-read', async () => {
|
||||
return clipboard.readText();
|
||||
});
|
||||
|
||||
interface SaveFileParams {
|
||||
content: string;
|
||||
fileName: string;
|
||||
}
|
||||
|
||||
interface SaveFileResult {
|
||||
success: boolean;
|
||||
path?: string;
|
||||
}
|
||||
|
||||
ipcMain.handle(
|
||||
'save-file',
|
||||
async (
|
||||
_: IpcMainInvokeEvent,
|
||||
{ content, fileName }: SaveFileParams
|
||||
): Promise<SaveFileResult> => {
|
||||
const { filePath } = await dialog.showSaveDialog({
|
||||
defaultPath: fileName,
|
||||
});
|
||||
|
||||
if (filePath) {
|
||||
await fs.writeFile(filePath, content, 'utf8');
|
||||
return { success: true, path: filePath };
|
||||
}
|
||||
return { success: false };
|
||||
}
|
||||
);
|
||||
|
||||
// Load the app
|
||||
if (isDevelopment) {
|
||||
mainWindow.loadURL('http://localhost:3001/');
|
||||
if (!isHeadless) {
|
||||
mainWindow.webContents.openDevTools();
|
||||
}
|
||||
} else {
|
||||
// In production, load from the asar archive
|
||||
mainWindow.loadFile(path.join(__dirname, 'renderer/index.html'));
|
||||
}
|
||||
}
|
||||
|
||||
app.whenReady().then(() => {
|
||||
createWindow().catch(console.error);
|
||||
|
||||
app.on('activate', function () {
|
||||
if (BrowserWindow.getAllWindows().length === 0) {
|
||||
createWindow().catch(console.error);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
app.on('window-all-closed', () => {
|
||||
if (process.platform !== 'darwin') {
|
||||
app.quit();
|
||||
}
|
||||
});
|
||||
@@ -1,12 +0,0 @@
|
||||
import { contextBridge, ipcRenderer } from 'electron';
|
||||
|
||||
// Define the API interface
|
||||
interface ElectronAPI {
|
||||
copyToClipboard(text: string): Promise<void>;
|
||||
}
|
||||
|
||||
// Expose protected methods that allow the renderer process to use
|
||||
// the ipcRenderer without exposing the entire object
|
||||
contextBridge.exposeInMainWorld('electronAPI', {
|
||||
copyToClipboard: (text: string) => ipcRenderer.invoke('clipboard-copy', text),
|
||||
} as ElectronAPI);
|
||||
@@ -1,124 +0,0 @@
|
||||
const eslint = require('@eslint/js');
|
||||
const typescript = require('@typescript-eslint/eslint-plugin');
|
||||
const typescriptParser = require('@typescript-eslint/parser');
|
||||
const importPlugin = require('eslint-plugin-import');
|
||||
const prettier = require('eslint-plugin-prettier');
|
||||
const react = require('eslint-plugin-react');
|
||||
const reactHooks = require('eslint-plugin-react-hooks');
|
||||
|
||||
module.exports = [
|
||||
{
|
||||
ignores: ['**/node_modules/**', '**/dist/**', '**/out/**', '**/coverage/**', '**/.vite/**'],
|
||||
},
|
||||
// Configuration for Node.js files
|
||||
{
|
||||
files: ['**/*.{js,cjs,ts,mts}'],
|
||||
languageOptions: {
|
||||
globals: {
|
||||
module: 'readonly',
|
||||
require: 'readonly',
|
||||
process: 'readonly',
|
||||
__dirname: 'readonly',
|
||||
__filename: 'readonly',
|
||||
},
|
||||
},
|
||||
},
|
||||
// Base configuration for all files
|
||||
{
|
||||
files: ['**/*.{js,jsx,ts,tsx}'],
|
||||
plugins: {
|
||||
'@typescript-eslint': typescript,
|
||||
react,
|
||||
'react-hooks': reactHooks,
|
||||
import: importPlugin,
|
||||
prettier,
|
||||
},
|
||||
languageOptions: {
|
||||
parser: typescriptParser,
|
||||
parserOptions: {
|
||||
ecmaVersion: 'latest',
|
||||
sourceType: 'module',
|
||||
ecmaFeatures: {
|
||||
jsx: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
settings: {
|
||||
react: {
|
||||
version: 'detect',
|
||||
},
|
||||
},
|
||||
rules: {
|
||||
...eslint.configs.recommended.rules,
|
||||
...typescript.configs.recommended.rules,
|
||||
...react.configs.recommended.rules,
|
||||
...reactHooks.configs.recommended.rules,
|
||||
'prettier/prettier': 'error',
|
||||
'react/react-in-jsx-scope': 'off',
|
||||
'@typescript-eslint/explicit-module-boundary-types': 'error',
|
||||
'@typescript-eslint/no-explicit-any': 'error',
|
||||
'@typescript-eslint/no-unused-vars': ['error', { argsIgnorePattern: '^_' }],
|
||||
'import/order': [
|
||||
'error',
|
||||
{
|
||||
groups: ['builtin', 'external', 'internal', ['parent', 'sibling']],
|
||||
pathGroups: [
|
||||
{
|
||||
pattern: 'react',
|
||||
group: 'external',
|
||||
position: 'before',
|
||||
},
|
||||
],
|
||||
pathGroupsExcludedImportTypes: ['react'],
|
||||
'newlines-between': 'always',
|
||||
alphabetize: {
|
||||
order: 'asc',
|
||||
caseInsensitive: true,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
// Browser-specific configuration
|
||||
{
|
||||
files: ['src/**/*.{ts,tsx}'],
|
||||
languageOptions: {
|
||||
globals: {
|
||||
window: 'readonly',
|
||||
document: 'readonly',
|
||||
navigator: 'readonly',
|
||||
console: 'readonly',
|
||||
setTimeout: 'readonly',
|
||||
Blob: 'readonly',
|
||||
HTMLInputElement: 'readonly',
|
||||
SVGSVGElement: 'readonly',
|
||||
},
|
||||
},
|
||||
},
|
||||
// Electron main process configuration
|
||||
{
|
||||
files: ['electron/**/*.ts'],
|
||||
languageOptions: {
|
||||
globals: {
|
||||
__dirname: 'readonly',
|
||||
process: 'readonly',
|
||||
console: 'readonly',
|
||||
},
|
||||
},
|
||||
},
|
||||
// Test configuration
|
||||
{
|
||||
files: ['**/*.test.{ts,tsx}', 'src/test/**/*.{ts,tsx}'],
|
||||
languageOptions: {
|
||||
globals: {
|
||||
console: 'readonly',
|
||||
jest: 'readonly',
|
||||
},
|
||||
},
|
||||
rules: {
|
||||
'@typescript-eslint/no-namespace': 'off',
|
||||
'@typescript-eslint/no-empty-interface': 'off',
|
||||
'@typescript-eslint/no-explicit-any': 'off',
|
||||
},
|
||||
},
|
||||
];
|
||||
@@ -1,50 +0,0 @@
|
||||
import { VitePlugin } from '@electron-forge/plugin-vite';
|
||||
import type { ForgeConfig } from '@electron-forge/shared-types';
|
||||
|
||||
const config: ForgeConfig = {
|
||||
packagerConfig: {
|
||||
asar: true,
|
||||
},
|
||||
rebuildConfig: {},
|
||||
makers: [
|
||||
{
|
||||
name: '@electron-forge/maker-squirrel',
|
||||
config: {},
|
||||
},
|
||||
{
|
||||
name: '@electron-forge/maker-zip',
|
||||
config: {},
|
||||
platforms: ['darwin'],
|
||||
},
|
||||
{
|
||||
name: '@electron-forge/maker-deb',
|
||||
config: {},
|
||||
},
|
||||
{
|
||||
name: '@electron-forge/maker-rpm',
|
||||
config: {},
|
||||
},
|
||||
],
|
||||
plugins: [
|
||||
new VitePlugin({
|
||||
build: [
|
||||
{
|
||||
entry: 'electron/main.ts',
|
||||
config: 'vite.main.config.ts',
|
||||
},
|
||||
{
|
||||
entry: 'electron/preload.ts',
|
||||
config: 'vite.preload.config.ts',
|
||||
},
|
||||
],
|
||||
renderer: [
|
||||
{
|
||||
name: 'main_window',
|
||||
config: 'vite.renderer.config.ts',
|
||||
},
|
||||
],
|
||||
}),
|
||||
],
|
||||
};
|
||||
|
||||
export default config;
|
||||
@@ -1,17 +0,0 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<meta
|
||||
http-equiv="Content-Security-Policy"
|
||||
content="default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline';"
|
||||
/>
|
||||
<link href="/src/index.css" rel="stylesheet" />
|
||||
<title>Goose v2</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
<script type="module" src="/src/web.tsx"></script>
|
||||
</body>
|
||||
</html>
|
||||
14960
ui-v2/package-lock.json
generated
14960
ui-v2/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -1,102 +0,0 @@
|
||||
{
|
||||
"name": "goose-v2",
|
||||
"private": true,
|
||||
"version": "1.0.0",
|
||||
"description": "Goose v2",
|
||||
"main": ".vite/build/main.js",
|
||||
"scripts": {
|
||||
"start": "concurrently -n web,electron -c blue,green \"npm run start:web\" \"npm run start:electron\"",
|
||||
"start:electron": "electron-forge start",
|
||||
"start:web": "vite",
|
||||
"build:web": "tsc --project tsconfig.json && vite build",
|
||||
"build:electron": "electron-forge make",
|
||||
"preview:web": "vite preview",
|
||||
"package": "electron-forge package",
|
||||
"make": "electron-forge make",
|
||||
"test": "vitest",
|
||||
"test:ui": "vitest --ui",
|
||||
"test:coverage": "vitest run --coverage",
|
||||
"test:e2e": "playwright test",
|
||||
"test:e2e:ui": "playwright test --ui --project=web --project=electron --workers=1",
|
||||
"test:e2e:web": "playwright test --project=web --headed",
|
||||
"test:e2e:web:headless": "playwright test --project=web",
|
||||
"test:e2e:web:ui": "playwright test --ui --project=web",
|
||||
"test:e2e:electron": "playwright test --project=electron --headed",
|
||||
"test:e2e:electron:headless": "HEADLESS=true playwright test --project=electron",
|
||||
"test:e2e:electron:ui": "playwright test --ui --project=electron",
|
||||
"tsc": "tsc --noEmit",
|
||||
"tsc:web": "tsc --project tsconfig.json --noEmit",
|
||||
"tsc:electron": "tsc --project tsconfig.electron.json --noEmit",
|
||||
"typecheck": "npm run tsc:web && npm run tsc:electron",
|
||||
"lint": "eslint . && npm run lint:style",
|
||||
"lint:fix": "eslint . --fix && npm run lint:style:fix",
|
||||
"lint:style": "stylelint src/**/*.css",
|
||||
"lint:style:fix": "stylelint src/**/*.css --fix",
|
||||
"prettier": "prettier --check \"src/**/*.{ts,tsx,js,jsx,css}\" \"electron/**/*.{ts,tsx,js,jsx,css}\"",
|
||||
"prettier:fix": "prettier --write \"src/**/*.{ts,tsx,js,jsx,css}\" \"electron/**/*.{ts,tsx,js,jsx,css}\"",
|
||||
"format": "npm run prettier:fix && npm run lint:style:fix",
|
||||
"check-all": "npm run typecheck && npm run lint && npm run prettier",
|
||||
"prepare": "cd ../.. && husky install"
|
||||
},
|
||||
"dependencies": {
|
||||
"react": "^19.1.0",
|
||||
"react-dom": "^19.1.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@electron-forge/cli": "^7.8.0",
|
||||
"@electron-forge/maker-deb": "^7.8.0",
|
||||
"@electron-forge/maker-rpm": "^7.8.0",
|
||||
"@electron-forge/maker-squirrel": "^7.8.0",
|
||||
"@electron-forge/maker-zip": "^7.8.0",
|
||||
"@electron-forge/plugin-vite": "^7.8.0",
|
||||
"@electron-forge/shared-types": "^7.8.0",
|
||||
"@playwright/test": "^1.42.1",
|
||||
"@tailwindcss/postcss": "^4.1.4",
|
||||
"@testing-library/jest-dom": "^6.6.3",
|
||||
"@testing-library/react": "^16.3.0",
|
||||
"@testing-library/user-event": "^14.6.1",
|
||||
"@types/eslint": "^9.6.1",
|
||||
"@types/react": "^19.1.2",
|
||||
"@types/react-dom": "^19.1.2",
|
||||
"@typescript-eslint/eslint-plugin": "^8.31.0",
|
||||
"@typescript-eslint/parser": "^8.31.0",
|
||||
"@vitejs/plugin-react": "^4.4.1",
|
||||
"@vitest/coverage-v8": "^3.1.2",
|
||||
"@vitest/ui": "^3.1.2",
|
||||
"autoprefixer": "^10.4.21",
|
||||
"concurrently": "^9.1.2",
|
||||
"electron": "^35.2.1",
|
||||
"eslint": "^9.25.1",
|
||||
"eslint-config-prettier": "^10.1.2",
|
||||
"eslint-plugin-import": "^2.31.0",
|
||||
"eslint-plugin-prettier": "^5.2.6",
|
||||
"eslint-plugin-react": "^7.37.5",
|
||||
"eslint-plugin-react-hooks": "^5.2.0",
|
||||
"husky": "^9.1.7",
|
||||
"jsdom": "^26.1.0",
|
||||
"lint-staged": "^15.5.1",
|
||||
"postcss": "^8.5.3",
|
||||
"prettier": "^3.5.3",
|
||||
"stylelint": "^16.19.1",
|
||||
"stylelint-config-standard": "^38.0.0",
|
||||
"tailwindcss": "^4.1.4",
|
||||
"ts-node": "^10.9.2",
|
||||
"typescript": "^5.8.3",
|
||||
"vite": "^6.3.3",
|
||||
"vitest": "^3.1.2"
|
||||
},
|
||||
"lint-staged": {
|
||||
"src/**/*.{ts,tsx}": [
|
||||
"eslint --fix --max-warnings 0 --no-warn-ignored",
|
||||
"prettier --write",
|
||||
"bash -c 'tsc --pretty --noEmit --project tsconfig.json'"
|
||||
],
|
||||
"src/**/*.{css,json}": [
|
||||
"prettier --write",
|
||||
"stylelint --fix"
|
||||
]
|
||||
},
|
||||
"config": {
|
||||
"forge": "./forge.config.ts"
|
||||
}
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
import { defineConfig, devices } from '@playwright/test';
|
||||
|
||||
export default defineConfig({
|
||||
testDir: './src/test/e2e',
|
||||
workers: 1,
|
||||
use: {
|
||||
trace: 'on-first-retry',
|
||||
// Use headless mode in CI, non-headless locally unless specified
|
||||
headless: process.env.CI === 'true' || process.env.HEADLESS === 'true',
|
||||
// Add longer timeouts for CI
|
||||
navigationTimeout: 30000,
|
||||
actionTimeout: 15000,
|
||||
},
|
||||
projects: [
|
||||
{
|
||||
name: 'web',
|
||||
testMatch: ['**/web/*.spec.ts'],
|
||||
use: {
|
||||
...devices['Desktop Chrome'],
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'electron',
|
||||
testMatch: ['**/electron/*.spec.ts'],
|
||||
use: {
|
||||
...devices['Desktop Chrome'],
|
||||
},
|
||||
},
|
||||
],
|
||||
timeout: 60000, // Increase overall timeout
|
||||
expect: {
|
||||
timeout: 15000, // Increase expect timeout
|
||||
},
|
||||
reporter: [['html'], ['list']],
|
||||
});
|
||||
@@ -1,9 +0,0 @@
|
||||
/* eslint-env node */
|
||||
|
||||
/** @type {import('postcss').Config} */
|
||||
module.exports = {
|
||||
plugins: {
|
||||
'@tailwindcss/postcss': {},
|
||||
autoprefixer: {},
|
||||
},
|
||||
};
|
||||
@@ -1,39 +0,0 @@
|
||||
import React, { useEffect, Suspense } from 'react';
|
||||
|
||||
import { platformService } from '@platform';
|
||||
|
||||
import GooseLogo from './components/GooseLogo';
|
||||
import SuspenseLoader from './components/SuspenseLoader';
|
||||
import { ElectronPlatformService } from './services/platform/electron/PlatformService';
|
||||
|
||||
const App: React.FC = (): React.ReactElement => {
|
||||
useEffect(() => {
|
||||
console.log(
|
||||
'Platform service is Electron:',
|
||||
platformService instanceof ElectronPlatformService
|
||||
);
|
||||
}, []);
|
||||
|
||||
const isElectron = platformService instanceof ElectronPlatformService;
|
||||
|
||||
return (
|
||||
<Suspense fallback={<SuspenseLoader />}>
|
||||
<div className="p-5 max-w-3xl mx-auto">
|
||||
<div
|
||||
className={`absolute top-2.5 right-2.5 px-2 py-1 text-white rounded text-xs ${
|
||||
isElectron ? 'bg-green-500' : 'bg-blue-500'
|
||||
}`}
|
||||
>
|
||||
Running in: {isElectron ? 'Electron' : 'Web Browser'}
|
||||
</div>
|
||||
|
||||
<div className="flex items-center gap-4">
|
||||
<GooseLogo />
|
||||
<h1 className="text-2xl font-bold text-textProminent">Goose v2</h1>
|
||||
</div>
|
||||
</div>
|
||||
</Suspense>
|
||||
);
|
||||
};
|
||||
|
||||
export default App;
|
||||
@@ -1,29 +0,0 @@
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
import { describe, it, expect, vi } from 'vitest';
|
||||
|
||||
import { Button } from './Button';
|
||||
|
||||
describe('Button', () => {
|
||||
it('renders with children', () => {
|
||||
render(<Button>Click me</Button>);
|
||||
expect(screen.getByText('Click me')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('calls onClick handler when clicked', async () => {
|
||||
const handleClick = vi.fn();
|
||||
render(<Button onClick={handleClick}>Click me</Button>);
|
||||
|
||||
await userEvent.click(screen.getByText('Click me'));
|
||||
expect(handleClick).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('applies variant styles correctly', () => {
|
||||
render(<Button variant="secondary">Secondary Button</Button>);
|
||||
const button = screen.getByText('Secondary Button');
|
||||
|
||||
expect(button).toHaveStyle({
|
||||
backgroundColor: '#6c757d',
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,68 +0,0 @@
|
||||
import React from 'react';
|
||||
|
||||
import { platformService } from '@platform';
|
||||
|
||||
interface ButtonProps {
|
||||
onClick?: () => void;
|
||||
children: React.ReactNode;
|
||||
copyText?: string;
|
||||
variant?: 'primary' | 'secondary';
|
||||
className?: string;
|
||||
}
|
||||
|
||||
export const Button: React.FC<ButtonProps> = ({
|
||||
onClick,
|
||||
children,
|
||||
copyText,
|
||||
variant = 'primary',
|
||||
className = '',
|
||||
}) => {
|
||||
const handleClick = async () => {
|
||||
if (copyText) {
|
||||
try {
|
||||
console.log('Attempting to copy text:', copyText);
|
||||
await platformService.copyToClipboard(copyText);
|
||||
console.log('Text copied successfully');
|
||||
} catch (error) {
|
||||
console.error('Failed to copy:', error);
|
||||
}
|
||||
}
|
||||
|
||||
if (onClick) {
|
||||
onClick();
|
||||
}
|
||||
};
|
||||
|
||||
const getVariantStyles = () => {
|
||||
switch (variant) {
|
||||
case 'secondary':
|
||||
return {
|
||||
backgroundColor: '#6c757d',
|
||||
color: 'white',
|
||||
};
|
||||
case 'primary':
|
||||
default:
|
||||
return {
|
||||
backgroundColor: '#4CAF50',
|
||||
color: 'white',
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<button
|
||||
className={`app-button ${className}`}
|
||||
onClick={handleClick}
|
||||
style={{
|
||||
padding: '10px 20px',
|
||||
border: 'none',
|
||||
borderRadius: '4px',
|
||||
cursor: 'pointer',
|
||||
transition: 'background-color 0.2s',
|
||||
...getVariantStyles(),
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</button>
|
||||
);
|
||||
};
|
||||
@@ -1,36 +0,0 @@
|
||||
import { FC } from 'react';
|
||||
|
||||
import { Goose, Rain } from './icons/Goose';
|
||||
|
||||
interface GooseLogoProps {
|
||||
className?: string;
|
||||
size?: 'default' | 'small';
|
||||
hover?: boolean;
|
||||
}
|
||||
|
||||
const GooseLogo: FC<GooseLogoProps> = ({ className = '', size = 'default', hover = true }) => {
|
||||
const sizes = {
|
||||
default: {
|
||||
frame: 'w-16 h-16',
|
||||
rain: 'w-[275px] h-[275px]',
|
||||
goose: 'w-16 h-16',
|
||||
},
|
||||
small: {
|
||||
frame: 'w-8 h-8',
|
||||
rain: 'w-[150px] h-[150px]',
|
||||
goose: 'w-8 h-8',
|
||||
},
|
||||
};
|
||||
return (
|
||||
<div
|
||||
className={`${className} ${sizes[size].frame} ${hover ? 'group/with-hover' : ''} relative overflow-hidden`}
|
||||
>
|
||||
<Rain
|
||||
className={`${sizes[size].rain} absolute left-0 bottom-0 ${hover ? 'opacity-0 group-hover/with-hover:opacity-100' : ''} transition-all duration-300 z-1`}
|
||||
/>
|
||||
<Goose className={`${sizes[size].goose} absolute left-0 bottom-0 z-2`} />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default GooseLogo;
|
||||
@@ -1,7 +0,0 @@
|
||||
import React from 'react';
|
||||
|
||||
const SuspenseLoader: React.FC = (): React.ReactElement => {
|
||||
return <div>Loading...</div>;
|
||||
};
|
||||
|
||||
export default SuspenseLoader;
|
||||
@@ -1,392 +0,0 @@
|
||||
import { FC } from 'react';
|
||||
|
||||
interface IconProps {
|
||||
className?: string;
|
||||
}
|
||||
|
||||
export const Goose: FC<IconProps> = ({ className = '' }) => {
|
||||
return (
|
||||
<svg
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
className={className}
|
||||
>
|
||||
<g clipPath="url(#clip0_2096_5193)">
|
||||
<path
|
||||
d="M20.9093 19.3861L19.5185 18.2413C18.7624 17.619 18.1189 16.8713 17.6157 16.0313C16.9205 14.8706 15.9599 13.8912 14.8133 13.1735L14.2533 12.8475C14.0614 12.7141 13.9276 12.5062 13.9086 12.2716C13.8963 12.1204 13.9326 11.9852 14.0171 11.8662C14.3087 11.4553 15.896 9.74698 16.1722 9.51845C16.528 9.22442 16.9243 8.97987 17.2921 8.69986C17.3443 8.66 17.3968 8.62035 17.4485 8.57989C17.4503 8.57808 17.4529 8.57668 17.4545 8.57508C17.5725 8.48195 17.6838 8.383 17.7724 8.26563C18.2036 7.76631 18.195 7.3443 18.195 7.3443C18.195 7.3443 18.1954 7.3439 18.1956 7.3437C18.1497 7.23133 17.9847 6.88163 17.6492 6.71759C17.9458 6.71178 18.2805 6.82294 18.4323 6.97156C18.6148 6.68534 18.7328 6.49967 18.9162 6.18762C18.9599 6.11352 18.9831 5.97652 18.8996 5.89981C18.8996 5.89981 18.8992 5.89981 18.8988 5.89981C18.8988 5.89981 18.8988 5.8994 18.8988 5.899C18.8972 5.8974 18.8952 5.8962 18.8936 5.8946C18.892 5.893 18.891 5.89119 18.8892 5.88939C18.8892 5.88939 18.8888 5.88939 18.8884 5.88939C18.8884 5.88939 18.8884 5.88899 18.8884 5.88859C18.885 5.88518 18.8812 5.88258 18.8776 5.87938C18.8754 5.87717 18.8736 5.87457 18.8716 5.87217C18.8692 5.87016 18.8665 5.86836 18.8643 5.86616C18.8609 5.86275 18.8587 5.85855 18.8551 5.85534C18.8551 5.85534 18.8545 5.85514 18.8543 5.85534C18.8543 5.85534 18.8543 5.85494 18.8543 5.85454C18.8527 5.85294 18.8507 5.85174 18.8491 5.85013C18.8475 5.84853 18.8463 5.84653 18.8447 5.84493C18.8447 5.84493 18.8441 5.84473 18.8439 5.84493C18.8439 5.84493 18.8439 5.84453 18.8439 5.84413C18.7672 5.7606 18.6302 5.78384 18.5561 5.8275C18.1503 6.06625 17.7555 6.32322 17.3996 6.54855C17.3996 6.54855 16.9778 6.53973 16.4783 6.97116C16.3607 7.05989 16.2618 7.17125 16.1688 7.28902C16.167 7.29082 16.1654 7.29322 16.164 7.29503C16.1234 7.3465 16.0837 7.39898 16.0441 7.45145C15.7639 7.81939 15.5195 8.21556 15.2255 8.57128C14.9971 8.84768 13.2887 10.4348 12.8777 10.7264C12.7587 10.8109 12.6237 10.8474 12.4723 10.835C12.2379 10.8161 12.0298 10.6821 11.8965 10.4903L11.5704 9.93024C10.8527 8.78318 9.87332 7.82299 8.71264 7.12778C7.87262 6.62466 7.12514 5.98092 6.50264 5.22503L5.35778 3.83421C5.3013 3.76571 5.19314 3.77693 5.15268 3.85585C5.02249 4.10941 4.77393 4.64479 4.58346 5.36483C4.57885 5.38186 4.58286 5.39988 4.59407 5.4135C4.83082 5.69952 5.37901 6.32983 6.03196 6.863C6.07742 6.90005 6.04017 6.97336 5.98369 6.95774C5.42047 6.80432 4.87288 6.55796 4.46308 6.34805C4.42964 6.33103 4.38918 6.35226 4.38437 6.38951C4.32068 6.89985 4.30425 7.46027 4.37155 8.05112C4.37355 8.07035 4.38577 8.08697 4.4036 8.09479C4.87088 8.29808 5.61816 8.59311 6.40269 8.78078C6.45958 8.7944 6.45777 8.87632 6.40029 8.88733C5.78941 9.0023 5.14968 9.02794 4.62973 9.02113C4.59327 9.02073 4.56643 9.05518 4.57625 9.09023C4.6806 9.45896 4.822 9.8339 5.00847 10.2115C5.08559 10.3811 5.16951 10.5475 5.25944 10.7104C5.27486 10.7382 5.3047 10.7548 5.33655 10.7534C5.76577 10.7324 6.28452 10.6871 6.80608 10.595C6.89501 10.5794 6.94268 10.6964 6.86757 10.7466C6.51345 10.9834 6.13571 11.1873 5.7844 11.3551C5.73733 11.3777 5.72211 11.4378 5.75315 11.4797C5.96186 11.7625 6.19139 12.0301 6.44075 12.2794C6.44075 12.2794 7.66853 13.5441 7.70198 13.6432C8.41841 12.9096 9.59612 12.0964 10.8966 11.3864C9.15488 12.8036 8.18387 13.8499 7.69517 14.4444L7.35447 14.9225C7.17742 15.1708 7.02379 15.4346 6.89541 15.7112C6.46579 16.6356 5.75756 18.5051 5.75756 18.5051C5.70328 18.6515 5.74754 18.7959 5.84168 18.89C5.84388 18.8922 5.84609 18.8944 5.84849 18.8964C5.85069 18.8986 5.8527 18.901 5.8549 18.9032C5.94924 18.9976 6.09345 19.0416 6.23986 18.9874C6.23986 18.9874 8.10897 18.2791 9.03371 17.8495C9.31031 17.7211 9.57429 17.5673 9.82245 17.3905L10.349 17.0153C10.6278 16.8166 11.0096 16.8483 11.2517 17.0904L12.4655 18.3042C12.7148 18.5535 12.9824 18.7831 13.2652 18.9918C13.3073 19.0226 13.3672 19.0076 13.3898 18.9605C13.5579 18.6094 13.7618 18.2313 13.9983 17.8774C14.0486 17.8022 14.1657 17.8501 14.1499 17.9388C14.0576 18.4606 14.0127 18.9794 13.9915 19.4084C13.9899 19.44 14.0067 19.4701 14.0345 19.4855C14.1972 19.5756 14.3636 19.6595 14.5335 19.7364C14.911 19.9229 15.2862 20.0645 15.6547 20.1687C15.6897 20.1785 15.7242 20.1516 15.7238 20.1152C15.7168 19.595 15.7424 18.9553 15.8576 18.3446C15.8684 18.2869 15.9503 18.2851 15.9641 18.3422C16.1516 19.127 16.4466 19.8742 16.6501 20.3413C16.6579 20.3591 16.6744 20.3712 16.6938 20.3734C17.2847 20.4407 17.8451 20.4242 18.3554 20.3606C18.3929 20.3559 18.4141 20.3155 18.3969 20.2818C18.187 19.872 17.9406 19.3241 17.7872 18.7612C17.7718 18.7046 17.8449 18.6675 17.8819 18.713C18.4151 19.3659 19.0454 19.9141 19.3314 20.1508C19.345 20.1621 19.3633 20.1659 19.3801 20.1615C20.1003 19.9712 20.6357 19.7226 20.8891 19.5922C20.968 19.5518 20.9792 19.4436 20.9107 19.3871L20.9093 19.3861Z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_2096_5193">
|
||||
<rect width="24" height="24" fill="white" />
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
);
|
||||
};
|
||||
|
||||
export const Rain: FC<IconProps> = ({ className = '' }) => {
|
||||
return (
|
||||
<svg
|
||||
width="103"
|
||||
height="103"
|
||||
viewBox="0 0 103 103"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
className={className}
|
||||
>
|
||||
<g id="wind">
|
||||
<g id="1" className="animate-[wind_2s_linear_infinite]">
|
||||
<path
|
||||
id="Vector 42_2"
|
||||
d="M66 33L62 37"
|
||||
stroke="url(#paint10_linear_2096_5587)"
|
||||
strokeLinecap="round"
|
||||
/>
|
||||
<path
|
||||
id="Vector 41_2"
|
||||
d="M70 43L68 45"
|
||||
stroke="url(#paint11_linear_2096_5587)"
|
||||
strokeLinecap="round"
|
||||
/>
|
||||
<path
|
||||
id="Vector 51_2"
|
||||
d="M60 34L59 35"
|
||||
stroke="url(#paint12_linear_2096_5587)"
|
||||
strokeLinecap="round"
|
||||
/>
|
||||
<path
|
||||
id="Vector 43_2"
|
||||
d="M56 47L52 51"
|
||||
stroke="url(#paint13_linear_2096_5587)"
|
||||
strokeLinecap="round"
|
||||
/>
|
||||
<path
|
||||
id="Vector 46_2"
|
||||
d="M98 1L94 5"
|
||||
stroke="url(#paint14_linear_2096_5587)"
|
||||
strokeLinecap="round"
|
||||
/>
|
||||
<path
|
||||
id="Vector 47_2"
|
||||
d="M102 11L100 13"
|
||||
stroke="url(#paint15_linear_2096_5587)"
|
||||
strokeLinecap="round"
|
||||
/>
|
||||
<path
|
||||
id="Vector 48_2"
|
||||
d="M88 15L84 19"
|
||||
stroke="url(#paint16_linear_2096_5587)"
|
||||
strokeLinecap="round"
|
||||
/>
|
||||
<path
|
||||
id="Vector 45_2"
|
||||
d="M76 26L72 30"
|
||||
stroke="url(#paint17_linear_2096_5587)"
|
||||
strokeLinecap="round"
|
||||
/>
|
||||
<path
|
||||
id="Vector 49_2"
|
||||
d="M87 28L83 32"
|
||||
stroke="url(#paint18_linear_2096_5587)"
|
||||
strokeLinecap="round"
|
||||
/>
|
||||
<path
|
||||
id="Vector 50_2"
|
||||
d="M76 34L74 36"
|
||||
stroke="url(#paint19_linear_2096_5587)"
|
||||
strokeLinecap="round"
|
||||
/>
|
||||
</g>
|
||||
<g id="2" className="animate-[wind_2s_linear_1s_infinite]">
|
||||
<path
|
||||
id="Vector 42"
|
||||
d="M66 33L62 37"
|
||||
stroke="url(#paint0_linear_2096_5587)"
|
||||
strokeLinecap="round"
|
||||
/>
|
||||
<path
|
||||
id="Vector 41"
|
||||
d="M70 43L68 45"
|
||||
stroke="url(#paint1_linear_2096_5587)"
|
||||
strokeLinecap="round"
|
||||
/>
|
||||
<path
|
||||
id="Vector 51"
|
||||
d="M60 34L59 35"
|
||||
stroke="url(#paint2_linear_2096_5587)"
|
||||
strokeLinecap="round"
|
||||
/>
|
||||
<path
|
||||
id="Vector 43"
|
||||
d="M56 47L52 51"
|
||||
stroke="url(#paint3_linear_2096_5587)"
|
||||
strokeLinecap="round"
|
||||
/>
|
||||
<path
|
||||
id="Vector 46"
|
||||
d="M98 1L94 5"
|
||||
stroke="url(#paint4_linear_2096_5587)"
|
||||
strokeLinecap="round"
|
||||
/>
|
||||
<path
|
||||
id="Vector 47"
|
||||
d="M102 11L100 13"
|
||||
stroke="url(#paint5_linear_2096_5587)"
|
||||
strokeLinecap="round"
|
||||
/>
|
||||
<path
|
||||
id="Vector 48"
|
||||
d="M88 15L84 19"
|
||||
stroke="url(#paint6_linear_2096_5587)"
|
||||
strokeLinecap="round"
|
||||
/>
|
||||
<path
|
||||
id="Vector 45"
|
||||
d="M76 26L72 30"
|
||||
stroke="url(#paint7_linear_2096_5587)"
|
||||
strokeLinecap="round"
|
||||
/>
|
||||
<path
|
||||
id="Vector 49"
|
||||
d="M87 28L83 32"
|
||||
stroke="url(#paint8_linear_2096_5587)"
|
||||
strokeLinecap="round"
|
||||
/>
|
||||
<path
|
||||
id="Vector 50"
|
||||
d="M76 34L74 36"
|
||||
stroke="url(#paint9_linear_2096_5587)"
|
||||
strokeLinecap="round"
|
||||
/>
|
||||
</g>
|
||||
</g>
|
||||
<defs>
|
||||
<linearGradient
|
||||
id="paint0_linear_2096_5587"
|
||||
x1="64"
|
||||
y1="32.9437"
|
||||
x2="64"
|
||||
y2="37.0563"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
>
|
||||
<stop stopColor="#EC5D2A" />
|
||||
<stop offset="1" stopColor="#57B9AF" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="paint1_linear_2096_5587"
|
||||
x1="69"
|
||||
y1="42.9719"
|
||||
x2="69"
|
||||
y2="45.0281"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
>
|
||||
<stop stopColor="#EC5D2A" />
|
||||
<stop offset="1" stopColor="#57B9AF" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="paint2_linear_2096_5587"
|
||||
x1="59.5"
|
||||
y1="33.9859"
|
||||
x2="59.5"
|
||||
y2="35.0141"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
>
|
||||
<stop stopColor="#EC5D2A" />
|
||||
<stop offset="1" stopColor="#57B9AF" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="paint3_linear_2096_5587"
|
||||
x1="54"
|
||||
y1="46.9437"
|
||||
x2="54"
|
||||
y2="51.0563"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
>
|
||||
<stop stopColor="#EC5D2A" />
|
||||
<stop offset="1" stopColor="#57B9AF" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="paint4_linear_2096_5587"
|
||||
x1="96"
|
||||
y1="0.943728"
|
||||
x2="96"
|
||||
y2="5.05625"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
>
|
||||
<stop stopColor="#EC5D2A" />
|
||||
<stop offset="1" stopColor="#57B9AF" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="paint5_linear_2096_5587"
|
||||
x1="101"
|
||||
y1="10.9719"
|
||||
x2="101"
|
||||
y2="13.0281"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
>
|
||||
<stop stopColor="#EC5D2A" />
|
||||
<stop offset="1" stopColor="#57B9AF" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="paint6_linear_2096_5587"
|
||||
x1="86"
|
||||
y1="14.9437"
|
||||
x2="86"
|
||||
y2="19.0563"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
>
|
||||
<stop stopColor="#EC5D2A" />
|
||||
<stop offset="1" stopColor="#57B9AF" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="paint7_linear_2096_5587"
|
||||
x1="74"
|
||||
y1="25.9437"
|
||||
x2="74"
|
||||
y2="30.0563"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
>
|
||||
<stop stopColor="#EC5D2A" />
|
||||
<stop offset="1" stopColor="#57B9AF" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="paint8_linear_2096_5587"
|
||||
x1="85"
|
||||
y1="27.9437"
|
||||
x2="85"
|
||||
y2="32.0563"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
>
|
||||
<stop stopColor="#EC5D2A" />
|
||||
<stop offset="1" stopColor="#57B9AF" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="paint9_linear_2096_5587"
|
||||
x1="75"
|
||||
y1="33.9719"
|
||||
x2="75"
|
||||
y2="36.0281"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
>
|
||||
<stop stopColor="#EC5D2A" />
|
||||
<stop offset="1" stopColor="#57B9AF" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="paint10_linear_2096_5587"
|
||||
x1="64"
|
||||
y1="32.9437"
|
||||
x2="64"
|
||||
y2="37.0563"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
>
|
||||
<stop stopColor="#EC5D2A" />
|
||||
<stop offset="1" stopColor="#57B9AF" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="paint11_linear_2096_5587"
|
||||
x1="69"
|
||||
y1="42.9719"
|
||||
x2="69"
|
||||
y2="45.0281"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
>
|
||||
<stop stopColor="#EC5D2A" />
|
||||
<stop offset="1" stopColor="#57B9AF" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="paint12_linear_2096_5587"
|
||||
x1="59.5"
|
||||
y1="33.9859"
|
||||
x2="59.5"
|
||||
y2="35.0141"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
>
|
||||
<stop stopColor="#EC5D2A" />
|
||||
<stop offset="1" stopColor="#57B9AF" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="paint13_linear_2096_5587"
|
||||
x1="54"
|
||||
y1="46.9437"
|
||||
x2="54"
|
||||
y2="51.0563"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
>
|
||||
<stop stopColor="#EC5D2A" />
|
||||
<stop offset="1" stopColor="#57B9AF" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="paint14_linear_2096_5587"
|
||||
x1="96"
|
||||
y1="0.943728"
|
||||
x2="96"
|
||||
y2="5.05625"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
>
|
||||
<stop stopColor="#EC5D2A" />
|
||||
<stop offset="1" stopColor="#57B9AF" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="paint15_linear_2096_5587"
|
||||
x1="101"
|
||||
y1="10.9719"
|
||||
x2="101"
|
||||
y2="13.0281"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
>
|
||||
<stop stopColor="#EC5D2A" />
|
||||
<stop offset="1" stopColor="#57B9AF" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="paint16_linear_2096_5587"
|
||||
x1="86"
|
||||
y1="14.9437"
|
||||
x2="86"
|
||||
y2="19.0563"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
>
|
||||
<stop stopColor="#EC5D2A" />
|
||||
<stop offset="1" stopColor="#57B9AF" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="paint17_linear_2096_5587"
|
||||
x1="74"
|
||||
y1="25.9437"
|
||||
x2="74"
|
||||
y2="30.0563"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
>
|
||||
<stop stopColor="#EC5D2A" />
|
||||
<stop offset="1" stopColor="#57B9AF" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="paint18_linear_2096_5587"
|
||||
x1="85"
|
||||
y1="27.9437"
|
||||
x2="85"
|
||||
y2="32.0563"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
>
|
||||
<stop stopColor="#EC5D2A" />
|
||||
<stop offset="1" stopColor="#57B9AF" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="paint19_linear_2096_5587"
|
||||
x1="75"
|
||||
y1="33.9719"
|
||||
x2="75"
|
||||
y2="36.0281"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
>
|
||||
<stop stopColor="#EC5D2A" />
|
||||
<stop offset="1" stopColor="#57B9AF" />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
);
|
||||
};
|
||||
@@ -1,11 +0,0 @@
|
||||
import React from 'react';
|
||||
|
||||
import ReactDOM from 'react-dom/client';
|
||||
|
||||
import App from './App';
|
||||
|
||||
ReactDOM.createRoot(document.getElementById('root')!).render(
|
||||
<React.StrictMode>
|
||||
<App />
|
||||
</React.StrictMode>
|
||||
);
|
||||
@@ -1,17 +0,0 @@
|
||||
@import url('tailwindcss');
|
||||
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
|
||||
:root {
|
||||
--bg-app: #fff;
|
||||
--text-prominent: #000;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
:root {
|
||||
--bg-app: #1a1a1a;
|
||||
--text-prominent: #fff;
|
||||
}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
import React from 'react';
|
||||
|
||||
import ReactDOM from 'react-dom/client';
|
||||
|
||||
import App from './App';
|
||||
|
||||
ReactDOM.createRoot(document.getElementById('root')!).render(
|
||||
<React.StrictMode>
|
||||
<App />
|
||||
</React.StrictMode>
|
||||
);
|
||||
@@ -1,4 +0,0 @@
|
||||
export interface IPlatformService {
|
||||
// Clipboard operations
|
||||
copyToClipboard(text: string): Promise<void>;
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
import { IPlatformService } from '../IPlatformService';
|
||||
|
||||
declare global {
|
||||
interface Window {
|
||||
electronAPI: {
|
||||
copyToClipboard: (text: string) => Promise<void>;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export class ElectronPlatformService implements IPlatformService {
|
||||
async copyToClipboard(text: string): Promise<void> {
|
||||
return window.electronAPI.copyToClipboard(text);
|
||||
}
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
import { ElectronPlatformService } from './PlatformService';
|
||||
import type { IPlatformService } from '../IPlatformService';
|
||||
|
||||
export const platformService = new ElectronPlatformService();
|
||||
export type { IPlatformService };
|
||||
@@ -1,2 +0,0 @@
|
||||
export type { IPlatformService } from './IPlatformService';
|
||||
export { platformService } from '@platform';
|
||||
@@ -1,7 +0,0 @@
|
||||
import { IPlatformService } from '../IPlatformService';
|
||||
|
||||
export class WebPlatformService implements IPlatformService {
|
||||
async copyToClipboard(text: string): Promise<void> {
|
||||
await navigator.clipboard.writeText(text);
|
||||
}
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
import { WebPlatformService } from './PlatformService';
|
||||
import type { IPlatformService } from '../IPlatformService';
|
||||
|
||||
export const platformService = new WebPlatformService();
|
||||
export type { IPlatformService };
|
||||
@@ -1,108 +0,0 @@
|
||||
import { spawn } from 'child_process';
|
||||
import { Buffer } from 'node:buffer';
|
||||
|
||||
import { test, expect } from '@playwright/test';
|
||||
|
||||
let electronProcess: ReturnType<typeof spawn> | undefined;
|
||||
|
||||
// Helper function to safely kill a process and its children
|
||||
async function killProcess(pid: number): Promise<void> {
|
||||
try {
|
||||
// Try to kill the process group first
|
||||
try {
|
||||
process.kill(-pid, 'SIGTERM');
|
||||
} catch (error: unknown) {
|
||||
const message = error instanceof Error ? error.message : String(error);
|
||||
console.log('Failed to kill process group:', message);
|
||||
}
|
||||
|
||||
// Wait a bit and then try to kill the process directly
|
||||
await new Promise((resolve) => setTimeout(resolve, 1000));
|
||||
|
||||
try {
|
||||
process.kill(pid, 'SIGTERM');
|
||||
} catch (error: unknown) {
|
||||
const message = error instanceof Error ? error.message : String(error);
|
||||
console.log('Failed to kill process:', message);
|
||||
}
|
||||
|
||||
// Final cleanup with SIGKILL if needed
|
||||
await new Promise((resolve) => setTimeout(resolve, 1000));
|
||||
try {
|
||||
process.kill(pid, 'SIGKILL');
|
||||
} catch (error: unknown) {
|
||||
// Process is probably already dead
|
||||
const message = error instanceof Error ? error.message : String(error);
|
||||
console.log('Process already terminated:', message);
|
||||
}
|
||||
} catch (error: unknown) {
|
||||
const message = error instanceof Error ? error.message : String(error);
|
||||
console.log('Error during process cleanup:', message);
|
||||
}
|
||||
}
|
||||
|
||||
test.describe('electron app', () => {
|
||||
test.beforeAll(async () => {
|
||||
console.log('Starting Electron app...');
|
||||
console.log('Environment:', process.env.NODE_ENV);
|
||||
console.log('HEADLESS:', process.env.HEADLESS);
|
||||
|
||||
// Start electron with minimal memory settings
|
||||
electronProcess = spawn('npm', ['run', 'start:electron'], {
|
||||
stdio: 'pipe',
|
||||
shell: true,
|
||||
env: {
|
||||
...process.env,
|
||||
ELECTRON_IS_DEV: '1',
|
||||
NODE_ENV: 'development',
|
||||
HEADLESS: process.env.HEADLESS || 'false',
|
||||
ELECTRON_START_URL: 'http://localhost:3001',
|
||||
// Add memory limits for Electron
|
||||
ELECTRON_EXTRA_LAUNCH_ARGS: '--js-flags="--max-old-space-size=512" --disable-gpu',
|
||||
},
|
||||
// Set detached to false and create a new process group
|
||||
detached: false,
|
||||
});
|
||||
|
||||
// Store the PID for cleanup
|
||||
const pid = electronProcess.pid;
|
||||
console.log('Started Electron app with PID:', pid);
|
||||
|
||||
// Capture stdout and stderr for debugging
|
||||
electronProcess.stdout?.on('data', (data: Buffer) => {
|
||||
console.log(`Electron stdout: ${data.toString()}`);
|
||||
});
|
||||
|
||||
electronProcess.stderr?.on('data', (data: Buffer) => {
|
||||
console.log(`Electron stderr: ${data.toString()}`);
|
||||
});
|
||||
|
||||
// Wait for the app to be ready
|
||||
await new Promise((resolve) => setTimeout(resolve, 2000));
|
||||
});
|
||||
|
||||
test.afterAll(async () => {
|
||||
console.log('Stopping Electron app...');
|
||||
|
||||
if (electronProcess?.pid) {
|
||||
console.log('Killing Electron process:', electronProcess.pid);
|
||||
await killProcess(electronProcess.pid);
|
||||
}
|
||||
|
||||
// Give processes time to fully terminate
|
||||
await new Promise((resolve) => setTimeout(resolve, 2000));
|
||||
});
|
||||
|
||||
test('shows correct runtime', async ({ page }) => {
|
||||
console.log('Navigating to http://localhost:3001');
|
||||
const response = await page.goto('http://localhost:3001');
|
||||
console.log('Navigation status:', response?.status());
|
||||
|
||||
// Wait for and check the text with more detailed logging
|
||||
console.log('Looking for runtime text...');
|
||||
const runtimeText = page.locator('text=Running in: Electron');
|
||||
|
||||
// Wait for the text to be visible
|
||||
await expect(runtimeText).toBeVisible({ timeout: 10000 });
|
||||
});
|
||||
});
|
||||
@@ -1,139 +0,0 @@
|
||||
import { spawn } from 'child_process';
|
||||
import http from 'http';
|
||||
import { Buffer } from 'node:buffer';
|
||||
|
||||
import { test, expect } from '@playwright/test';
|
||||
|
||||
let webProcess: ReturnType<typeof spawn> | undefined;
|
||||
|
||||
// Helper function to check if server is ready
|
||||
async function waitForServer(url: string, timeout: number): Promise<boolean> {
|
||||
const start = Date.now();
|
||||
|
||||
while (Date.now() - start < timeout) {
|
||||
try {
|
||||
await new Promise<void>((resolve, reject) => {
|
||||
const req = http.get(url, (res) => {
|
||||
if (res.statusCode === 200) {
|
||||
resolve();
|
||||
} else {
|
||||
reject(new Error(`Status code: ${res.statusCode}`));
|
||||
}
|
||||
res.resume(); // Consume response data to free up memory
|
||||
});
|
||||
|
||||
req.on('error', reject);
|
||||
req.setTimeout(1000, () => reject(new Error('Request timeout')));
|
||||
});
|
||||
|
||||
console.log('Server is ready');
|
||||
return true;
|
||||
} catch (error: unknown) {
|
||||
const message = error instanceof Error ? error.message : String(error);
|
||||
console.log('Waiting for server...', message);
|
||||
await new Promise((resolve) => setTimeout(resolve, 1000));
|
||||
}
|
||||
}
|
||||
|
||||
console.log('Server failed to respond in time');
|
||||
return false;
|
||||
}
|
||||
|
||||
// Helper function to safely kill a process and its children
|
||||
async function killProcess(pid: number): Promise<void> {
|
||||
try {
|
||||
// Try to kill the process group first
|
||||
try {
|
||||
process.kill(-pid, 'SIGTERM');
|
||||
} catch (error: unknown) {
|
||||
const message = error instanceof Error ? error.message : String(error);
|
||||
console.log('Failed to kill process group:', message);
|
||||
}
|
||||
|
||||
// Wait a bit and then try to kill the process directly
|
||||
await new Promise((resolve) => setTimeout(resolve, 1000));
|
||||
|
||||
try {
|
||||
process.kill(pid, 'SIGTERM');
|
||||
} catch (error: unknown) {
|
||||
const message = error instanceof Error ? error.message : String(error);
|
||||
console.log('Failed to kill process:', message);
|
||||
}
|
||||
|
||||
// Final cleanup with SIGKILL if needed
|
||||
await new Promise((resolve) => setTimeout(resolve, 1000));
|
||||
try {
|
||||
process.kill(pid, 'SIGKILL');
|
||||
} catch (error: unknown) {
|
||||
// Process is probably already dead
|
||||
const message = error instanceof Error ? error.message : String(error);
|
||||
console.log('Process already terminated:', message);
|
||||
}
|
||||
} catch (error: unknown) {
|
||||
const message = error instanceof Error ? error.message : String(error);
|
||||
console.log('Error during process cleanup:', message);
|
||||
}
|
||||
}
|
||||
|
||||
test.describe('web app', () => {
|
||||
test.beforeAll(async () => {
|
||||
console.log('Starting web app...');
|
||||
|
||||
// Start the vite dev server
|
||||
webProcess = spawn('npm', ['run', 'start:web'], {
|
||||
stdio: 'pipe',
|
||||
shell: true,
|
||||
env: {
|
||||
...process.env,
|
||||
NODE_ENV: 'development',
|
||||
},
|
||||
// Set detached to false and create a new process group
|
||||
detached: false,
|
||||
});
|
||||
|
||||
// Store the PID for cleanup
|
||||
const pid = webProcess.pid;
|
||||
console.log('Started Vite server with PID:', pid);
|
||||
|
||||
// Capture stdout and stderr for debugging
|
||||
webProcess.stdout?.on('data', (data: Buffer) => {
|
||||
console.log(`Vite stdout: ${data.toString()}`);
|
||||
});
|
||||
|
||||
webProcess.stderr?.on('data', (data: Buffer) => {
|
||||
console.log(`Vite stderr: ${data.toString()}`);
|
||||
});
|
||||
|
||||
// Wait for server to be ready
|
||||
console.log('Waiting for server to be ready...');
|
||||
const serverReady = await waitForServer('http://localhost:3000', 30000);
|
||||
if (!serverReady) {
|
||||
throw new Error('Server failed to start');
|
||||
}
|
||||
});
|
||||
|
||||
test.afterAll(async () => {
|
||||
console.log('Cleaning up processes...');
|
||||
|
||||
if (webProcess?.pid) {
|
||||
console.log('Killing Vite server process:', webProcess.pid);
|
||||
await killProcess(webProcess.pid);
|
||||
}
|
||||
|
||||
// Give processes time to fully terminate
|
||||
await new Promise((resolve) => setTimeout(resolve, 2000));
|
||||
});
|
||||
|
||||
test('shows correct runtime', async ({ page }) => {
|
||||
console.log('Navigating to http://localhost:3000');
|
||||
const response = await page.goto('http://localhost:3000');
|
||||
console.log('Navigation status:', response?.status());
|
||||
|
||||
// Wait for and check the text with more detailed logging
|
||||
console.log('Looking for runtime text...');
|
||||
const runtimeText = page.locator('text=Running in: Web Browser');
|
||||
|
||||
// Wait for the text to be visible
|
||||
await expect(runtimeText).toBeVisible({ timeout: 10000 });
|
||||
});
|
||||
});
|
||||
@@ -1,15 +0,0 @@
|
||||
import '@testing-library/jest-dom';
|
||||
|
||||
// Add custom matchers
|
||||
declare global {
|
||||
namespace Vi {
|
||||
interface Assertion {
|
||||
// Define the custom matcher without depending on jest types
|
||||
toHaveBeenCalledExactlyOnceWith(...args: unknown[]): void;
|
||||
}
|
||||
interface AsymmetricMatchersContaining {
|
||||
// Define the custom matcher without depending on jest types
|
||||
toHaveBeenCalledExactlyOnceWith(...args: unknown[]): void;
|
||||
}
|
||||
}
|
||||
}
|
||||
24
ui-v2/src/test/types.d.ts
vendored
24
ui-v2/src/test/types.d.ts
vendored
@@ -1,24 +0,0 @@
|
||||
/// <reference types="vitest" />
|
||||
/// <reference types="@testing-library/jest-dom" />
|
||||
|
||||
declare module '*.svg' {
|
||||
import * as React from 'react';
|
||||
export const ReactComponent: React.FunctionComponent<React.SVGProps<SVGSVGElement>>;
|
||||
const src: string;
|
||||
export default src;
|
||||
}
|
||||
|
||||
declare module '*.jpg' {
|
||||
const content: string;
|
||||
export default content;
|
||||
}
|
||||
|
||||
declare module '*.png' {
|
||||
const content: string;
|
||||
export default content;
|
||||
}
|
||||
|
||||
declare module '*.json' {
|
||||
const content: string;
|
||||
export default content;
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
import React from 'react';
|
||||
|
||||
import ReactDOM from 'react-dom/client';
|
||||
|
||||
import App from './App';
|
||||
|
||||
ReactDOM.createRoot(document.getElementById('root')!).render(
|
||||
<React.StrictMode>
|
||||
<App />
|
||||
</React.StrictMode>
|
||||
);
|
||||
@@ -1,13 +0,0 @@
|
||||
/** @type {import('tailwindcss').Config} */
|
||||
export default {
|
||||
content: ['./index.html', './src/**/*.{js,ts,jsx,tsx}'],
|
||||
theme: {
|
||||
extend: {
|
||||
colors: {
|
||||
bgApp: 'var(--bg-app)',
|
||||
textProminent: 'var(--text-prominent)',
|
||||
},
|
||||
},
|
||||
},
|
||||
plugins: [],
|
||||
};
|
||||
@@ -1,32 +0,0 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "ES2020",
|
||||
"useDefineForClassFields": true,
|
||||
"lib": ["ES2020", "DOM", "DOM.Iterable"],
|
||||
"module": "ESNext",
|
||||
"skipLibCheck": true,
|
||||
"moduleResolution": "bundler",
|
||||
"allowImportingTsExtensions": true,
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true,
|
||||
"noEmit": true,
|
||||
"jsx": "react-jsx",
|
||||
"strict": true,
|
||||
"noUnusedLocals": true,
|
||||
"noUnusedParameters": true,
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
"noImplicitAny": true,
|
||||
"noImplicitThis": true,
|
||||
"noUncheckedIndexedAccess": true,
|
||||
"exactOptionalPropertyTypes": true,
|
||||
"noImplicitReturns": true,
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
"@platform": ["src/services/platform/electron"],
|
||||
"@platform/*": ["src/services/platform/electron/*"]
|
||||
},
|
||||
"types": ["vitest/globals", "@testing-library/jest-dom", "electron"]
|
||||
},
|
||||
"include": ["src", "electron", "src/test/types.d.ts"],
|
||||
"references": [{ "path": "./tsconfig.node.json" }]
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "ES2020",
|
||||
"useDefineForClassFields": true,
|
||||
"lib": ["ES2020", "DOM", "DOM.Iterable"],
|
||||
"module": "ESNext",
|
||||
"skipLibCheck": true,
|
||||
"moduleResolution": "bundler",
|
||||
"allowImportingTsExtensions": true,
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true,
|
||||
"noEmit": true,
|
||||
"jsx": "react-jsx",
|
||||
"strict": true,
|
||||
"noUnusedLocals": true,
|
||||
"noUnusedParameters": true,
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
"noImplicitAny": true,
|
||||
"noImplicitThis": true,
|
||||
"noUncheckedIndexedAccess": true,
|
||||
"exactOptionalPropertyTypes": true,
|
||||
"noImplicitReturns": true,
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
"@platform": ["src/services/platform/web"],
|
||||
"@platform/*": ["src/services/platform/web/*"]
|
||||
},
|
||||
"types": ["vitest/globals", "@testing-library/jest-dom"]
|
||||
},
|
||||
"include": ["src", "src/test/types.d.ts"],
|
||||
"exclude": ["electron"],
|
||||
"references": [{ "path": "./tsconfig.node.json" }]
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"composite": true,
|
||||
"skipLibCheck": true,
|
||||
"module": "ESNext",
|
||||
"moduleResolution": "bundler",
|
||||
"allowSyntheticDefaultImports": true
|
||||
},
|
||||
"include": ["vite.config.ts", "vite.main.config.ts", "vite.renderer.config.ts"]
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
import path from 'path';
|
||||
import { fileURLToPath } from 'url';
|
||||
|
||||
import react from '@vitejs/plugin-react';
|
||||
import { defineConfig } from 'vite';
|
||||
|
||||
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||
|
||||
export default defineConfig({
|
||||
plugins: [react()],
|
||||
resolve: {
|
||||
alias: {
|
||||
'@shared': path.resolve(__dirname, './shared'),
|
||||
'@platform': path.resolve(__dirname, './src/services/platform/web'),
|
||||
},
|
||||
},
|
||||
define: {
|
||||
'process.env.IS_ELECTRON': JSON.stringify(false),
|
||||
},
|
||||
build: {
|
||||
outDir: 'dist/web',
|
||||
emptyOutDir: true,
|
||||
},
|
||||
server: {
|
||||
port: 3000,
|
||||
strictPort: true,
|
||||
},
|
||||
});
|
||||
@@ -1,28 +0,0 @@
|
||||
import { builtinModules } from 'module';
|
||||
import path from 'path';
|
||||
|
||||
import { defineConfig } from 'vite';
|
||||
|
||||
// https://vitejs.dev/config/
|
||||
export default defineConfig({
|
||||
build: {
|
||||
outDir: '.vite/build',
|
||||
lib: {
|
||||
entry: {
|
||||
main: path.join(__dirname, 'electron/main.ts'),
|
||||
preload: path.join(__dirname, 'electron/preload.ts'),
|
||||
},
|
||||
formats: ['cjs'],
|
||||
},
|
||||
rollupOptions: {
|
||||
external: ['electron', ...builtinModules],
|
||||
output: {
|
||||
format: 'cjs',
|
||||
entryFileNames: '[name].js',
|
||||
},
|
||||
},
|
||||
emptyOutDir: false,
|
||||
sourcemap: true,
|
||||
minify: false,
|
||||
},
|
||||
});
|
||||
@@ -1,26 +0,0 @@
|
||||
import { builtinModules } from 'module';
|
||||
|
||||
import { defineConfig } from 'vite';
|
||||
|
||||
export default defineConfig({
|
||||
root: process.cwd(),
|
||||
build: {
|
||||
outDir: '.vite/build/preload',
|
||||
lib: {
|
||||
entry: 'electron/preload.ts',
|
||||
formats: ['cjs'],
|
||||
fileName: () => 'preload.js',
|
||||
},
|
||||
rollupOptions: {
|
||||
external: ['electron', ...builtinModules],
|
||||
output: {
|
||||
format: 'cjs',
|
||||
entryFileNames: 'preload.js',
|
||||
sourcemap: false,
|
||||
},
|
||||
},
|
||||
emptyOutDir: true,
|
||||
sourcemap: false,
|
||||
minify: false,
|
||||
},
|
||||
});
|
||||
@@ -1,41 +0,0 @@
|
||||
import path from 'path';
|
||||
import { fileURLToPath } from 'url';
|
||||
|
||||
import react from '@vitejs/plugin-react';
|
||||
import { defineConfig } from 'vite';
|
||||
|
||||
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||
|
||||
// https://vitejs.dev/config/
|
||||
export default defineConfig({
|
||||
plugins: [react()],
|
||||
resolve: {
|
||||
alias: {
|
||||
'@shared': path.resolve(__dirname, './shared'),
|
||||
'@platform': path.resolve(__dirname, './src/services/platform/electron'),
|
||||
},
|
||||
},
|
||||
base: './',
|
||||
define: {
|
||||
'process.env.IS_ELECTRON': JSON.stringify(true),
|
||||
},
|
||||
build: {
|
||||
outDir: '.vite/build/renderer',
|
||||
emptyOutDir: true,
|
||||
rollupOptions: {
|
||||
input: {
|
||||
index: path.join(__dirname, 'index.html'),
|
||||
},
|
||||
},
|
||||
// Ensure production builds don't need unsafe-eval
|
||||
target: 'esnext',
|
||||
minify: 'esbuild',
|
||||
},
|
||||
root: path.join(__dirname, ''),
|
||||
publicDir: 'public',
|
||||
clearScreen: false,
|
||||
server: {
|
||||
port: 3001,
|
||||
strictPort: true,
|
||||
},
|
||||
});
|
||||
@@ -1,26 +0,0 @@
|
||||
/// <reference types="vitest" />
|
||||
import path from 'path';
|
||||
|
||||
import react from '@vitejs/plugin-react';
|
||||
import { defineConfig } from 'vite';
|
||||
|
||||
export default defineConfig({
|
||||
plugins: [react()],
|
||||
test: {
|
||||
globals: true,
|
||||
environment: 'jsdom',
|
||||
setupFiles: ['./src/test/setup.ts'],
|
||||
css: true,
|
||||
exclude: ['**/test/e2e/**', '**/node_modules/**'],
|
||||
coverage: {
|
||||
provider: 'v8',
|
||||
reporter: ['text', 'html'],
|
||||
exclude: ['node_modules/', 'src/test/'],
|
||||
},
|
||||
},
|
||||
resolve: {
|
||||
alias: {
|
||||
'@platform': path.resolve(__dirname, './src/services/platform/web'),
|
||||
},
|
||||
},
|
||||
});
|
||||
Reference in New Issue
Block a user