Skip to content

Commit 42e3de6

Browse files
committed
feat: open goose settings
1 parent 242384e commit 42e3de6

6 files changed

Lines changed: 110 additions & 14 deletions

File tree

src/common-types/index.ts

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,5 +28,41 @@ export enum MessageType {
2828
GET_SERVER_STATUS = 'getServerStatus',
2929
RESTART_SERVER = 'restartServer',
3030
FOCUS_CHAT_INPUT = 'focusChatInput',
31-
PREPARE_MESSAGE_WITH_CODE = 'prepareMessageWithCode' // Added for <100 line selections
31+
PREPARE_MESSAGE_WITH_CODE = 'prepareMessageWithCode', // Added for <100 line selections
32+
OPEN_SETTINGS_FILE = 'openSettingsFile' // Added for opening settings
3233
}
34+
35+
// Types copied from src/types/messages.ts to be shared
36+
37+
export type Role = 'user' | 'assistant';
38+
39+
export interface TextContent {
40+
type: 'text';
41+
text: string;
42+
annotations?: Record<string, unknown>;
43+
}
44+
45+
export interface ImageContent {
46+
type: 'image';
47+
data: string; // Assuming base64 encoded data for webview compatibility
48+
mimeType: string;
49+
annotations?: Record<string, unknown>;
50+
}
51+
52+
// Simplified Content type for webview - excluding tool-related types for now
53+
// If tool interactions are needed in webview later, these can be added back carefully
54+
export type SimpleContent = TextContent | ImageContent;
55+
56+
// Basic Message structure shared between extension and webview
57+
export interface Message {
58+
id?: string; // Optional ID, might be assigned later
59+
role: Role;
60+
created: number; // Unix timestamp (seconds)
61+
content: SimpleContent[]; // Use simplified content for now
62+
// Removed tool-related fields for simplicity in shared type
63+
}
64+
65+
// Note: Tool-related types (ToolCall, ToolResult, etc.) are kept in src/types/messages.ts
66+
// as they are primarily used within the extension host logic for now.
67+
// If the webview needs to display or interact with tool calls/results directly,
68+
// these types would need to be shared and potentially adapted.

src/extension.ts

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ import { GooseCodeActionProvider } from './utils/codeActionProvider';
1313
import { SessionManager, SessionEvents } from './server/chat/sessionManager';
1414
// Import MessageType from common types
1515
import { MessageType } from './common-types';
16+
// Import config reader function
17+
import { getConfigFilePath } from './utils/configReader';
1618
// Import logging utilities
1719
import { DefaultLogger, getLogger, LogLevel } from './utils/logging';
1820

@@ -430,6 +432,29 @@ class GooseViewProvider implements vscode.WebviewViewProvider {
430432
});
431433
break;
432434

435+
case MessageType.OPEN_SETTINGS_FILE:
436+
try {
437+
const configPath = getConfigFilePath(); // Use the imported function
438+
if (configPath) {
439+
const uri = vscode.Uri.file(configPath);
440+
try {
441+
const doc = await vscode.workspace.openTextDocument(uri);
442+
await vscode.window.showTextDocument(doc);
443+
logger.info(`Opened settings file: ${configPath}`);
444+
} catch (err) {
445+
logger.error(`Failed to open settings file at ${configPath}:`, err);
446+
vscode.window.showErrorMessage(`Could not open settings file. It might not exist or there was a read error. Expected location: ${configPath}`);
447+
}
448+
} else {
449+
logger.error('Could not determine the path to the settings file.');
450+
vscode.window.showErrorMessage('Could not determine the path to the Goose settings file for your OS.');
451+
}
452+
} catch (error) {
453+
logger.error('Error handling OPEN_SETTINGS_FILE:', error);
454+
vscode.window.showErrorMessage('An unexpected error occurred while trying to open the settings file.');
455+
}
456+
break;
457+
433458
default:
434459
console.log(`Unhandled message: ${message.command}`);
435460
}

