-
Notifications
You must be signed in to change notification settings - Fork 5.1k
Add tikz extension #24361
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Add tikz extension #24361
Conversation
- fixed icon - Add MIT License to the project - new extension
|
Congratulations on your new Raycast extension! 🚀 Due to our current reduced availability, the initial review may take up to 10-15 business days. Once the PR is approved and merged, the extension will be available on our Store. |
Greptile OverviewGreptile SummaryThis PR adds a new TikZ extension for generating LaTeX diagrams, providing both an AI tool for Raycast AI chat and a manual command with a form interface. What ChangedNew Extension Added:
Core Functionality:
Issues FoundCritical Issues:
Best Practice Violations:
Implementation QualityThe core logic is well-structured with proper error handling, comprehensive documentation, and thorough testing utilities. The LaTeX compilation approach is sound with fallback mechanisms. However, the issues above need to be resolved before this can be merged. Confidence Score: 2/5
Important Files ChangedFile Analysis
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
8 files reviewed, 8 comments
| @@ -0,0 +1,195 @@ | |||
| import { environment, showToast, Toast } from "@raycast/api"; | |||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This tool is missing the required export const confirmation function that other tools in the codebase implement. Without this, users won't get a confirmation dialog before the tool executes, which is important for operations that create files.
Looking at similar tools like app-creator/src/tools/create.ts, you should add:
| import { environment, showToast, Toast } from "@raycast/api"; | |
| import { environment, showToast, Toast, Tool } from "@raycast/api"; |
And add at the end of the file:
export const confirmation: Tool.Confirmation<Input> = async (input: Input) => {
return {
message: `Generate TikZ diagram${input.fileName ? ` "${input.fileName}"` : ""}?\n\`\`\`latex\n${input.tikzCode.slice(0, 200)}${input.tikzCode.length > 200 ? "..." : ""}\n\`\`\``,
};
};| @@ -0,0 +1,6 @@ | |||
| const { defineConfig } = require("eslint/config"); | |||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
eslint/config does not export defineConfig. This import path is incorrect for ESLint v9. Looking at the repository pattern used in other recently added extensions, the correct import should be from @eslint/js.
| const { defineConfig } = require("eslint/config"); | |
| const { defineConfig } = require("@eslint/js"); |
However, checking other extensions in this codebase, most don't use defineConfig at all - they directly export the config array. Here's the simpler pattern used elsewhere:
| const { defineConfig } = require("eslint/config"); | |
| const raycastConfig = require("@raycast/eslint-config"); | |
| module.exports = raycastConfig; |
|
|
||
| // Generate unique filename | ||
| const timestamp = Date.now(); | ||
| const baseName = input.fileName?.replace(/[^a-z0-9]/gi, "_") || "diagram"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Potential security issue: The fileName parameter is sanitized with a simple regex replacement, but it's not validated before being used in file operations. If fileName is undefined or becomes an empty string after sanitization, this could lead to file path issues.
The expression input.fileName?.replace(/[^a-z0-9]/gi, "_") returns undefined if fileName is not provided, but the || "diagram" fallback only applies if the value is falsy. This is working correctly, but could be clearer.
More importantly, the sanitized filename is not validated for length or for becoming an empty string after sanitization (e.g., if someone passes only special characters like "!@#$%"). Consider adding validation:
| const baseName = input.fileName?.replace(/[^a-z0-9]/gi, "_") || "diagram"; | |
| const baseName = (input.fileName?.replace(/[^a-z0-9]/gi, "_").substring(0, 100) || "diagram") || "diagram"; |
|
|
||
| // Generate filename | ||
| const timestamp = Date.now(); | ||
| const baseName = values.fileName?.trim().replace(/[^a-z0-9]/gi, "_") || "diagram"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same filename sanitization issue as in the tool file. The regex replacement could result in an empty string if only special characters are provided, though the || "diagram" fallback handles it. Consider adding explicit validation and length limiting for consistency:
| const baseName = values.fileName?.trim().replace(/[^a-z0-9]/gi, "_") || "diagram"; | |
| const baseName = (values.fileName?.trim().replace(/[^a-z0-9]/gi, "_").substring(0, 100) || "diagram") || "diagram"; |
Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!
| "platforms": [ | ||
| "macOS" | ||
| ], | ||
| "categories": [ | ||
| "Design Tools", | ||
| "Data", | ||
| "Media", | ||
| "Fun", | ||
| "Developer Tools", | ||
| "Web" | ||
| ], |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This extension has a view-type command ("create-tikz" with "mode": "view") and therefore requires a metadata/ folder with Raycast-styled screenshots.
The metadata folder is missing. You need to add a metadata/ directory with at least one screenshot showing the command UI in action. See other extensions like app-creator/metadata/ for examples.
| if (existsSync(pdfFilePath)) unlinkSync(pdfFilePath); | ||
| if (existsSync(texFilePath)) unlinkSync(texFilePath); | ||
| if (existsSync(logFilePath)) unlinkSync(logFilePath); | ||
| if (existsSync(auxFilePath)) unlinkSync(auxFilePath); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Using unlinkSync to delete files goes against best practices. You should use Raycast's built-in trash() method instead, which is safer and cross-platform compatible.
| if (existsSync(pdfFilePath)) unlinkSync(pdfFilePath); | |
| if (existsSync(texFilePath)) unlinkSync(texFilePath); | |
| if (existsSync(logFilePath)) unlinkSync(logFilePath); | |
| if (existsSync(auxFilePath)) unlinkSync(auxFilePath); | |
| import { trash } from "@raycast/api"; | |
| // Then in the cleanup section: | |
| if (existsSync(pdfFilePath)) await trash(pdfFilePath); | |
| if (existsSync(texFilePath)) await trash(texFilePath); | |
| if (existsSync(logFilePath)) await trash(logFilePath); | |
| if (existsSync(auxFilePath)) await trash(auxFilePath); |
Note: This will require changing the cleanup block to be async or moving it to a separate async function.
Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!
| if (existsSync(pdfFilePath)) unlinkSync(pdfFilePath); | ||
| if (existsSync(texFilePath)) unlinkSync(texFilePath); | ||
| if (existsSync(logFilePath)) unlinkSync(logFilePath); | ||
| if (existsSync(auxFilePath)) unlinkSync(auxFilePath); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Using unlinkSync to delete files goes against best practices. You should use Raycast's built-in trash() method instead.
| if (existsSync(pdfFilePath)) unlinkSync(pdfFilePath); | |
| if (existsSync(texFilePath)) unlinkSync(texFilePath); | |
| if (existsSync(logFilePath)) unlinkSync(logFilePath); | |
| if (existsSync(auxFilePath)) unlinkSync(auxFilePath); | |
| import { trash } from "@raycast/api"; | |
| // Then in the cleanup section: | |
| if (existsSync(pdfFilePath)) await trash(pdfFilePath); | |
| if (existsSync(texFilePath)) await trash(texFilePath); | |
| if (existsSync(logFilePath)) await trash(logFilePath); | |
| if (existsSync(auxFilePath)) await trash(auxFilePath); |
Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!
| @@ -0,0 +1,3 @@ | |||
| # TikZ Changelog | |||
|
|
|||
| ## [Initial Version] - {PR_MERGE_DATE} No newline at end of file | |||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The CHANGELOG is incomplete and needs to document the actual changes being introduced. Currently it just has a placeholder {PR_MERGE_DATE}.
For an initial version, you should at minimum describe what the extension does:
| ## [Initial Version] - {PR_MERGE_DATE} | |
| ## [Initial Version] - 2025-01-XX | |
| ### Added | |
| - AI tool for generating TikZ diagrams from LaTeX code | |
| - Manual command for creating TikZ diagrams with form interface | |
| - Support for PDF and PNG output formats | |
| - Automatic LaTeX document wrapper handling | |
| - Common TikZ libraries pre-configured |
Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!
Description
You can now use the AI chat to generate math images of anything. It uses the TikZ (LaTeX derivative) syntax to generate math images with high accuracy.
Screencast
Checklist
npm run buildand tested this distribution build in Raycastassetsfolder are used by the extension itselfREADMEare placed outside of themetadatafolder