Add terminal snippets feature with sidebar UI #377

Merged
LukeGus merged 2 commits from feature-snippets-in-ssh-tools into dev-1.8.0 2025-10-08 01:02:25 +00:00
LukeGus commented 2025-10-08 00:50:52 +00:00 (Migrated from github.com)
No description provided.
gemini-code-assist[bot] commented 2025-10-08 00:51:07 +00:00 (Migrated from github.com)

Summary of Changes

Hello @LukeGus, I'm Gemini Code Assist1 ! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This PR introduces a comprehensive 'terminal snippets' feature, enabling users to manage and execute saved commands via a new sidebar UI. It includes full-stack implementation, from database schema and API endpoints to frontend components and internationalization.

Highlights

  • Intent: This pull request introduces a new 'terminal snippets' feature, allowing users to save, manage, and quickly execute common commands directly within the terminal interface. It includes both backend API support and a frontend sidebar UI for interaction.
  • Key Changes: The changes span across backend, frontend, and internationalization files:
  1. Backend API & Database:

    • A new snippets table is added to the database schema (src/backend/database/db/index.ts, src/backend/database/db/schema.ts) to store user-specific command snippets (name, content, description, timestamps).
    • New RESTful API endpoints (/snippets) are implemented in src/backend/database/routes/snippets.ts for CRUD operations (create, read, update, delete) on snippets, including authentication and data access control.
    • The main backend application (src/backend/database/database.ts) is updated to import and use these new snippet routes.
  2. Frontend UI & Logic:

    • New TypeScript interfaces (Snippet, SnippetData) are defined in src/types/index.ts for type safety.
    • A new React component, SnippetsSidebar.tsx, is added (src/ui/Desktop/Apps/Terminal/SnippetsSidebar.tsx). This component provides the user interface for viewing, creating, editing, deleting, executing, and copying snippets.
    • The TopNavbar.tsx component (src/ui/Desktop/Navigation/TopNavbar.tsx) is updated to include a new button to toggle the SnippetsSidebar. This button is enabled only when a terminal tab is active, and it integrates the functionality to send snippet content to the active terminal.
    • New API client functions (getSnippets, createSnippet, updateSnippet, deleteSnippet) are added to src/ui/main-axios.ts to interact with the backend snippet endpoints.
  3. Internationalization:

    • New translation keys for the 'snippets' feature, including UI labels, placeholders, validation messages, and success/error messages, are added to src/locales/en/translation.json and src/locales/zh/translation.json.
  • Reviewer Activity: No reviewer activity has been recorded yet.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.


  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution. ↩︎

## Summary of Changes Hello @LukeGus, I'm Gemini Code Assist[^1]! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed! This PR introduces a comprehensive 'terminal snippets' feature, enabling users to manage and execute saved commands via a new sidebar UI. It includes full-stack implementation, from database schema and API endpoints to frontend components and internationalization. ### Highlights * **Intent**: This pull request introduces a new 'terminal snippets' feature, allowing users to save, manage, and quickly execute common commands directly within the terminal interface. It includes both backend API support and a frontend sidebar UI for interaction. * **Key Changes**: The changes span across backend, frontend, and internationalization files: 1. **Backend API & Database:** * A new `snippets` table is added to the database schema (`src/backend/database/db/index.ts`, `src/backend/database/db/schema.ts`) to store user-specific command snippets (name, content, description, timestamps). * New RESTful API endpoints (`/snippets`) are implemented in `src/backend/database/routes/snippets.ts` for CRUD operations (create, read, update, delete) on snippets, including authentication and data access control. * The main backend application (`src/backend/database/database.ts`) is updated to import and use these new snippet routes. 2. **Frontend UI & Logic:** * New TypeScript interfaces (`Snippet`, `SnippetData`) are defined in `src/types/index.ts` for type safety. * A new React component, `SnippetsSidebar.tsx`, is added (`src/ui/Desktop/Apps/Terminal/SnippetsSidebar.tsx`). This component provides the user interface for viewing, creating, editing, deleting, executing, and copying snippets. * The `TopNavbar.tsx` component (`src/ui/Desktop/Navigation/TopNavbar.tsx`) is updated to include a new button to toggle the `SnippetsSidebar`. This button is enabled only when a terminal tab is active, and it integrates the functionality to send snippet content to the active terminal. * New API client functions (`getSnippets`, `createSnippet`, `updateSnippet`, `deleteSnippet`) are added to `src/ui/main-axios.ts` to interact with the backend snippet endpoints. 3. **Internationalization:** * New translation keys for the 'snippets' feature, including UI labels, placeholders, validation messages, and success/error messages, are added to `src/locales/en/translation.json` and `src/locales/zh/translation.json`. * **Reviewer Activity**: No reviewer activity has been recorded yet. <details> <summary><b>Using Gemini Code Assist</b></summary> <br> The full guide for Gemini Code Assist can be found on our [documentation page](https://developers.google.com/gemini-code-assist/docs/review-github-code), here are some quick tips. <b>Invoking Gemini</b> You can request assistance from Gemini at any point by creating a comment using either `/gemini <command>` or `@gemini-code-assist <command>`. Below is a summary of the supported commands on the current page. Feature | Command | Description --- | --- | --- Code Review | `/gemini review` | Performs a code review for the current pull request in its current state. Pull Request Summary | `/gemini summary` | Provides a summary of the current pull request in its current state. Comment | @gemini-code-assist | Responds in comments when explicitly tagged, both in pull request comments and review comments. Help | `/gemini help` | Displays a list of available commands. <b>Customization</b> To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a `.gemini/` folder in the base of the repository. Detailed instructions can be found [here](https://developers.google.com/gemini-code-assist/docs/customize-gemini-behavior-github). <b>Limitations & Feedback</b> Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with :thumbsup: and :thumbsdown: on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up [here](https://google.qualtrics.com/jfe/form/SV_2cyuGuTWsEw84yG). <b>You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the [Gemini Code Assist IDE Extension](https://cloud.google.com/products/gemini/code-assist).</b> </details> [^1]: Review the [Privacy Notices](https://policies.google.com/privacy), [Generative AI Prohibited Use Policy](https://policies.google.com/terms/generative-ai/use-policy), [Terms of Service](https://policies.google.com/terms), and learn how to configure Gemini Code Assist in GitHub [here](https://developers.google.com/gemini-code-assist/docs/customize-gemini-behavior-github). Gemini can make mistakes, so double check it and [use code with caution](https://support.google.com/legal/answer/13505487).
gemini-code-assist[bot] (Migrated from github.com) reviewed 2025-10-08 00:52:57 +00:00
gemini-code-assist[bot] (Migrated from github.com) left a comment