src/utils/configReader.ts

Lines changed: 21 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ export interface OS {
3030
/**
3131
* Get the path to the Goose config file based on the current operating system
3232
* @param os The OS module to use (for testing)
33-
* @returns The path to the config file, or null if the platform isn't supported
33+
* @returns The path to the config file, or null if the platform isn't supported or APPDATA is missing on Windows.
3434
*/
3535
export function getConfigPath(os: OS = osDefault): string | null {
3636
const homeDir = os.homedir();
@@ -42,9 +42,10 @@ export function getConfigPath(os: OS = osDefault): string | null {
4242
const appData = process.env.APPDATA;
4343
if (!appData) {
4444
logger.error('Could not determine APPDATA directory on Windows.');
45-
return null;
45+
return null; // Return null if APPDATA is not set
4646
}
47-
configPath = path.join(homeDir, 'AppData', 'Roaming', 'Block', 'goose', 'config', 'config.yaml');
47+
// Use appData instead of homeDir for the base path on Windows
48+
configPath = path.join(appData, 'Block', 'goose', 'config', 'config.yaml');
4849
break;
4950
case 'darwin': // macOS
5051
case 'linux':
@@ -59,7 +60,7 @@ export function getConfigPath(os: OS = osDefault): string | null {
5960
* Read and parse the Goose configuration file
6061
* @param fs Optional file system implementation for testing
6162
* @param os Optional OS implementation for testing
62-
* @returns A GooseConfig object containing provider and model, or null values if not found
63+
* @returns A GooseConfig object containing provider and model, or null values if not found or on error.
6364
*/
6465
export function readGooseConfig(fs: FileSystem = fsDefault, os: OS = osDefault): GooseConfig {
6566
const defaultConfig: GooseConfig = { provider: null, model: null };
@@ -79,12 +80,12 @@ export function readGooseConfig(fs: FileSystem = fsDefault, os: OS = osDefault):
7980
}
8081

8182
const fileContents = fs.readFileSync(configPath, 'utf8');
82-
83+
8384
// Handle both string and buffer return types
84-
const contentStr = typeof fileContents === 'string'
85-
? fileContents
85+
const contentStr = typeof fileContents === 'string'
86+
? fileContents
8687
: fileContents.toString('utf8');
87-
88+
8889
const config = YAML.parse(contentStr) as any; // Use 'any' for flexibility, validate below
8990

9091
if (typeof config !== 'object' || config === null) {
@@ -97,9 +98,10 @@ export function readGooseConfig(fs: FileSystem = fsDefault, os: OS = osDefault):
9798

9899
if (!provider) {
99100
logger.warn('GOOSE_PROVIDER key not found or invalid in config file.');
101+
// Don't return early, still check for model
100102
}
101103
if (!model) {
102-
logger.warn('GOOSE_MODEL key not found or invalid in config file.');
104+
logger.warn('GOOSE_MODEL key not found or invalid in config file.');
103105
}
104106

105107
logger.info(`Loaded config: Provider=${provider ?? 'MISSING'}, Model=${model ?? 'MISSING'}`);
@@ -112,3 +114,13 @@ export function readGooseConfig(fs: FileSystem = fsDefault, os: OS = osDefault):
112114
return defaultConfig; // Return nulls on error (file read/parse error)
113115
}
114116
}
117+
118+
/**
119+
* Gets the determined path to the configuration file.
120+
* This is essentially a wrapper around getConfigPath for clarity.
121+
* @param os Optional OS implementation for testing
122+
* @returns The config file path string, or null if not found/determined.
123+
*/
124+
export function getConfigFilePath(os: OS = osDefault): string | null {
125+
return getConfigPath(os);
126+
}

webview-ui/src/App.tsx

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,14 @@ const App: React.FC = () => {
6464
currentSession
6565
} = useSessionManagement(isLoading, sessionDrawerRef, sessionToggleButtonRef); // Pass refs to the hook
6666

67+
// Handler for opening the settings file
68+
const handleOpenSettings = useCallback(() => {
69+
const vscode = getVSCodeAPI();
70+
vscode.postMessage({
71+
command: MessageType.OPEN_SETTINGS_FILE
72+
});
73+
}, []);
74+
6775
// Handler for sending a chat message
6876
const handleSendMessage = useCallback(() => {
6977
if (!inputMessage.trim() && codeReferences.length === 0) {
@@ -143,6 +151,7 @@ const App: React.FC = () => {
143151
onToggleSessionDrawer={toggleSessionDrawer}
144152
isGenerating={isLoading}
145153
onNewSession={handleCreateSession}
154+
onOpenSettings={handleOpenSettings} // Pass settings handler
146155
toggleButtonRef={sessionToggleButtonRef} // Pass ref down to Header
147156
/>
148157

webview-ui/src/components/Header.tsx

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,15 @@
11
import React, { RefObject } from 'react'; // Import RefObject
22
import { SessionMetadata } from './SessionList';
33
// Import Lucide icons
4-
import { History, Plus } from 'lucide-react';
4+
import { History, Plus, Settings } from 'lucide-react'; // Added Settings icon
55

66
interface HeaderProps {
77
status: string;
88
currentSession: SessionMetadata | null; // Keep currentSession prop if needed elsewhere, otherwise remove if unused
99
onToggleSessionDrawer: () => void;
1010
isGenerating: boolean;
1111
onNewSession: () => void;
12+
onOpenSettings: () => void; // Add handler for opening settings
1213
toggleButtonRef: RefObject<HTMLButtonElement>; // Add ref prop for the toggle button
1314
}
1415

@@ -17,6 +18,7 @@ export const Header: React.FC<HeaderProps> = ({
1718
onToggleSessionDrawer,
1819
isGenerating,
1920
onNewSession,
21+
onOpenSettings, // Destructure the settings handler
2022
toggleButtonRef // Destructure the ref prop
2123
}) => {
2224
// Helper to get status display text (incorporating isGenerating)
@@ -64,6 +66,16 @@ export const Header: React.FC<HeaderProps> = ({
6466
<Plus size={16} /> {/* Use Lucide Plus icon */}
6567
</button>
6668

69+
{/* Settings Button */}
70+
<button
71+
className="icon-button"
72+
title="Open Settings File"
73+
onClick={onOpenSettings}
74+
disabled={isGenerating} // Optionally disable during generation
75+
>
76+
<Settings size={16} /> {/* Use Lucide Settings icon */}
77+
</button>
78+
6779
{/* Session History Button - Assign the ref here */}
6880
<button
6981
ref={toggleButtonRef} // Assign the ref

webview-ui/src/types/index.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,8 @@ export enum MessageType {
2424
GET_SESSIONS = 'getSessions',
2525
RESTART_SERVER = 'restartServer',
2626
GET_SERVER_STATUS = 'getServerStatus',
27-
FOCUS_CHAT_INPUT = 'focusChatInput'
27+
FOCUS_CHAT_INPUT = 'focusChatInput',
28+
OPEN_SETTINGS_FILE = 'openSettingsFile' // Added for opening settings
2829
}
2930

3031
// Type for code references
@@ -57,9 +58,10 @@ export interface VSCodeAPI {
5758
}
5859

5960
// Re-export Message type from shared types
60-
export type { Message } from '../../../src/shared/types';
61+
export type { Message } from '../../../src/common-types/index'; // Re-added with correct path
6162

6263
// Export TextContent and MessageContent types
64+
// Note: These might differ slightly from the shared SimpleContent if webview needs specific handling
6365
export interface TextContent {
6466
type: 'text';
6567
text: string;
@@ -77,4 +79,4 @@ export interface ActionContent {
7779
action: string;
7880
}
7981

80-
export type MessageContent = TextContent | ImageContent | ActionContent;
82+
export type MessageContent = TextContent | ImageContent | ActionContent;

0 commit comments

Comments
 (0)