Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion packages/altair-electron-interop/src/settings.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,16 @@
export interface SettingStore {
settings: {
proxy_setting: 'none' | 'autodetect' | 'system' | 'pac' | 'proxy_server';
proxy_setting:
| 'none'
| 'autodetect'
| 'system'
| 'pac'
| 'proxy_server'
| 'uds_proxy';
pac_address?: string;
proxy_host?: string;
proxy_port?: string;
socket_path?: string;
};
disable_hardware_acceleration: boolean;
}
Expand Down
2 changes: 2 additions & 0 deletions packages/altair-electron-settings/.flowbite-react/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
class-list.json
pid
10 changes: 10 additions & 0 deletions packages/altair-electron-settings/.flowbite-react/config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"$schema": "https://unpkg.com/flowbite-react/schema.json",
"components": [],
"dark": true,
"path": "src/components",
"prefix": "",
"rsc": true,
"tsx": true,
"version": 3
}
22 changes: 22 additions & 0 deletions packages/altair-electron-settings/.flowbite-react/init.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
/* eslint-disable */
// @ts-nocheck
// biome-ignore-all lint: auto-generated file

// This file is auto-generated by the flowbite-react CLI.
// Do not edit this file directly.
// Instead, edit the .flowbite-react/config.json file.

import { StoreInit } from "flowbite-react/store/init";
import React from "react";

export const CONFIG = {
dark: true,
prefix: "",
version: 3,
};

export function ThemeInit() {
return <StoreInit {...CONFIG} />;
}

