-
- {sections.map((section) => (
-
- props.setSelectedSection(
- props.selectedSection === section.id ? null : section.id,
- )
- }
- />
- ))}
-
+
+
+
+
+
+ {sections.map((section) => (
+
+ props.setSelectedSection(
+ props.selectedSection === section.id ? null : section.id,
+ )
+ }
+ />
+ ))}
+
);
diff --git a/gui/src/components/mainInput/Lump/sections/PromptsSection.tsx b/gui/src/components/mainInput/Lump/sections/PromptsSection.tsx
index 21a3e294bc..6ad5368091 100644
--- a/gui/src/components/mainInput/Lump/sections/PromptsSection.tsx
+++ b/gui/src/components/mainInput/Lump/sections/PromptsSection.tsx
@@ -3,8 +3,9 @@ import {
PencilIcon,
} from "@heroicons/react/24/outline";
import { BookmarkIcon as BookmarkSolid } from "@heroicons/react/24/solid";
+import { useBookmarkedSlashCommands } from "../../../../hooks/useBookmarkedSlashCommands";
+import { useAppSelector } from "../../../../redux/hooks";
import { fontSize } from "../../../../util";
-import { useBookmarkedSlashCommands } from "../../../ConversationStarters/useBookmarkedSlashCommands";
import { ExploreBlocksButton } from "./ExploreBlocksButton";
interface PromptRowProps {
@@ -54,22 +55,32 @@ function PromptRow({
}
export function PromptsSection() {
- const { cmdsSortedByBookmark, bookmarkStatuses, toggleBookmark } =
- useBookmarkedSlashCommands();
+ const { isCommandBookmarked, toggleBookmark } = useBookmarkedSlashCommands();
+ const slashCommands = useAppSelector(
+ (state) => state.config.config.slashCommands ?? [],
+ );
const handleEdit = (prompt: any) => {
// Handle edit action here
console.log("Editing prompt:", prompt);
};
+ const sortedCommands = [...slashCommands].sort((a, b) => {
+ const aBookmarked = isCommandBookmarked(a.name);
+ const bBookmarked = isCommandBookmarked(b.name);
+ if (aBookmarked && !bBookmarked) return -1;
+ if (!aBookmarked && bBookmarked) return 1;
+ return 0;
+ });
+
return (
- {cmdsSortedByBookmark?.map((prompt, i) => (
+ {sortedCommands.map((prompt) => (
toggleBookmark(prompt)}
onEdit={() => handleEdit(prompt)}
/>
diff --git a/gui/src/hooks/useBookmarkedSlashCommands.ts b/gui/src/hooks/useBookmarkedSlashCommands.ts
new file mode 100644
index 0000000000..d00c609346
--- /dev/null
+++ b/gui/src/hooks/useBookmarkedSlashCommands.ts
@@ -0,0 +1,45 @@
+import { SlashCommandDescription } from "core";
+import { usePostHog } from "posthog-js/react";
+import {
+ bookmarkSlashCommand,
+ selectBookmarkedSlashCommands,
+ unbookmarkSlashCommand,
+} from "../redux";
+import { useAppDispatch, useAppSelector } from "../redux/hooks";
+
+export function useBookmarkedSlashCommands() {
+ const dispatch = useAppDispatch();
+ const posthog = usePostHog();
+ const bookmarkedCommands = useAppSelector(selectBookmarkedSlashCommands);
+
+ const isCommandBookmarked = (commandName: string): boolean => {
+ return bookmarkedCommands.includes(commandName);
+ };
+
+ const toggleBookmark = (command: SlashCommandDescription) => {
+ const isBookmarked = isCommandBookmarked(command.name);
+
+ posthog.capture("toggle_bookmarked_slash_command", {
+ isBookmarked,
+ });
+
+ if (isBookmarked) {
+ dispatch(
+ unbookmarkSlashCommand({
+ commandName: command.name,
+ }),
+ );
+ } else {
+ dispatch(
+ bookmarkSlashCommand({
+ commandName: command.name,
+ }),
+ );
+ }
+ };
+
+ return {
+ isCommandBookmarked,
+ toggleBookmark,
+ };
+}
diff --git a/gui/src/hooks/useSetup.ts b/gui/src/hooks/useSetup.ts
index 974787f503..d90259a9b4 100644
--- a/gui/src/hooks/useSetup.ts
+++ b/gui/src/hooks/useSetup.ts
@@ -4,7 +4,11 @@ import { IdeMessengerContext } from "../context/IdeMessenger";
import { ConfigResult } from "@continuedev/config-yaml";
import { BrowserSerializedContinueConfig } from "core";
-import { selectProfileThunk } from "../redux";
+import {
+ initializeProfilePreferencesThunk,
+ selectProfileThunk,
+ selectSelectedProfileId,
+} from "../redux";
import { useAppDispatch, useAppSelector } from "../redux/hooks";
import {
selectDefaultModel,
@@ -28,6 +32,7 @@ function useSetup() {
const ideMessenger = useContext(IdeMessengerContext);
const history = useAppSelector((store) => store.session.history);
const defaultModel = useAppSelector(selectDefaultModel);
+ const selectedProfileId = useAppSelector(selectSelectedProfileId);
const hasLoadedConfig = useRef(false);
@@ -47,6 +52,12 @@ function useSetup() {
dispatch(setConfigResult(configResult));
dispatch(selectProfileThunk(profileId));
+ const isNewProfileId = profileId && profileId !== selectedProfileId;
+
+ if (isNewProfileId) {
+ dispatch(initializeProfilePreferencesThunk({ profileId }));
+ }
+
// Perform any actions needed with the config
if (configResult.config?.ui?.fontSize) {
setLocalStorage("fontSize", configResult.config.ui.fontSize);
diff --git a/gui/src/pages/gui/Chat.tsx b/gui/src/pages/gui/Chat.tsx
index 653efcd3df..50f1291ec5 100644
--- a/gui/src/pages/gui/Chat.tsx
+++ b/gui/src/pages/gui/Chat.tsx
@@ -427,7 +427,7 @@ export function Chat() {
className="flex items-center gap-2"
>
- Last Session
+ Last Session
)}
diff --git a/gui/src/redux/slices/profiles/slice.ts b/gui/src/redux/slices/profiles/slice.ts
index 2d3386a5fc..c8eabf1683 100644
--- a/gui/src/redux/slices/profiles/slice.ts
+++ b/gui/src/redux/slices/profiles/slice.ts
@@ -1,9 +1,15 @@
import { createSlice, PayloadAction } from "@reduxjs/toolkit";
+import { SlashCommandDescription } from "core";
import { ProfileDescription } from "core/config/ConfigHandler";
-import { ensureProfilePreferences } from "./utils";
+
+const DEFAULT_SLASH_COMMANDS_BOOKMARKS_COUNT = 5;
+
+const INITIAL_PREFERENCES_STATE: PreferencesState = {
+ bookmarkedSlashCommands: [],
+};
export interface PreferencesState {
- bookmarksByName: string[];
+ bookmarkedSlashCommands: string[];
}
export interface ProfilesState {
@@ -24,21 +30,42 @@ export const profilesSlice = createSlice({
reducers: {
setSelectedProfile: (state, { payload }: PayloadAction
) => {
state.selectedProfileId = payload;
-
- if (payload) {
- ensureProfilePreferences(state, payload);
- }
},
setAvailableProfiles: (
state,
{ payload }: PayloadAction,
) => {
state.availableProfiles = payload;
+ },
+ initializeProfilePreferences: (
+ state,
+ action: PayloadAction<{
+ profileId: string;
+ defaultSlashCommands?: SlashCommandDescription[];
+ }>,
+ ) => {
+ const { profileId, defaultSlashCommands = [] } = action.payload;
+ const defaultSlashCommandNames = defaultSlashCommands.map(
+ (cmd) => cmd.name,
+ );
- if (payload) {
- for (const profile of payload) {
- ensureProfilePreferences(state, profile.id);
- }
+ // First ensure all profile preferences are complete to handle
+ // the case where a new preference has been added since last load
+ Object.keys(state.preferencesByProfileId).forEach((pid) => {
+ state.preferencesByProfileId[pid] = {
+ ...INITIAL_PREFERENCES_STATE,
+ ...state.preferencesByProfileId[pid],
+ };
+ });
+
+ // Then initialize preferences for the new profile if needed
+ if (!state.preferencesByProfileId[profileId]) {
+ state.preferencesByProfileId[profileId] = {
+ bookmarkedSlashCommands: defaultSlashCommandNames.slice(
+ 0,
+ DEFAULT_SLASH_COMMANDS_BOOKMARKS_COUNT,
+ ),
+ };
}
},
bookmarkSlashCommand: (
@@ -46,16 +73,15 @@ export const profilesSlice = createSlice({
action: PayloadAction<{ commandName: string }>,
) => {
const { commandName } = action.payload;
- const profileId = state.selectedProfileId;
+ const preferences =
+ state.preferencesByProfileId[state.selectedProfileId ?? ""];
- if (!profileId) return;
+ if (!preferences) return;
- const bookmarks = state.preferencesByProfileId[profileId].bookmarksByName;
- if (!bookmarks.includes(commandName)) {
- bookmarks.push(commandName);
+ if (!preferences.bookmarkedSlashCommands.includes(commandName)) {
+ preferences.bookmarkedSlashCommands.push(commandName);
}
},
-
unbookmarkSlashCommand: (
state,
action: PayloadAction<{ commandName: string }>,
@@ -66,9 +92,12 @@ export const profilesSlice = createSlice({
if (!profileId) return;
const preferences = state.preferencesByProfileId[profileId];
- preferences.bookmarksByName = preferences.bookmarksByName.filter(
- (cmd) => cmd !== commandName,
- );
+ if (!preferences) return;
+
+ preferences.bookmarkedSlashCommands =
+ preferences.bookmarkedSlashCommands.filter(
+ (cmd) => cmd !== commandName,
+ );
},
},
selectors: {
@@ -85,7 +114,7 @@ export const profilesSlice = createSlice({
selectBookmarkedSlashCommands: (state) => {
if (!state.selectedProfileId) return [];
const preferences = state.preferencesByProfileId[state.selectedProfileId];
- return preferences?.bookmarksByName || [];
+ return preferences?.bookmarkedSlashCommands || [];
},
selectPreferencesByProfileId: (state) => state.preferencesByProfileId,
@@ -97,6 +126,7 @@ export const {
setSelectedProfile,
bookmarkSlashCommand,
unbookmarkSlashCommand,
+ initializeProfilePreferences,
} = profilesSlice.actions;
export const {
diff --git a/gui/src/redux/slices/profiles/thunks.ts b/gui/src/redux/slices/profiles/thunks.ts
index 7ca1094903..585e7cd2b4 100644
--- a/gui/src/redux/slices/profiles/thunks.ts
+++ b/gui/src/redux/slices/profiles/thunks.ts
@@ -1,7 +1,12 @@
import { createAsyncThunk } from "@reduxjs/toolkit";
import { ProfileDescription } from "core/config/ConfigHandler";
+import { isDeprecatedCommandName } from "../../../components/ConversationStarters/utils";
import { ThunkApiType } from "../../store";
-import { setAvailableProfiles, setSelectedProfile } from "./slice";
+import {
+ initializeProfilePreferences,
+ setAvailableProfiles,
+ setSelectedProfile,
+} from "./slice";
export const selectProfileThunk = createAsyncThunk<
void,
@@ -39,6 +44,7 @@ export const selectProfileThunk = createAsyncThunk<
// Only update if there's a change
if ((newId ?? null) !== (initialId ?? null)) {
dispatch(setSelectedProfile(newId));
+
extra.ideMessenger.post("didChangeSelectedProfile", {
id: newId,
});
@@ -83,3 +89,26 @@ export const updateProfilesThunk = createAsyncThunk<
// This will trigger reselection if needed
dispatch(selectProfileThunk(selectedProfileId));
});
+
+export const initializeProfilePreferencesThunk = createAsyncThunk<
+ void,
+ { profileId: string },
+ ThunkApiType
+>(
+ "profiles/initializeProfilePreferences",
+ async (data, { getState, dispatch }) => {
+ const { profileId } = data;
+ const state = getState();
+
+ // Get slash commands from config
+ const defaultSlashCommands =
+ state.config.config.slashCommands.filter(isDeprecatedCommandName) ?? [];
+
+ dispatch(
+ initializeProfilePreferences({
+ defaultSlashCommands,
+ profileId,
+ }),
+ );
+ },
+);
diff --git a/gui/src/redux/slices/profiles/utils.ts b/gui/src/redux/slices/profiles/utils.ts
deleted file mode 100644
index 58da69942d..0000000000
--- a/gui/src/redux/slices/profiles/utils.ts
+++ /dev/null
@@ -1,12 +0,0 @@
-import { ProfilesState } from "./slice";
-
-export function ensureProfilePreferences(
- state: ProfilesState,
- profileId: string,
-) {
- if (!state.preferencesByProfileId[profileId]) {
- state.preferencesByProfileId[profileId] = {
- bookmarksByName: [],
- };
- }
-}
diff --git a/gui/src/redux/store.ts b/gui/src/redux/store.ts
index 6d6c11b2b3..8aff8e1ded 100644
--- a/gui/src/redux/store.ts
+++ b/gui/src/redux/store.ts
@@ -62,12 +62,8 @@ const saveSubsetFilters = [
createFilter("ui", ["toolSettings", "toolGroupSettings"]),
createFilter("indexing", []),
createFilter("tabs", ["tabs"]),
- // Add this new filter for the profiles slice
- createFilter("profiles", [
- "preferencesByProfileId",
- "selectedOrganizationId",
- "selectedProfileId",
- ]),
+ createFilter("organizations", ["selectedOrganizationId"]),
+ createFilter("profiles", ["preferencesByProfileId", "selectedProfileId"]),
];
const migrations: MigrationManifest = {