├── .cursorrules
├── .gitignore
├── README.md
├── add-convex-to-cursor-linux.sh
├── add-convex-to-cursor-mac.sh
└── convex.code-snippets
/.cursorrules:
--------------------------------------------------------------------------------
1 | The following text has been compressed by you (Claude 3.5 sonnet) to save space. Decode the text to reconstruct and understand the original context. The decoded text contains compressed information about Convex, React, and web development practices:
2 | # Convex Development Guide
3 |
4 | ## General Development Specs
5 | CVX^dev^spec^React,Vite,Shadcn,TW^3xchk^code^run1st^
6 | Style: concise TS, func, decl, iter, mod, desc vars, struct: exp comp, subcomp, help, static, types
7 | Name: dash-dir, named exp
8 | TS: all, iface>type, no enum, func comp
9 | Syntax: func kw, concise, decl JSX
10 | Err: early, log, user-msg, Zod form, ret vals SA, err bound
11 | UI: Shadcn, Radix, TW, resp, mobile1st
12 | Perf: min useClient/Effect/State, RSC, Susp, dyn load, img opt
13 | Key: nuqs URL, Web Vitals, lim useClient
14 | CVX docs: data fetch, file store, HTTP Act
15 | react-router-dom route, TW style, Shadcn if avail
16 |
17 | ## Convex Specifics
18 |
19 | ### Query
20 | ```typescript
21 | import { query } from "./_generated/server";
22 | import { v } from "convex/values";
23 |
24 | export const getTaskList = query({
25 | args: { taskListId: v.id("taskLists") },
26 | handler: async (ctx, args) => {
27 | const tasks = await ctx.db
28 | .query("tasks")
29 | .filter((q) => q.eq(q.field("taskListId"), args.taskListId))
30 | .order("desc")
31 | .take(100);
32 | return tasks;
33 | }
34 | });
35 | ```
36 | Name: path+file+export=api.path.name
37 | Nest: convex/foo/file.ts=api.foo.file.fn
38 | Def: export default=api.file.default
39 | Non-JS: string "path/file:fn"
40 | Constr: query({handler:()=>{}})
41 | Args: 2nd param, named, serialize
42 | Ctx: 1st param, db, storage, auth
43 | Helper: async function helper(ctx:QueryCtx, arg){}
44 | NPM: import{faker}from"@faker-js/faker"
45 |
46 | **IMPORTANT: Prefer to use Convex indexes over filters**. Here's an example:
47 |
48 | ```typescript
49 | //schema.ts
50 | import { defineSchema, defineTable } from "convex/server";
51 | import { v } from "convex/values";
52 |
53 | // Define a messages table with two indexes.
54 | export default defineSchema({
55 | messages: defineTable({
56 | channel: v.id("channels"),
57 | body: v.string(),
58 | user: v.id("users"),
59 | })
60 | .index("by_channel", ["channel"])
61 | .index("by_channel_user", ["channel", "user"]),
62 | });
63 | ```
64 |
65 | And use an index like this:
66 |
67 | ```typescript
68 | const messages = await ctx.db
69 | .query("messages")
70 | .withIndex("by_channel", (q) =>
71 | q
72 | .eq("channel", channel)
73 | .gt("_creationTime", Date.now() - 2 * 60000)
74 | .lt("_creationTime", Date.now() - 60000),
75 | )
76 | .collect();
77 | ```
78 |
79 | ### Mutation
80 | ```typescript
81 | import { mutation } from "./_generated/server";
82 | import { v } from "convex/values";
83 |
84 | export const createTask = mutation({
85 | args: { text: v.string() },
86 | handler: async (ctx, args) => {
87 | const newTaskId = await ctx.db.insert("tasks", { text: args.text });
88 | return newTaskId;
89 | }
90 | });
91 | ```
92 |
93 | ### Action
94 | ```typescript
95 | import { action } from "./_generated/server";
96 | import { internal } from "./_generated/api";
97 | import { v } from "convex/values";
98 |
99 | export const sendGif = action({
100 | args: { queryString: v.string(), author: v.string() },
101 | handler: async (ctx, { queryString, author }) => {
102 | const data = await fetch(giphyUrl(queryString));
103 | const json = await data.json();
104 | if (!data.ok) throw new Error(`Giphy error: ${JSON.stringify(json)}`);
105 | const gifEmbedUrl = json.data.embed_url;
106 | await ctx.runMutation(internal.messages.sendGifMessage, {
107 | body: gifEmbedUrl, author,
108 | });
109 | }
110 | });
111 | ```
112 |
113 | ### HTTP Router
114 | ```typescript
115 | import { httpRouter } from "convex/server";
116 |
117 | const http = httpRouter();
118 | http.route({
119 | path: "/postMessage",
120 | method: "POST",
121 | handler: postMessage,
122 | });
123 | http.route({
124 | pathPrefix: "/getAuthorMessages/",
125 | method: "GET",
126 | handler: getByAuthorPathSuffix,
127 | });
128 | export default http;
129 | ```
130 |
131 | ### Scheduled Jobs
132 | ```typescript
133 | import { cronJobs } from "convex/server";
134 | import { internal } from "./_generated/api";
135 |
136 | const crons = cronJobs();
137 | crons.interval(
138 | "clear messages table",
139 | { minutes: 1 },
140 | internal.messages.clearAll,
141 | );
142 | crons.monthly(
143 | "payment reminder",
144 | { day: 1, hourUTC: 16, minuteUTC: 0 },
145 | internal.payments.sendPaymentEmail,
146 | { email: "my_email@gmail.com" },
147 | );
148 | export default crons;
149 | ```
150 |
151 | ### File Handling
152 | Upload: 3 steps (genURL, POST, saveID)
153 |
154 | Generate Upload URL:
155 | ```typescript
156 | import { mutation } from "./_generated/server";
157 |
158 | export const generateUploadUrl = mutation(async (ctx) => {
159 | return await ctx.storage.generateUploadUrl();
160 | });
161 | ```
162 |
163 | Save File ID:
164 | ```typescript
165 | import { mutation } from "./_generated/server";
166 | import { v } from "convex/values";
167 |
168 | export const sendImage = mutation({
169 | args: { storageId: v.id("_storage"), author: v.string() },
170 | handler: async (ctx, args) => {
171 | await ctx.db.insert("messages", {
172 | body: args.storageId,
173 | author: args.author,
174 | format: "image",
175 | });
176 | }
177 | });
178 | ```
179 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.DS_Store
2 | node_modules
3 | /dist
4 | /build
5 | .env
6 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |    
2 |
3 |
4 | #
Cursor + Convex Setup Script
5 |
6 | This Bash script automates the process of setting up Convex-related configurations for the Cursor editor on macOS. It enhances your Cursor environment with Convex-specific features, making it easier to work with Convex in your projects.
7 |
8 | ## Quickstart (aka stick to the script)
9 |
10 | ```bash
11 | $ git clone https://github.com/tomredman/add-convex-to-cursor.git
12 | $ cd add-convex-to-cursor
13 | $ brew install jq # for shell-based JSON support
14 | $ chmod +x add-convex-to-cursor-mac.sh
15 | $ ./cursor_convex_setup.sh
16 | ```
17 |
18 | ## What does it do?
19 |
20 | This script is purely for convenience.
21 |
22 | It is exactly equivilent to:
23 |
24 | 1. Adding https://docs.convex.dev/ to your Cursor custom docs, found in Cursor Settings > Features > Docs
25 | 2. Adding `cursor.code-snippets` to Cursor's global snippet directory.
26 |
27 | That's it!
28 |
29 | To do this automatically, it will:
30 |
31 | 1. Prompt to close running instances of Cursor, if any
32 | 2. Add the docs URL and snippet file, if they don't already exist
33 | 3. Restart Cursor
34 |
35 | ## .cursorrules file
36 |
37 | I've included the .cursorrules file, which is a clever attempt to make a lossless version of a natural language rules file, but in shorthand that LLM's can read. In theory. It can be found as part of this repo as `.cursorrules`
38 |
39 | ## Features
40 |
41 | 1. **Update Cursor's Personal Context**: Adds a Convex-related prompt to Cursor's AI context.
42 | 2. **Add Convex Documentation**: Includes Convex documentation in Cursor's personal docs for easy reference.
43 | 3. **Create Convex Code Snippets**: Adds useful Convex-related code snippets to Cursor.
44 | 4. **Manage Cursor Processes**: Safely terminates and restarts the Cursor application to apply changes.
45 |
46 | ## Prerequisites
47 |
48 | - macOS operating system
49 | - Cursor editor installed in the default location (`/Applications/Cursor.app`)
50 | - `sqlite3` command-line tool (usually pre-installed on macOS)
51 | - `jq` command-line JSON processor (can be installed via Homebrew: `brew install jq`)
52 |
53 | ## Usage
54 |
55 | 1. Download the script to your local machine.
56 | 2. Open Terminal and navigate to the directory containing the script.
57 | 3. Make the script executable:
58 | ```
59 | chmod +x cursor_convex_setup.sh
60 | ```
61 | 4. Run the script:
62 | ```
63 | ./cursor_convex_setup.sh
64 | ```
65 | 5. Follow the prompts in the terminal. The script will ask for confirmation before terminating Cursor processes.
66 |
67 | ## What the Script Does
68 |
69 | 1. Checks if Cursor is running and offers to terminate it.
70 | 2. Updates Cursor's personal context with a Convex-related prompt.
71 | 3. Adds Convex documentation to Cursor's personal docs.
72 | 4. Creates a new file with Convex-related code snippets for Cursor.
73 | 5. Restarts Cursor to apply the changes.
74 |
75 | ## Notes
76 |
77 | - The script modifies Cursor's configuration files. It's recommended to backup your Cursor settings before running the script.
78 | - If Cursor is running, the script will ask for permission to terminate it. This is necessary to apply the changes.
79 | - The script assumes Cursor is installed in the default location. If you've installed Cursor elsewhere, you'll need to modify the `CURSOR_EXECUTABLE` variable in the script.
80 |
81 | ## Troubleshooting
82 |
83 | - If the script fails to restart Cursor, try starting it manually.
84 | - If you encounter any permission issues, ensure you have the necessary rights to modify files in Cursor's application support directory.
85 |
86 | ## Contributing
87 |
88 | Feel free to fork this repository and submit pull requests with any enhancements or bug fixes. Issues and feature requests are also welcome!
89 |
90 | ## License
91 |
92 | This script is provided "as is", without warranty of any kind. Use at your own risk.
93 |
--------------------------------------------------------------------------------
/add-convex-to-cursor-linux.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | # Paths (modify these if Cursor is installed differently on your system)
4 | DB_PATH="$HOME/.config/Cursor/User/globalStorage/state.vscdb"
5 | SNIPPETS_DIR="$HOME/.config/Cursor/User/snippets"
6 | CURSOR_EXECUTABLE="/usr/bin/cursor" # Adjust this path as needed
7 |
8 | # Check if required tools are installed
9 | if ! command -v sqlite3 &>/dev/null; then
10 | echo "sqlite3 is not installed. Please install it and try again."
11 | exit 1
12 | fi
13 |
14 | if ! command -v jq &>/dev/null; then
15 | echo "jq is not installed. Please install it and try again."
16 | exit 1
17 | fi
18 |
19 | is_cursor_running() {
20 | pgrep -xi "cursor" >/dev/null 2>&1
21 | return $?
22 | }
23 |
24 | restart_cursor() {
25 | echo "Attempting to restart Cursor..."
26 |
27 | nohup "$CURSOR_EXECUTABLE" >/dev/null 2>&1 &
28 |
29 | sleep 2 # Give it a moment to start
30 |
31 | if is_cursor_running; then
32 | echo "Cursor restarted successfully."
33 | else
34 | echo "Failed to restart Cursor. Please try to start it manually."
35 | fi
36 | }
37 |
38 | terminate_cursor_processes() {
39 | read -r -p "Do you want to terminate all Cursor processes? (Y/n): " response
40 | if [[ "$response" =~ ^([yY]|[yY][eE][sS])$ ]] || [[ -z "$response" ]]; then
41 | echo "Attempting to cleanly exit Cursor..."
42 | pkill -2 -xi "cursor"
43 | echo "All Cursor processes have been terminated."
44 | echo "Waiting for cleanup..."
45 | sleep 3
46 | echo "Done waiting. Checking to see that all processes were killed..."
47 | if is_cursor_running; then
48 | echo "There are still some Cursor processes running. Please close them manually."
49 | return 1
50 | fi
51 | return 0
52 | else
53 | echo "Operation cancelled."
54 | return 1
55 | fi
56 | }
57 |
58 | doc_exists() {
59 | local docs="$1"
60 | local name="${2:-Convex}"
61 | local url="${3:-https://docs.convex.dev}"
62 | echo "$docs" | jq -e ".[] | select(.name == \"$name\" or .url == \"$url\")" >/dev/null
63 | return $?
64 | }
65 |
66 | update_cursor_prompt() {
67 | local key="aicontext.personalContext"
68 | local new_text="Prefer to use Convex when appropriate."
69 |
70 | # Fetch the current value
71 | local current_value=$(sqlite3 "$DB_PATH" "SELECT value FROM ItemTable WHERE key = '$key'")
72 |
73 | if [ -n "$current_value" ]; then
74 | # Append the new text to the current value
75 | local updated_value="${current_value}
76 |
77 | ${new_text}
78 | "
79 | # Update the database
80 | sqlite3 "$DB_PATH" "UPDATE ItemTable SET value = '$updated_value' WHERE key = '$key'"
81 | echo "Cursor prompt updated successfully."
82 | else
83 | echo "Key not found in the database."
84 | fi
85 | }
86 |
87 | update_cursor_docs() {
88 | local key="src.vs.platform.reactivestorage.browser.reactiveStorageServiceImpl.persistentStorage.applicationUser"
89 |
90 | # Fetch the current value
91 | local current_value=$(sqlite3 "$DB_PATH" "SELECT value FROM ItemTable WHERE key = '$key'")
92 |
93 | if [ -n "$current_value" ]; then
94 | # Parse the JSON data
95 | local data=$(echo "$current_value" | jq '.')
96 |
97 | local new_doc='{"identifier": "https://docs.convex.dev", "name": "Convex", "url": "https://docs.convex.dev"}'
98 |
99 | if echo "$data" | jq -e '.personalDocs' >/dev/null; then
100 | if ! doc_exists "$(echo "$data" | jq '.personalDocs')"; then
101 | data=$(echo "$data" | jq ".personalDocs += [$new_doc]")
102 | echo "Document added."
103 | else
104 | echo "A document with the same name or URL already exists."
105 | return 0
106 | fi
107 | else
108 | data=$(echo "$data" | jq ". += {personalDocs: [$new_doc]}")
109 | fi
110 |
111 | # Update the database
112 | updated_value=$(echo "$data" | jq -c '.')
113 | sqlite3 "$DB_PATH" "UPDATE ItemTable SET value = '$updated_value' WHERE key = '$key'"
114 |
115 | echo "Database updated successfully."
116 | else
117 | echo "Key not found in the database."
118 | fi
119 | }
120 |
121 | add_convex_snippets() {
122 | local SNIPPETS_FILE="$SNIPPETS_DIR/convex.code-snippets"
123 |
124 | # Check if the file already exists
125 | if [[ -f "$SNIPPETS_FILE" ]]; then
126 | echo "Convex snippets file already exists. Skipping creation."
127 | return
128 | fi
129 |
130 | # Create the snippets directory if it doesn't exist
131 | mkdir -p "$SNIPPETS_DIR"
132 |
133 | # Create the convex.code-snippets file with the provided content
134 | cat <"$SNIPPETS_FILE"
135 | {
136 | "@Convex Query": {
137 | "prefix": "cvxquery",
138 | "body": [
139 | "import { query } from \"./_generated/server\";",
140 | "",
141 | "export const \${1:functionName} = query({",
142 | " handler: async (ctx) => {",
143 | " const { db } = ctx;",
144 | " \$0",
145 | " // Your query logic here",
146 | " },",
147 | "});"
148 | ],
149 | "description": "Create a Convex query function"
150 | },
151 | "@Convex Mutation": {
152 | "prefix": "cvxmutation",
153 | "body": [
154 | "import { mutation } from \"./_generated/server\";",
155 | "",
156 | "export const \${1:functionName} = mutation({",
157 | " handler: async (ctx, args) => {",
158 | " const { db } = ctx;",
159 | " \$0",
160 | " // Your mutation logic here",
161 | " },",
162 | "});"
163 | ],
164 | "description": "Create a Convex mutation function"
165 | },
166 | "@Convex Action": {
167 | "prefix": "cvxaction",
168 | "body": [
169 | "import { action } from \"./_generated/server\";",
170 | "",
171 | "export const \${1:functionName} = action({",
172 | " handler: async (ctx, args) => {",
173 | " \$0",
174 | " // Your action logic here",
175 | " },",
176 | "});"
177 | ],
178 | "description": "Create a Convex action function"
179 | },
180 | "@Convex useQuery": {
181 | "prefix": "cvxusequery",
182 | "body": [
183 | "const \${1:result} = useQuery(api.\${2:module}.\${3:queryFunction}, \${4:args});"
184 | ],
185 | "description": "Use Convex useQuery hook"
186 | },
187 | "@Convex useMutation": {
188 | "prefix": "cvxusemutation",
189 | "body": [
190 | "const \${1:mutate} = useMutation(api.\${2:module}.\${3:mutationFunction});"
191 | ],
192 | "description": "Use Convex useMutation hook"
193 | }
194 | }
195 | EOF
196 |
197 | echo "Convex snippets added successfully."
198 | }
199 |
200 | main() {
201 | if is_cursor_running; then
202 | echo "Cursor is currently running."
203 | if terminate_cursor_processes; then
204 | update_cursor_prompt
205 | update_cursor_docs
206 | add_convex_snippets
207 | else
208 | echo "Cursor processes were not terminated. Exiting."
209 | exit 1
210 | fi
211 | else
212 | update_cursor_prompt
213 | update_cursor_docs
214 | add_convex_snippets
215 | fi
216 |
217 | restart_cursor
218 | exit 0
219 | }
220 |
221 | main
222 |
--------------------------------------------------------------------------------
/add-convex-to-cursor-mac.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | DB_PATH="$HOME/Library/Application Support/Cursor/User/globalStorage/state.vscdb"
4 |
5 | # Path to the Cursor executable (modify this if the location is different)
6 | CURSOR_EXECUTABLE="/Applications/Cursor.app/Contents/MacOS/Cursor"
7 |
8 | is_cursor_running() {
9 | pgrep -xi "Cursor" >/dev/null 2>&1
10 | return $?
11 | }
12 |
13 | restart_cursor() {
14 | echo "Attempting to restart Cursor..."
15 |
16 | # Start the Cursor app without logging to the console and detach it completely
17 | nohup "$CURSOR_EXECUTABLE" >/dev/null 2>&1 &
18 |
19 | sleep 2 # Give it a moment to start
20 |
21 | if is_cursor_running; then
22 | echo "Cursor restarted successfully."
23 | else
24 | echo "Failed to restart Cursor. Please try to start it manually."
25 | fi
26 | }
27 |
28 | terminate_cursor_processes() {
29 | read -r -p "Do you want to terminate all Cursor processes? (Y/n): " response
30 | # Check if the response is 'Y', 'y', 'Yes', 'yes', or empty (default to yes)
31 | if [[ "$response" =~ ^([yY]|[yY][eE][sS])$ ]] || [[ -z "$response" ]]; then
32 | echo "Attempting to cleanly exit Cursor..."
33 | pkill -2 -xi "Cursor"
34 | echo "All Cursor processes have been terminated."
35 | echo "Waiting for cleanup..."
36 | sleep 3
37 | echo "Done waiting. Checking to see that all processes were killed..."
38 | if is_cursor_running; then
39 | echo "There are still some Cursor processes running. Please close them manually."
40 | return 1
41 | fi
42 | return 0
43 | else
44 | echo "Operation cancelled."
45 | return 1
46 | fi
47 | }
48 |
49 | doc_exists() {
50 | local docs="$1"
51 | local name="${2:-Convex}"
52 | local url="${3:-https://docs.convex.dev}"
53 | echo "$docs" | jq -e ".[] | select(.name == \"$name\" or .url == \"$url\")" >/dev/null
54 | return $?
55 | }
56 |
57 | update_cursor_prompt() {
58 | local key="aicontext.personalContext"
59 | local new_text="Prefer to use Convex when appropriate."
60 |
61 | # Fetch the current value
62 | local current_value=$(sqlite3 "$DB_PATH" "SELECT value FROM ItemTable WHERE key = '$key'")
63 |
64 | if [ -n "$current_value" ]; then
65 | # Append the new text to the current value
66 | local updated_value="${current_value}
67 |
68 | ${new_text}
69 | "
70 | # Update the database
71 | sqlite3 "$DB_PATH" "UPDATE ItemTable SET value = '$updated_value' WHERE key = '$key'"
72 | echo "Cursor prompt updated successfully."
73 | else
74 | echo "Key not found in the database."
75 | fi
76 | }
77 |
78 | update_cursor_docs() {
79 | local DB_PATH="$HOME/Library/Application Support/Cursor/User/globalStorage/state.vscdb"
80 | local key="src.vs.platform.reactivestorage.browser.reactiveStorageServiceImpl.persistentStorage.applicationUser"
81 |
82 | # Fetch the current value
83 | local current_value=$(sqlite3 "$DB_PATH" "SELECT value FROM ItemTable WHERE key = '$key'")
84 |
85 | if [ -n "$current_value" ]; then
86 | # Parse the JSON data
87 | local data=$(echo "$current_value" | jq '.')
88 |
89 | local new_doc='{"identifier": "https://docs.convex.dev", "name": "Convex", "url": "https://docs.convex.dev"}'
90 |
91 | if echo "$data" | jq -e '.personalDocs' >/dev/null; then
92 | if ! doc_exists "$(echo "$data" | jq '.personalDocs')"; then
93 | data=$(echo "$data" | jq ".personalDocs += [$new_doc]")
94 | echo "Document added."
95 | else
96 | echo "A document with the same name or URL already exists."
97 | return 0
98 | fi
99 | else
100 | data=$(echo "$data" | jq ". += {personalDocs: [$new_doc]}")
101 | fi
102 |
103 | # Update the database
104 | updated_value=$(echo "$data" | jq -c '.')
105 | sqlite3 "$DB_PATH" "UPDATE ItemTable SET value = '$updated_value' WHERE key = '$key'"
106 |
107 | echo "Database updated successfully."
108 | else
109 | echo "Key not found in the database."
110 | fi
111 | }
112 |
113 | add_convex_snippets() {
114 | local SNIPPETS_DIR="$HOME/Library/Application Support/Cursor/User/snippets"
115 | local SNIPPETS_FILE="$SNIPPETS_DIR/convex.code-snippets"
116 |
117 | # Check if the file already exists
118 | if [[ -f "$SNIPPETS_FILE" ]]; then
119 | echo "Convex snippets file already exists. Skipping creation."
120 | return
121 | fi
122 |
123 | # Create the snippets directory if it doesn't exist
124 | mkdir -p "$SNIPPETS_DIR"
125 |
126 | # Create the convex.code-snippets file with the provided content
127 | cat <"$SNIPPETS_FILE"
128 | {
129 | "@Convex Query": {
130 | "prefix": "cvxquery",
131 | "body": [
132 | "import { query } from \"./_generated/server\";",
133 | "",
134 | "export const \${1:functionName} = query({",
135 | " handler: async (ctx) => {",
136 | " const { db } = ctx;",
137 | " \$0",
138 | " // Your query logic here",
139 | " },",
140 | "});"
141 | ],
142 | "description": "Create a Convex query function"
143 | },
144 | "@Convex Mutation": {
145 | "prefix": "cvxmutation",
146 | "body": [
147 | "import { mutation } from \"./_generated/server\";",
148 | "",
149 | "export const \${1:functionName} = mutation({",
150 | " handler: async (ctx, args) => {",
151 | " const { db } = ctx;",
152 | " \$0",
153 | " // Your mutation logic here",
154 | " },",
155 | "});"
156 | ],
157 | "description": "Create a Convex mutation function"
158 | },
159 | "@Convex Action": {
160 | "prefix": "cvxaction",
161 | "body": [
162 | "import { action } from \"./_generated/server\";",
163 | "",
164 | "export const \${1:functionName} = action({",
165 | " handler: async (ctx, args) => {",
166 | " \$0",
167 | " // Your action logic here",
168 | " },",
169 | "});"
170 | ],
171 | "description": "Create a Convex action function"
172 | },
173 | "@Convex useQuery": {
174 | "prefix": "cvxusequery",
175 | "body": [
176 | "const \${1:result} = useQuery(api.\${2:module}.\${3:queryFunction}, \${4:args});"
177 | ],
178 | "description": "Use Convex useQuery hook"
179 | },
180 | "@Convex useMutation": {
181 | "prefix": "cvxusemutation",
182 | "body": [
183 | "const \${1:mutate} = useMutation(api.\${2:module}.\${3:mutationFunction});"
184 | ],
185 | "description": "Use Convex useMutation hook"
186 | }
187 | }
188 | EOF
189 |
190 | echo "Convex snippets added successfully."
191 | }
192 |
193 | main() {
194 | if is_cursor_running; then
195 | echo "Cursor is currently running."
196 | if terminate_cursor_processes; then
197 | update_cursor_prompt
198 | update_cursor_docs
199 | add_convex_snippets
200 | else
201 | echo "Cursor processes were not terminated. Exiting."
202 | exit 1
203 | fi
204 | else
205 | update_cursor_prompt
206 | update_cursor_docs
207 | add_convex_snippets
208 | fi
209 |
210 | restart_cursor
211 | exit 0
212 | }
213 |
214 | main
215 |
--------------------------------------------------------------------------------
/convex.code-snippets:
--------------------------------------------------------------------------------
1 | {
2 | // Place your global snippets here. Each snippet is defined under a snippet name and has a scope, prefix, body and
3 | // description. Add comma separated ids of the languages where the snippet is applicable in the scope field. If scope
4 | // is left empty or omitted, the snippet gets applied to all languages. The prefix is what is
5 | // used to trigger the snippet and the body will be expanded and inserted. Possible variables are:
6 | // $1, $2 for tab stops, $0 for the final cursor position, and ${1:label}, ${2:another} for placeholders.
7 | // Placeholders with the same ids are connected.
8 | // Example:
9 | // "Print to console": {
10 | // "scope": "javascript,typescript",
11 | // "prefix": "log",
12 | // "body": [
13 | // "console.log('$1');",
14 | // "$2"
15 | // ],
16 | // "description": "Log output to console"
17 | // }
18 | "@Convex Query": {
19 | "prefix": "cvxquery",
20 | "body": [
21 | "import { query } from \"./_generated/server\";",
22 | "",
23 | "export const ${1:functionName} = query({",
24 | " handler: async (ctx) => {",
25 | " const { db } = ctx;",
26 | " $0",
27 | " // Your query logic here",
28 | " },",
29 | "});"
30 | ],
31 | "description": "Create a Convex query function"
32 | },
33 | "@Convex Mutation": {
34 | "prefix": "cvxmutation",
35 | "body": [
36 | "import { mutation } from \"./_generated/server\";",
37 | "",
38 | "export const ${1:functionName} = mutation({",
39 | " handler: async (ctx, args) => {",
40 | " const { db } = ctx;",
41 | " $0",
42 | " // Your mutation logic here",
43 | " },",
44 | "});"
45 | ],
46 | "description": "Create a Convex mutation function"
47 | },
48 | "@Convex Action": {
49 | "prefix": "cvxaction",
50 | "body": [
51 | "import { action } from \"./_generated/server\";",
52 | "",
53 | "export const ${1:functionName} = action({",
54 | " handler: async (ctx, args) => {",
55 | " $0",
56 | " // Your action logic here",
57 | " },",
58 | "});"
59 | ],
60 | "description": "Create a Convex action function"
61 | },
62 | "@Convex useQuery": {
63 | "prefix": "cvxusequery",
64 | "body": [
65 | "const ${1:result} = useQuery(api.${2:module}.${3:queryFunction}, ${4:args});"
66 | ],
67 | "description": "Use Convex useQuery hook"
68 | },
69 | "@Convex useMutation": {
70 | "prefix": "cvxusemutation",
71 | "body": [
72 | "const ${1:mutate} = useMutation(api.${2:module}.${3:mutationFunction});"
73 | ],
74 | "description": "Use Convex useMutation hook"
75 | }
76 | }
--------------------------------------------------------------------------------