ThemeInit.displayName = "ThemeInit";
3 changes: 2 additions & 1 deletion packages/altair-electron-settings/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@
},
"dependencies": {
"@altairgraphql/electron-interop": "workspace:*",
"flowbite-react": "^0.7.2",
"flowbite": "^3.1.2",
"flowbite-react": "^0.12.9",
Comment on lines +16 to +17
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

Verify Flowbite React component API compatibility.

The update from flowbite-react ^0.7.2 to ^0.12.9 is a significant version jump. While the learnings indicate no major breaking changes in 0.12.x, ensure all Flowbite React component usage across the codebase is compatible with the new version.

Run the following script to verify all Flowbite React component imports and usage:

Based on learnings


🏁 Script executed:

#!/bin/bash
# Description: Find all Flowbite React component imports and usage patterns
# to verify compatibility with version 0.12.9

# Find all imports from flowbite-react
echo "=== Flowbite React imports ==="
rg -nP --type=tsx --type=ts "from ['\"]flowbite-react['\"]" -A 2

# Find component usage patterns
echo -e "\n=== Component usage patterns ==="
rg -nP --type=tsx --type=ts "<(Sidebar|Modal|Button|Dropdown|Navbar|Tooltip)" -C 2

Length of output: 120


🏁 Script executed:

#!/bin/bash
# Description: Find all Flowbite React component imports and usage patterns
# using glob patterns for .ts and .tsx files

echo "=== Flowbite React imports ==="
rg -nP --glob '*.ts?(x)' "from ['\"]flowbite-react['\"]" -n -C2

echo -e "\n=== Component usage patterns ==="
rg -nP --glob '*.ts?(x)' "<(Sidebar|Modal|Button|Dropdown|Navbar|Tooltip)" -n -C2

Length of output: 362


#!/bin/bash
echo "=== Import or require flowbite-react ==="
rg -nP "(?:from|require)\s*\(?['\"]flowbite-react['\"]\)?"

echo -e "\n=== Flowbite component tags usage ==="
rg -nP "<(Sidebar|Modal|Button|Dropdown|Navbar|Tooltip)" -n -C2

🏁 Script executed:

#!/bin/bash
echo "=== Import or require flowbite-react ==="
rg -nP "(?:from|require)\s*['\"]flowbite-react['\"]"

echo -e "\n=== Flowbite component usage ==="
rg -nP "<(Sidebar|Modal|Button|Dropdown|Navbar|Tooltip)" -n -C2

Length of output: 4105


Update Flowbite-React component API usage

  • In plugins/apollo-tracing/src/components/Tracing/TracingRow.tsx replace the legacy <Tooltip id="…"/> pattern with the v0.12.x API (e.g. <Tooltip content="…" placement="…">…</Tooltip>).
  • Verify Sidebar in packages/altair-electron-settings/src/App.tsx and Radio, Label, Button, TextInput, Card imports and props in packages/altair-electron-settings/src/NetworkSettings.tsx align with the v0.12.x API.
🤖 Prompt for AI Agents
In packages/altair-electron-settings/package.json around lines 16-17 and
affecting plugins/apollo-tracing/src/components/Tracing/TracingRow.tsx,
packages/altair-electron-settings/src/App.tsx and
packages/altair-electron-settings/src/NetworkSettings.tsx: update Flowbite-React
v0.12.x API usage — replace legacy self-closing <Tooltip id="…"/> instances with
the new wrapper form <Tooltip content="…" placement="…">...</Tooltip> in
TracingRow.tsx (move the tooltip text into content and wrap the target element),
and in App.tsx and NetworkSettings.tsx align imports and props to v0.12.x by
importing the correct components from 'flowbite-react' and adjusting prop
names/structure (e.g., use content/placement for Tooltip, ensure
Radio/Label/Button/TextInput/Card props follow v0.12.x signatures such as
controlled value/onChange handlers and updated variant/size prop names); consult
the v0.12.x docs and update each component usage accordingly so the code
compiles with the new Flowbite-React version.

"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-hook-form": "^7.50.0",
Expand Down
17 changes: 11 additions & 6 deletions packages/altair-electron-settings/src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
import { Sidebar } from 'flowbite-react';
import {
Sidebar,
SidebarItems,
SidebarItemGroup,
SidebarItem,
} from 'flowbite-react';
import { HiWifi } from 'react-icons/hi';
import { NetworkSettings } from './NetworkSettings';

Expand All @@ -9,11 +14,11 @@ function App() {
aria-label="Default sidebar example"
className="fixed top-0 left-0 z-40 h-screen"
>
<Sidebar.Items>
<Sidebar.ItemGroup>
<Sidebar.Item icon={HiWifi}>Network</Sidebar.Item>
</Sidebar.ItemGroup>
</Sidebar.Items>
<SidebarItems>
<SidebarItemGroup>
<SidebarItem icon={HiWifi}>Network</SidebarItem>
</SidebarItemGroup>
</SidebarItems>
</Sidebar>
<NetworkSettings />
</div>
Expand Down
157 changes: 85 additions & 72 deletions packages/altair-electron-settings/src/NetworkSettings.tsx
Original file line number Diff line number Diff line change
@@ -1,97 +1,110 @@
import { Radio, Label, Button, TextInput } from 'flowbite-react';
import { Radio, Label, Button, TextInput, Card } from 'flowbite-react';
import { SubmitHandler, useForm } from 'react-hook-form';
import { ipcRenderer as ipc } from 'electron';
import {
SETTINGS_STORE_EVENTS,
SettingStore,
} from '@altairgraphql/electron-interop';
import { log } from './log';

export function NetworkSettings() {
const initialData =
ipc.sendSync(SETTINGS_STORE_EVENTS.GET_SETTINGS_DATA) || {};
console.log(initialData);
const initialData = ipc.sendSync(SETTINGS_STORE_EVENTS.GET_SETTINGS_DATA) || {};
log(initialData);
const { register, handleSubmit, watch } = useForm<SettingStore['settings']>({
defaultValues: initialData,
});
const onSubmit: SubmitHandler<SettingStore['settings']> = data => {
console.log(data);
const onSubmit: SubmitHandler<SettingStore['settings']> = (data) => {
log(data);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🚨 issue (security): Logging submitted settings data may leak sensitive information.

Limit logged fields to avoid exposing sensitive data, or remove this log in production.

ipc.sendSync(SETTINGS_STORE_EVENTS.UPDATE_SETTINGS_DATA, data);
ipc.sendSync('from-renderer:restart-app');
};

return (
<div className="ml-64 p-4">
<form onSubmit={handleSubmit(onSubmit)}>
<fieldset className="flex max-w-md flex-col gap-4">
<legend className="mb-4">Configure proxy</legend>
<div className="flex items-center gap-2">
<Radio
id="proxy-none"
value="none"
{...register('proxy_setting')}
/>
<Label htmlFor="proxy-none">No proxy</Label>
</div>
<div className="flex items-center gap-2">
<Radio
id="proxy-autodetect"
value="autodetect"
{...register('proxy_setting')}
/>
<Label htmlFor="proxy-autodetect">Auto-detect proxy settings</Label>
</div>
<div className="flex items-center gap-2">
<Radio
id="proxy-system"
value="system"
{...register('proxy_setting')}
/>
<Label htmlFor="proxy-system">Use system proxy settings</Label>
</div>
<div className="flex items-center gap-2">
<Radio id="proxy-pac" value="pac" {...register('proxy_setting')} />
<Label htmlFor="proxy-pac">
Use automatic configuration script
</Label>
</div>
{watch('proxy_setting') === 'pac' && (
<div className="ml-8 nested">
<TextInput
type="text"
placeholder="Address URL"
{...register('pac_address')}
<Card>
<form onSubmit={handleSubmit(onSubmit)}>
<fieldset className="flex max-w-md flex-col gap-4">
<legend className="mb-4 dark:text-white">Configure proxy</legend>
<div className="flex items-center gap-2">
<Radio id="proxy-none" value="none" {...register('proxy_setting')} />
<Label htmlFor="proxy-none">No proxy</Label>
</div>
<div className="flex items-center gap-2">
<Radio
id="proxy-autodetect"
value="autodetect"
{...register('proxy_setting')}
/>
<Label htmlFor="proxy-autodetect">Auto-detect proxy settings</Label>
</div>
<div className="flex items-center gap-2">
<Radio
id="proxy-system"
value="system"
{...register('proxy_setting')}
/>
<Label htmlFor="proxy-system">Use system proxy settings</Label>
</div>
)}
<div className="flex items-center gap-2">
<Radio
id="proxy-proxyserver"
value="proxy_server"
{...register('proxy_setting')}
/>
<Label htmlFor="proxy-proxyserver">
Use proxy server for connections
</Label>
</div>
{watch('proxy_setting') === 'proxy_server' && (
<div className="ml-8 nested flex items-center gap-2">
<TextInput
type="text"
placeholder="Host"
{...register('proxy_host')}
<div className="flex items-center gap-2">
<Radio id="proxy-pac" value="pac" {...register('proxy_setting')} />
<Label htmlFor="proxy-pac">Use automatic configuration script</Label>
</div>
{watch('proxy_setting') === 'pac' && (
<div className="ml-8 nested">
<TextInput
type="text"
placeholder="Address URL"
{...register('pac_address')}
/>
</div>
)}
<div className="flex items-center gap-2">
<Radio
id="proxy-proxyserver"
value="proxy_server"
{...register('proxy_setting')}
/>
<TextInput
type="text"
placeholder="Port"
{...register('proxy_port')}
<Label htmlFor="proxy-proxyserver">
Use proxy server for connections
</Label>
</div>
{watch('proxy_setting') === 'proxy_server' && (
<div className="ml-8 nested flex items-center gap-2">
<TextInput
type="text"
placeholder="Host"
{...register('proxy_host')}
/>
<TextInput
type="text"
placeholder="Port"
{...register('proxy_port')}
/>
</div>
)}
<div className="flex items-center gap-2">
<Radio
id="proxy-unixsocket"
value="uds_proxy"
{...register('proxy_setting')}
/>
<Label htmlFor="proxy-unixsocket">Use Unix domain socket proxy</Label>
</div>
{watch('proxy_setting') === 'uds_proxy' && (
<div className="ml-8 nested flex flex-col gap-2">
<TextInput
Comment on lines +93 to +95
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion: Consider validating socket_path input for common path errors.

Since users can enter any value, adding validation or warnings for invalid paths (such as those not starting with '/') can help prevent configuration errors.

Suggested change
{watch('proxy_setting') === 'uds_proxy' && (
<div className="ml-8 nested flex flex-col gap-2">
<TextInput
+ {watch('proxy_setting') === 'uds_proxy' && (
+ <div className="ml-8 nested flex flex-col gap-2">
+ <TextInput
+ label="Socket path"
+ {...register('socket_path', {
+ validate: (value: string) => {
+ if (!value.startsWith('/')) {
+ return 'Socket path should start with "/"';
+ }
+ if (value.trim() === '') {
+ return 'Socket path cannot be empty';
+ }
+ // Add more validation rules as needed
+ return true;
+ },
+ })}
+ error={!!errors.socket_path}
+ helperText={errors.socket_path?.message}
+ />

type="text"
placeholder="Socket path (e.g., /var/run/proxy.sock)"
{...register('socket_path')}
/>
</div>
)}
Comment on lines +91 to +101
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Validate the Unix socket path before restarting

Saving uds_proxy without a socket path sends an empty value to the main process, making setupProxy() throw on the next launch and the app never opens. Block submission until a non-empty path is provided so users don't soft-brick their install.

-  const { register, handleSubmit, watch } = useForm<SettingStore['settings']>({
-    defaultValues: initialData,
-  });
+  const {
+    register,
+    handleSubmit,
+    watch,
+    formState: { errors },
+  } = useForm<SettingStore['settings']>({
+    defaultValues: initialData,
+  });
+  const proxySetting = watch('proxy_setting');
@@
-            {watch('proxy_setting') === 'uds_proxy' && (
+            {proxySetting === 'uds_proxy' && (
               <div className="ml-8 nested flex flex-col gap-2">
                 <TextInput
                   type="text"
                   placeholder="Socket path (e.g., /var/run/proxy.sock)"
-                  {...register('socket_path')}
+                  {...register('socket_path', {
+                    validate: (value) =>
+                      proxySetting !== 'uds_proxy' || value?.trim()
+                        ? true
+                        : 'Socket path is required when using a Unix domain socket proxy',
+                  })}
                 />
+                {errors.socket_path && (
+                  <p className="text-sm text-red-600">
+                    {errors.socket_path.message}
+                  </p>
+                )}
               </div>
             )}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<Label htmlFor="proxy-unixsocket">Use Unix domain socket proxy</Label>
</div>
{watch('proxy_setting') === 'uds_proxy' && (
<div className="ml-8 nested flex flex-col gap-2">
<TextInput
type="text"
placeholder="Socket path (e.g., /var/run/proxy.sock)"
{...register('socket_path')}
/>
</div>
)}
<Label htmlFor="proxy-unixsocket">Use Unix domain socket proxy</Label>
</div>
{proxySetting === 'uds_proxy' && (
<div className="ml-8 nested flex flex-col gap-2">
<TextInput
type="text"
placeholder="Socket path (e.g., /var/run/proxy.sock)"
{...register('socket_path', {
validate: (value) =>
proxySetting !== 'uds_proxy' || value?.trim()
? true
: 'Socket path is required when using a Unix domain socket proxy',
})}
/>
{errors.socket_path && (
<p className="text-sm text-red-600">
{errors.socket_path.message}
</p>
)}
</div>
)}
🤖 Prompt for AI Agents
In packages/altair-electron-settings/src/NetworkSettings.tsx around lines
91-101, the form allows selecting 'uds_proxy' without requiring a socket path
which causes setupProxy() to throw on launch; add validation to block submission
when proxy_setting === 'uds_proxy' and socket_path is empty: mark the TextInput
as required when visible, add a validation rule via register('socket_path', {
required: 'Socket path is required' }) (or call setError on submit if empty),
display the error message inline, and ensure the form submit handler prevents
saving until a non-empty path is provided.

<div className="flex items-center gap-2">
<Button type="submit">Save and restart</Button>
</div>
)}
<div className="flex items-center gap-2">
<Button type="submit">Save and restart</Button>
</div>
</fieldset>
</form>
</fieldset>
</form>
</Card>
</div>
);
}
4 changes: 4 additions & 0 deletions packages/altair-electron-settings/src/log.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export const log = (...args: any[]) => {
// eslint-disable-next-line no-console
console.log('[Settings]', ...args);
};
7 changes: 5 additions & 2 deletions packages/altair-electron-settings/tailwind.config.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
const flowbiteReact = require("flowbite-react/plugin/tailwindcss");

/** @type {import('tailwindcss').Config} */
module.exports = {
content: [
'./index.html',
'./src/**/*.{js,ts,jsx,tsx}',
'node_modules/flowbite-react/lib/esm/**/*.js',
'../../node_modules/flowbite-react/lib/esm/**/*.js',
".flowbite-react/class-list.json"
],
theme: {
extend: {},
},
// plugins: [require('flowbite/plugin')],
};
plugins: [require('flowbite/plugin'), flowbiteReact],
};
3 changes: 2 additions & 1 deletion packages/altair-electron-settings/vite.config.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import commonjs from '@rollup/plugin-commonjs';
import flowbiteReact from 'flowbite-react/plugin/vite';

// https://vitejs.dev/config/
export default defineConfig({
base: '',
plugins: [react(), commonjs()],
plugins: [react(), commonjs(), flowbiteReact()],
build: {
rollupOptions: {
external: ['electron'],
Expand Down
38 changes: 4 additions & 34 deletions packages/altair-electron/src/app/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { app, protocol, session, shell, dialog } from 'electron';
import { app, protocol, shell, dialog } from 'electron';
import { readFile } from 'fs';
import path from 'path';
import isDev from 'electron-is-dev';
Expand All @@ -12,6 +12,7 @@ import {
} from '@altairgraphql/electron-interop';
import { error, log } from '../utils/log';
import { findCustomProtocolUrlInArgv } from '../utils';
import { setupProxy } from '../proxy';

export class ElectronApp {
store: InMemoryStore;
Expand Down Expand Up @@ -60,39 +61,8 @@ export class ElectronApp {
// Some APIs can only be used after this event occurs.
app.whenReady().then(async () => {
const settings = store.get('settings');
log(settings);
if (settings) {
/**
* @type Electron.Config
*/
const proxyConfig: Electron.ProxyConfig = {
mode: 'direct',
};

switch (settings.proxy_setting) {
case 'none':
proxyConfig.mode = 'direct';
break;
case 'autodetect':
proxyConfig.mode = 'auto_detect';
break;
case 'system':
proxyConfig.mode = 'system';
break;
case 'pac':
proxyConfig.mode = 'pac_script';
proxyConfig.pacScript = settings.pac_address;
break;
case 'proxy_server':
proxyConfig.mode = 'fixed_servers';
proxyConfig.proxyRules = `${settings.proxy_host}:${settings.proxy_port}`;
break;
default:
}
await session.defaultSession.setProxy(proxyConfig);
const proxy = await session.defaultSession.resolveProxy('http://localhost');
log(proxy, proxyConfig);
}
await setupProxy(settings);

Comment on lines 63 to +65
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Handle proxy setup failures to keep the app booting

If setupProxy() throws (e.g., UDS selected with a missing socket path), the rejection stops window creation and leaves the app without a UI. Surface the error and exit gracefully instead of letting the promise reject unhandled.

-      await setupProxy(settings);
+      try {
+        await setupProxy(settings);
+      } catch (proxyError) {
+        error('Error setting up proxy', proxyError);
+        dialog.showErrorBox(
+          'Proxy configuration error',
+          proxyError instanceof Error
+            ? proxyError.message
+            : String(proxyError)
+        );
+        return;
+      }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const settings = store.get('settings');
log(settings);
if (settings) {
/**
* @type Electron.Config
*/
const proxyConfig: Electron.ProxyConfig = {
mode: 'direct',
};
switch (settings.proxy_setting) {
case 'none':
proxyConfig.mode = 'direct';
break;
case 'autodetect':
proxyConfig.mode = 'auto_detect';
break;
case 'system':
proxyConfig.mode = 'system';
break;
case 'pac':
proxyConfig.mode = 'pac_script';
proxyConfig.pacScript = settings.pac_address;
break;
case 'proxy_server':
proxyConfig.mode = 'fixed_servers';
proxyConfig.proxyRules = `${settings.proxy_host}:${settings.proxy_port}`;
break;
default:
}
await session.defaultSession.setProxy(proxyConfig);
const proxy = await session.defaultSession.resolveProxy('http://localhost');
log(proxy, proxyConfig);
}
await setupProxy(settings);
const settings = store.get('settings');
try {
await setupProxy(settings);
} catch (proxyError) {
error('Error setting up proxy', proxyError);
dialog.showErrorBox(
'Proxy configuration error',
proxyError instanceof Error
? proxyError.message
: String(proxyError)
);
return;
}
🤖 Prompt for AI Agents
In packages/altair-electron/src/app/index.ts around lines 63 to 65, the await
setupProxy(settings) call can throw and currently leaves the app without a UI;
wrap the call in a try/catch, and in the catch block surface the error (e.g.,
use Electron's dialog.showErrorBox or processLogger.error with the error stack)
and then exit gracefully (app.exit(1) or process.exit(1)) so the failure is
handled instead of letting the promise reject unhandled.

try {
await this.windowManager.createWindow();
} catch (err) {
Expand Down
Loading
Loading