Code Review

This pull request introduces a new terminal snippets feature, complete with backend API endpoints, database schema updates, and a frontend sidebar UI. The implementation is well-structured and covers all necessary aspects of the feature. My review focuses on enhancing input validation on the backend for robustness, improving database query efficiency, and increasing type safety on the frontend for better maintainability.

## Code Review This pull request introduces a new terminal snippets feature, complete with backend API endpoints, database schema updates, and a frontend sidebar UI. The implementation is well-structured and covers all necessary aspects of the feature. My review focuses on enhancing input validation on the backend for robustness, improving database query efficiency, and increasing type safety on the frontend for better maintainability.
@@ -0,0 +1,251 @@
import express from "express";
gemini-code-assist[bot] (Migrated from github.com) commented 2025-10-08 00:52:57 +00:00

high

The route parameter id is used with parseInt without validation. If id is not a string that can be parsed into a number (e.g., an empty string or non-numeric characters), parseInt will return NaN. This could lead to unexpected behavior or errors in the database query. You should validate that id is a valid number before using it.

This same validation should be applied to the PUT /:id and DELETE /:id routes as well.

    const { id } = req.params;
    const snippetId = parseInt(id, 10);

    if (!isNonEmptyString(userId) || isNaN(snippetId)) {
      authLogger.warn("Invalid request for snippet fetch: invalid ID", { userId, id });
      return res.status(400).json({ error: "Invalid request parameters" });
    }
