├── .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 | ![Static Badge](https://img.shields.io/badge/platform-convex-orange) ![Static Badge](https://img.shields.io/badge/script-bash-green) ![Static Badge](https://img.shields.io/badge/version-0.1-yellow) ![Static Badge](https://img.shields.io/badge/contributions-accepting-green) 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 | } --------------------------------------------------------------------------------