![high](https://www.gstatic.com/codereviewagent/high-priority.svg) The route parameter `id` is used with `parseInt` without validation. If `id` is not a string that can be parsed into a number (e.g., an empty string or non-numeric characters), `parseInt` will return `NaN`. This could lead to unexpected behavior or errors in the database query. You should validate that `id` is a valid number before using it. This same validation should be applied to the `PUT /:id` and `DELETE /:id` routes as well. ```suggestion const { id } = req.params; const snippetId = parseInt(id, 10); if (!isNonEmptyString(userId) || isNaN(snippetId)) { authLogger.warn("Invalid request for snippet fetch: invalid ID", { userId, id }); return res.status(400).json({ error: "Invalid request parameters" }); } ```
@@ -0,0 +167,4 @@
if (updateData.content !== undefined)
updateFields.content = updateData.content.trim();
if (updateData.description !== undefined)
updateFields.description = updateData.description?.trim() || null;
gemini-code-assist[bot] (Migrated from github.com) commented 2025-10-08 00:52:57 +00:00

high

The update endpoint allows setting name and content to empty strings, which is inconsistent with the creation endpoint that requires non-empty strings via isNonEmptyString. You should add validation to ensure name and content are not empty if they are being updated.

Additionally, you can avoid using any for updateFields by using a more specific type like Partial<typeof snippets.$inferInsert> for better type safety.

      const updateFields: Partial<typeof snippets.$inferInsert> = {
        updatedAt: sql`CURRENT_TIMESTAMP`,
      };

      if (updateData.name !== undefined) {
        if (!isNonEmptyString(updateData.name)) {
          return res.status(400).json({ error: "Name cannot be empty" });
        }
        updateFields.name = updateData.name.trim();
      }
      if (updateData.content !== undefined) {
        if (!isNonEmptyString(updateData.content)) {
          return res.status(400).json({ error: "Content cannot be empty" });
        }
        updateFields.content = updateData.content.trim();
      }
      if (updateData.description !== undefined) {
        updateFields.description = updateData.description?.trim() || null;
      }
![high](https://www.gstatic.com/codereviewagent/high-priority.svg) The update endpoint allows setting `name` and `content` to empty strings, which is inconsistent with the creation endpoint that requires non-empty strings via `isNonEmptyString`. You should add validation to ensure `name` and `content` are not empty if they are being updated. Additionally, you can avoid using `any` for `updateFields` by using a more specific type like `Partial<typeof snippets.$inferInsert>` for better type safety. ```typescript const updateFields: Partial<typeof snippets.$inferInsert> = { updatedAt: sql`CURRENT_TIMESTAMP`, }; if (updateData.name !== undefined) { if (!isNonEmptyString(updateData.name)) { return res.status(400).json({ error: "Name cannot be empty" }); } updateFields.name = updateData.name.trim(); } if (updateData.content !== undefined) { if (!isNonEmptyString(updateData.content)) { return res.status(400).json({ error: "Content cannot be empty" }); } updateFields.content = updateData.content.trim(); } if (updateData.description !== undefined) { updateFields.description = updateData.description?.trim() || null; } ```
@@ -0,0 +177,4 @@
const updated = await db
.select()
.from(snippets)
.where(eq(snippets.id, parseInt(id)));
gemini-code-assist[bot] (Migrated from github.com) commented 2025-10-08 00:52:57 +00:00

medium

To improve efficiency, you can use Drizzle's .returning() method on the update query. This will return the updated record in a single database call, avoiding the need for a separate select query.

      const updated = await db
        .update(snippets)
        .set(updateFields)
        .where(and(eq(snippets.id, parseInt(id)), eq(snippets.userId, userId)))
        .returning();
![medium](https://www.gstatic.com/codereviewagent/medium-priority.svg) To improve efficiency, you can use Drizzle's `.returning()` method on the update query. This will return the updated record in a single database call, avoiding the need for a separate `select` query. ```typescript const updated = await db .update(snippets) .set(updateFields) .where(and(eq(snippets.id, parseInt(id)), eq(snippets.userId, userId))) .returning(); ```
@@ -2158,0 +2197,4 @@
} catch (error) {
throw handleApiError(error, "delete snippet");
}
}
gemini-code-assist[bot] (Migrated from github.com) commented 2025-10-08 00:52:57 +00:00

medium

To improve type safety and maintainability, you should use the specific Snippet and SnippetData types for the parameters and return values of these API functions instead of any. You'll need to add Snippet and SnippetData to your imports from ../types/index.js.

export async function getSnippets(): Promise<Snippet[]> {
  try {
    const response = await authApi.get<Snippet[]>("/snippets");
    return response.data;
  } catch (error) {
    throw handleApiError(error, "fetch snippets");
  }
}

export async function createSnippet(snippetData: SnippetData): Promise<Snippet> {
  try {
    const response = await authApi.post<Snippet>("/snippets", snippetData);
    return response.data;
  } catch (error) {
    throw handleApiError(error, "create snippet");
  }
}

export async function updateSnippet(
  snippetId: number,
  snippetData: Partial<SnippetData>,
): Promise<Snippet> {
  try {
    const response = await authApi.put<Snippet>(
      `/snippets/${snippetId}`,
      snippetData,
    );
    return response.data;
  } catch (error) {
    throw handleApiError(error, "update snippet");
  }
}

export async function deleteSnippet(snippetId: number): Promise<{ success: boolean }> {
  try {
    const response = await authApi.delete<{ success: boolean }>(
      `/snippets/${snippetId}`,
    );
    return response.data;
  } catch (error) {
    throw handleApiError(error, "delete snippet");
  }
}
![medium](https://www.gstatic.com/codereviewagent/medium-priority.svg) To improve type safety and maintainability, you should use the specific `Snippet` and `SnippetData` types for the parameters and return values of these API functions instead of `any`. You'll need to add `Snippet` and `SnippetData` to your imports from `../types/index.js`. ```typescript export async function getSnippets(): Promise<Snippet[]> { try { const response = await authApi.get<Snippet[]>("/snippets"); return response.data; } catch (error) { throw handleApiError(error, "fetch snippets"); } } export async function createSnippet(snippetData: SnippetData): Promise<Snippet> { try { const response = await authApi.post<Snippet>("/snippets", snippetData); return response.data; } catch (error) { throw handleApiError(error, "create snippet"); } } export async function updateSnippet( snippetId: number, snippetData: Partial<SnippetData>, ): Promise<Snippet> { try { const response = await authApi.put<Snippet>( `/snippets/${snippetId}`, snippetData, ); return response.data; } catch (error) { throw handleApiError(error, "update snippet"); } } export async function deleteSnippet(snippetId: number): Promise<{ success: boolean }> { try { const response = await authApi.delete<{ success: boolean }>( `/snippets/${snippetId}`, ); return response.data; } catch (error) { throw handleApiError(error, "delete snippet"); } } ```
Sign in to join this conversation.