mirror of
https://github.com/snapotter-hq/SnapOtter.git
synced 2026-08-03 07:46:42 +02:00
feat(modality)!: SnapOtter 2.0 phase 3 modality framework: media/doc engines, pool routing, display modes (#218)
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
import { IMAGE_INPUTS } from "./modality.js";
|
||||
import type { CategoryInfo, SocialMediaPreset, Tool } from "./types.js";
|
||||
|
||||
export const CATEGORIES: CategoryInfo[] = [
|
||||
@@ -20,6 +21,9 @@ export const TOOLS: Tool[] = [
|
||||
category: "essentials",
|
||||
icon: "Maximize2",
|
||||
route: "/resize",
|
||||
modality: "image",
|
||||
acceptedInputs: IMAGE_INPUTS,
|
||||
executionHint: "fast",
|
||||
},
|
||||
{
|
||||
id: "crop",
|
||||
@@ -28,6 +32,9 @@ export const TOOLS: Tool[] = [
|
||||
category: "essentials",
|
||||
icon: "Crop",
|
||||
route: "/crop",
|
||||
modality: "image",
|
||||
acceptedInputs: IMAGE_INPUTS,
|
||||
executionHint: "fast",
|
||||
},
|
||||
{
|
||||
id: "rotate",
|
||||
@@ -36,6 +43,9 @@ export const TOOLS: Tool[] = [
|
||||
category: "essentials",
|
||||
icon: "RotateCw",
|
||||
route: "/rotate",
|
||||
modality: "image",
|
||||
acceptedInputs: IMAGE_INPUTS,
|
||||
executionHint: "fast",
|
||||
},
|
||||
{
|
||||
id: "convert",
|
||||
@@ -44,6 +54,9 @@ export const TOOLS: Tool[] = [
|
||||
category: "essentials",
|
||||
icon: "FileOutput",
|
||||
route: "/convert",
|
||||
modality: "image",
|
||||
acceptedInputs: IMAGE_INPUTS,
|
||||
executionHint: "fast",
|
||||
},
|
||||
{
|
||||
id: "compress",
|
||||
@@ -52,6 +65,9 @@ export const TOOLS: Tool[] = [
|
||||
category: "essentials",
|
||||
icon: "Minimize2",
|
||||
route: "/compress",
|
||||
modality: "image",
|
||||
acceptedInputs: IMAGE_INPUTS,
|
||||
executionHint: "fast",
|
||||
},
|
||||
// Optimization
|
||||
{
|
||||
@@ -62,6 +78,9 @@ export const TOOLS: Tool[] = [
|
||||
category: "optimization",
|
||||
icon: "Globe",
|
||||
route: "/optimize-for-web",
|
||||
modality: "image",
|
||||
acceptedInputs: IMAGE_INPUTS,
|
||||
executionHint: "fast",
|
||||
},
|
||||
{
|
||||
id: "strip-metadata",
|
||||
@@ -70,6 +89,9 @@ export const TOOLS: Tool[] = [
|
||||
category: "optimization",
|
||||
icon: "ShieldOff",
|
||||
route: "/strip-metadata",
|
||||
modality: "image",
|
||||
acceptedInputs: IMAGE_INPUTS,
|
||||
executionHint: "fast",
|
||||
},
|
||||
{
|
||||
id: "edit-metadata",
|
||||
@@ -78,6 +100,9 @@ export const TOOLS: Tool[] = [
|
||||
category: "optimization",
|
||||
icon: "PenLine",
|
||||
route: "/edit-metadata",
|
||||
modality: "image",
|
||||
acceptedInputs: IMAGE_INPUTS,
|
||||
executionHint: "fast",
|
||||
},
|
||||
{
|
||||
id: "bulk-rename",
|
||||
@@ -86,6 +111,9 @@ export const TOOLS: Tool[] = [
|
||||
category: "optimization",
|
||||
icon: "FilePen",
|
||||
route: "/bulk-rename",
|
||||
modality: "image",
|
||||
acceptedInputs: IMAGE_INPUTS,
|
||||
executionHint: "fast",
|
||||
},
|
||||
{
|
||||
id: "image-to-pdf",
|
||||
@@ -94,6 +122,9 @@ export const TOOLS: Tool[] = [
|
||||
category: "optimization",
|
||||
icon: "FileText",
|
||||
route: "/image-to-pdf",
|
||||
modality: "image",
|
||||
acceptedInputs: IMAGE_INPUTS,
|
||||
executionHint: "fast",
|
||||
},
|
||||
{
|
||||
id: "favicon",
|
||||
@@ -102,6 +133,9 @@ export const TOOLS: Tool[] = [
|
||||
category: "optimization",
|
||||
icon: "AppWindow",
|
||||
route: "/favicon",
|
||||
modality: "image",
|
||||
acceptedInputs: IMAGE_INPUTS,
|
||||
executionHint: "fast",
|
||||
},
|
||||
// Adjustments
|
||||
{
|
||||
@@ -111,6 +145,9 @@ export const TOOLS: Tool[] = [
|
||||
category: "adjustments",
|
||||
icon: "SlidersHorizontal",
|
||||
route: "/adjust-colors",
|
||||
modality: "image",
|
||||
acceptedInputs: IMAGE_INPUTS,
|
||||
executionHint: "fast",
|
||||
},
|
||||
{
|
||||
id: "sharpening",
|
||||
@@ -119,6 +156,9 @@ export const TOOLS: Tool[] = [
|
||||
category: "adjustments",
|
||||
icon: "Focus",
|
||||
route: "/sharpening",
|
||||
modality: "image",
|
||||
acceptedInputs: IMAGE_INPUTS,
|
||||
executionHint: "fast",
|
||||
},
|
||||
{
|
||||
id: "replace-color",
|
||||
@@ -127,6 +167,9 @@ export const TOOLS: Tool[] = [
|
||||
category: "adjustments",
|
||||
icon: "Pipette",
|
||||
route: "/replace-color",
|
||||
modality: "image",
|
||||
acceptedInputs: IMAGE_INPUTS,
|
||||
executionHint: "fast",
|
||||
},
|
||||
{
|
||||
id: "color-blindness",
|
||||
@@ -135,6 +178,9 @@ export const TOOLS: Tool[] = [
|
||||
category: "adjustments",
|
||||
icon: "Eye",
|
||||
route: "/color-blindness",
|
||||
modality: "image",
|
||||
acceptedInputs: IMAGE_INPUTS,
|
||||
executionHint: "fast",
|
||||
},
|
||||
// AI Tools
|
||||
{
|
||||
@@ -144,6 +190,9 @@ export const TOOLS: Tool[] = [
|
||||
category: "ai",
|
||||
icon: "Eraser",
|
||||
route: "/remove-background",
|
||||
modality: "image",
|
||||
acceptedInputs: IMAGE_INPUTS,
|
||||
executionHint: "long",
|
||||
},
|
||||
{
|
||||
id: "upscale",
|
||||
@@ -152,6 +201,9 @@ export const TOOLS: Tool[] = [
|
||||
category: "ai",
|
||||
icon: "ZoomIn",
|
||||
route: "/upscale",
|
||||
modality: "image",
|
||||
acceptedInputs: IMAGE_INPUTS,
|
||||
executionHint: "long",
|
||||
},
|
||||
{
|
||||
id: "erase-object",
|
||||
@@ -160,6 +212,9 @@ export const TOOLS: Tool[] = [
|
||||
category: "ai",
|
||||
icon: "Wand",
|
||||
route: "/erase-object",
|
||||
modality: "image",
|
||||
acceptedInputs: IMAGE_INPUTS,
|
||||
executionHint: "long",
|
||||
},
|
||||
{
|
||||
id: "ocr",
|
||||
@@ -168,6 +223,9 @@ export const TOOLS: Tool[] = [
|
||||
category: "ai",
|
||||
icon: "ScanText",
|
||||
route: "/ocr",
|
||||
modality: "image",
|
||||
acceptedInputs: IMAGE_INPUTS,
|
||||
executionHint: "long",
|
||||
},
|
||||
{
|
||||
id: "blur-faces",
|
||||
@@ -176,6 +234,9 @@ export const TOOLS: Tool[] = [
|
||||
category: "ai",
|
||||
icon: "EyeOff",
|
||||
route: "/blur-faces",
|
||||
modality: "image",
|
||||
acceptedInputs: IMAGE_INPUTS,
|
||||
executionHint: "long",
|
||||
},
|
||||
{
|
||||
id: "smart-crop",
|
||||
@@ -184,6 +245,9 @@ export const TOOLS: Tool[] = [
|
||||
category: "ai",
|
||||
icon: "Crosshair",
|
||||
route: "/smart-crop",
|
||||
modality: "image",
|
||||
acceptedInputs: IMAGE_INPUTS,
|
||||
executionHint: "long",
|
||||
},
|
||||
{
|
||||
id: "image-enhancement",
|
||||
@@ -192,6 +256,9 @@ export const TOOLS: Tool[] = [
|
||||
category: "ai",
|
||||
icon: "Sparkles",
|
||||
route: "/image-enhancement",
|
||||
modality: "image",
|
||||
acceptedInputs: IMAGE_INPUTS,
|
||||
executionHint: "fast", // pure sharp/CV; optional deepEnhance defers to noise-removal
|
||||
},
|
||||
{
|
||||
id: "enhance-faces",
|
||||
@@ -200,6 +267,9 @@ export const TOOLS: Tool[] = [
|
||||
category: "ai",
|
||||
icon: "ScanFace",
|
||||
route: "/enhance-faces",
|
||||
modality: "image",
|
||||
acceptedInputs: IMAGE_INPUTS,
|
||||
executionHint: "long",
|
||||
},
|
||||
{
|
||||
id: "colorize",
|
||||
@@ -208,6 +278,9 @@ export const TOOLS: Tool[] = [
|
||||
category: "ai",
|
||||
icon: "Palette",
|
||||
route: "/colorize",
|
||||
modality: "image",
|
||||
acceptedInputs: IMAGE_INPUTS,
|
||||
executionHint: "long",
|
||||
},
|
||||
{
|
||||
id: "noise-removal",
|
||||
@@ -216,6 +289,9 @@ export const TOOLS: Tool[] = [
|
||||
category: "ai",
|
||||
icon: "AudioLines",
|
||||
route: "/noise-removal",
|
||||
modality: "image",
|
||||
acceptedInputs: IMAGE_INPUTS,
|
||||
executionHint: "long",
|
||||
},
|
||||
{
|
||||
id: "red-eye-removal",
|
||||
@@ -224,6 +300,9 @@ export const TOOLS: Tool[] = [
|
||||
category: "ai",
|
||||
icon: "ScanEye",
|
||||
route: "/red-eye-removal",
|
||||
modality: "image",
|
||||
acceptedInputs: IMAGE_INPUTS,
|
||||
executionHint: "long",
|
||||
},
|
||||
{
|
||||
id: "restore-photo",
|
||||
@@ -232,6 +311,9 @@ export const TOOLS: Tool[] = [
|
||||
category: "ai",
|
||||
icon: "Undo2",
|
||||
route: "/restore-photo",
|
||||
modality: "image",
|
||||
acceptedInputs: IMAGE_INPUTS,
|
||||
executionHint: "long",
|
||||
},
|
||||
{
|
||||
id: "passport-photo",
|
||||
@@ -240,6 +322,9 @@ export const TOOLS: Tool[] = [
|
||||
category: "ai",
|
||||
icon: "UserCheck",
|
||||
route: "/passport-photo",
|
||||
modality: "image",
|
||||
acceptedInputs: IMAGE_INPUTS,
|
||||
executionHint: "long",
|
||||
},
|
||||
{
|
||||
id: "content-aware-resize",
|
||||
@@ -248,6 +333,9 @@ export const TOOLS: Tool[] = [
|
||||
category: "ai",
|
||||
icon: "Scaling",
|
||||
route: "/content-aware-resize",
|
||||
modality: "image",
|
||||
acceptedInputs: IMAGE_INPUTS,
|
||||
executionHint: "long",
|
||||
},
|
||||
{
|
||||
id: "ai-canvas-expand",
|
||||
@@ -256,6 +344,9 @@ export const TOOLS: Tool[] = [
|
||||
category: "ai",
|
||||
icon: "Expand",
|
||||
route: "/ai-canvas-expand",
|
||||
modality: "image",
|
||||
acceptedInputs: IMAGE_INPUTS,
|
||||
executionHint: "long",
|
||||
},
|
||||
{
|
||||
id: "transparency-fixer",
|
||||
@@ -264,6 +355,9 @@ export const TOOLS: Tool[] = [
|
||||
category: "ai",
|
||||
icon: "ShieldCheck",
|
||||
route: "/transparency-fixer",
|
||||
modality: "image",
|
||||
acceptedInputs: IMAGE_INPUTS,
|
||||
executionHint: "long",
|
||||
},
|
||||
// Watermark & Overlay
|
||||
{
|
||||
@@ -273,6 +367,9 @@ export const TOOLS: Tool[] = [
|
||||
category: "watermark",
|
||||
icon: "Type",
|
||||
route: "/watermark-text",
|
||||
modality: "image",
|
||||
acceptedInputs: IMAGE_INPUTS,
|
||||
executionHint: "fast",
|
||||
},
|
||||
{
|
||||
id: "watermark-image",
|
||||
@@ -281,6 +378,9 @@ export const TOOLS: Tool[] = [
|
||||
category: "watermark",
|
||||
icon: "Image",
|
||||
route: "/watermark-image",
|
||||
modality: "image",
|
||||
acceptedInputs: IMAGE_INPUTS,
|
||||
executionHint: "fast",
|
||||
},
|
||||
{
|
||||
id: "text-overlay",
|
||||
@@ -289,6 +389,9 @@ export const TOOLS: Tool[] = [
|
||||
category: "watermark",
|
||||
icon: "TextCursorInput",
|
||||
route: "/text-overlay",
|
||||
modality: "image",
|
||||
acceptedInputs: IMAGE_INPUTS,
|
||||
executionHint: "fast",
|
||||
},
|
||||
{
|
||||
id: "compose",
|
||||
@@ -297,6 +400,9 @@ export const TOOLS: Tool[] = [
|
||||
category: "watermark",
|
||||
icon: "Layers",
|
||||
route: "/compose",
|
||||
modality: "image",
|
||||
acceptedInputs: IMAGE_INPUTS,
|
||||
executionHint: "fast",
|
||||
},
|
||||
{
|
||||
id: "meme-generator",
|
||||
@@ -305,6 +411,9 @@ export const TOOLS: Tool[] = [
|
||||
category: "watermark",
|
||||
icon: "Laugh",
|
||||
route: "/meme-generator",
|
||||
modality: "image",
|
||||
acceptedInputs: IMAGE_INPUTS,
|
||||
executionHint: "fast",
|
||||
},
|
||||
// Utilities
|
||||
{
|
||||
@@ -314,6 +423,9 @@ export const TOOLS: Tool[] = [
|
||||
category: "utilities",
|
||||
icon: "Info",
|
||||
route: "/info",
|
||||
modality: "image",
|
||||
acceptedInputs: IMAGE_INPUTS,
|
||||
executionHint: "fast",
|
||||
},
|
||||
{
|
||||
id: "compare",
|
||||
@@ -322,6 +434,9 @@ export const TOOLS: Tool[] = [
|
||||
category: "utilities",
|
||||
icon: "Columns2",
|
||||
route: "/compare",
|
||||
modality: "image",
|
||||
acceptedInputs: IMAGE_INPUTS,
|
||||
executionHint: "fast",
|
||||
},
|
||||
{
|
||||
id: "find-duplicates",
|
||||
@@ -330,6 +445,9 @@ export const TOOLS: Tool[] = [
|
||||
category: "utilities",
|
||||
icon: "Copy",
|
||||
route: "/find-duplicates",
|
||||
modality: "image",
|
||||
acceptedInputs: IMAGE_INPUTS,
|
||||
executionHint: "fast",
|
||||
},
|
||||
{
|
||||
id: "color-palette",
|
||||
@@ -338,6 +456,9 @@ export const TOOLS: Tool[] = [
|
||||
category: "utilities",
|
||||
icon: "Droplets",
|
||||
route: "/color-palette",
|
||||
modality: "image",
|
||||
acceptedInputs: IMAGE_INPUTS,
|
||||
executionHint: "fast",
|
||||
},
|
||||
{
|
||||
id: "qr-generate",
|
||||
@@ -346,6 +467,9 @@ export const TOOLS: Tool[] = [
|
||||
category: "utilities",
|
||||
icon: "QrCode",
|
||||
route: "/qr-generate",
|
||||
modality: "image",
|
||||
acceptedInputs: IMAGE_INPUTS,
|
||||
executionHint: "fast",
|
||||
},
|
||||
{
|
||||
id: "html-to-image",
|
||||
@@ -354,6 +478,9 @@ export const TOOLS: Tool[] = [
|
||||
category: "utilities",
|
||||
icon: "Globe",
|
||||
route: "/html-to-image",
|
||||
modality: "image",
|
||||
acceptedInputs: IMAGE_INPUTS,
|
||||
executionHint: "fast",
|
||||
},
|
||||
{
|
||||
id: "barcode-read",
|
||||
@@ -362,6 +489,9 @@ export const TOOLS: Tool[] = [
|
||||
category: "utilities",
|
||||
icon: "ScanLine",
|
||||
route: "/barcode-read",
|
||||
modality: "image",
|
||||
acceptedInputs: IMAGE_INPUTS,
|
||||
executionHint: "fast",
|
||||
},
|
||||
{
|
||||
id: "image-to-base64",
|
||||
@@ -370,6 +500,9 @@ export const TOOLS: Tool[] = [
|
||||
category: "utilities",
|
||||
icon: "Code",
|
||||
route: "/image-to-base64",
|
||||
modality: "image",
|
||||
acceptedInputs: IMAGE_INPUTS,
|
||||
executionHint: "fast",
|
||||
},
|
||||
// Layout & Composition
|
||||
{
|
||||
@@ -379,6 +512,9 @@ export const TOOLS: Tool[] = [
|
||||
category: "layout",
|
||||
icon: "LayoutGrid",
|
||||
route: "/collage",
|
||||
modality: "image",
|
||||
acceptedInputs: IMAGE_INPUTS,
|
||||
executionHint: "fast",
|
||||
},
|
||||
{
|
||||
id: "stitch",
|
||||
@@ -387,6 +523,9 @@ export const TOOLS: Tool[] = [
|
||||
category: "layout",
|
||||
icon: "Columns2",
|
||||
route: "/stitch",
|
||||
modality: "image",
|
||||
acceptedInputs: IMAGE_INPUTS,
|
||||
executionHint: "fast",
|
||||
},
|
||||
{
|
||||
id: "split",
|
||||
@@ -395,6 +534,9 @@ export const TOOLS: Tool[] = [
|
||||
category: "layout",
|
||||
icon: "Grid3x3",
|
||||
route: "/split",
|
||||
modality: "image",
|
||||
acceptedInputs: IMAGE_INPUTS,
|
||||
executionHint: "fast",
|
||||
},
|
||||
{
|
||||
id: "border",
|
||||
@@ -403,6 +545,9 @@ export const TOOLS: Tool[] = [
|
||||
category: "layout",
|
||||
icon: "Frame",
|
||||
route: "/border",
|
||||
modality: "image",
|
||||
acceptedInputs: IMAGE_INPUTS,
|
||||
executionHint: "fast",
|
||||
},
|
||||
{
|
||||
id: "beautify",
|
||||
@@ -411,6 +556,9 @@ export const TOOLS: Tool[] = [
|
||||
category: "layout",
|
||||
icon: "ImagePlus",
|
||||
route: "/beautify",
|
||||
modality: "image",
|
||||
acceptedInputs: IMAGE_INPUTS,
|
||||
executionHint: "fast",
|
||||
},
|
||||
// Format & Conversion
|
||||
{
|
||||
@@ -420,6 +568,9 @@ export const TOOLS: Tool[] = [
|
||||
category: "format",
|
||||
icon: "FileImage",
|
||||
route: "/svg-to-raster",
|
||||
modality: "image",
|
||||
acceptedInputs: [".svg", ".svgz"],
|
||||
executionHint: "fast",
|
||||
},
|
||||
{
|
||||
id: "vectorize",
|
||||
@@ -428,6 +579,9 @@ export const TOOLS: Tool[] = [
|
||||
category: "format",
|
||||
icon: "PenTool",
|
||||
route: "/vectorize",
|
||||
modality: "image",
|
||||
acceptedInputs: IMAGE_INPUTS,
|
||||
executionHint: "fast",
|
||||
},
|
||||
{
|
||||
id: "gif-tools",
|
||||
@@ -437,6 +591,9 @@ export const TOOLS: Tool[] = [
|
||||
category: "format",
|
||||
icon: "Film",
|
||||
route: "/gif-tools",
|
||||
modality: "image",
|
||||
acceptedInputs: IMAGE_INPUTS,
|
||||
executionHint: "fast",
|
||||
},
|
||||
{
|
||||
id: "pdf-to-image",
|
||||
@@ -445,6 +602,9 @@ export const TOOLS: Tool[] = [
|
||||
category: "format",
|
||||
icon: "BookImage",
|
||||
route: "/pdf-to-image",
|
||||
modality: "image",
|
||||
acceptedInputs: IMAGE_INPUTS,
|
||||
executionHint: "fast",
|
||||
},
|
||||
];
|
||||
|
||||
|
||||
@@ -49,6 +49,12 @@ export const ar: TranslationKeys = {
|
||||
format: "التنسيق والتحويل",
|
||||
ai: "أدوات AI",
|
||||
},
|
||||
modalities: {
|
||||
image: "Image",
|
||||
video: "Video",
|
||||
audio: "Audio",
|
||||
documentsAndFiles: "Documents & Files",
|
||||
},
|
||||
tools: {
|
||||
resize: {
|
||||
name: "تغيير الحجم",
|
||||
@@ -271,6 +277,14 @@ export const ar: TranslationKeys = {
|
||||
description: "ربط عدة أدوات في سير عمل واحد",
|
||||
},
|
||||
processing: { canceled: "Processing canceled" },
|
||||
mediaPlayer: {
|
||||
unsupported: "Your browser does not support this media format.",
|
||||
},
|
||||
documentView: {
|
||||
loadFailed: "Failed to load document.",
|
||||
previousPage: "Previous page",
|
||||
nextPage: "Next page",
|
||||
},
|
||||
},
|
||||
toolSettings: {
|
||||
compress: {
|
||||
|
||||
@@ -49,6 +49,12 @@ export const de: TranslationKeys = {
|
||||
format: "Format & Konvertierung",
|
||||
ai: "AI-Werkzeuge",
|
||||
},
|
||||
modalities: {
|
||||
image: "Image",
|
||||
video: "Video",
|
||||
audio: "Audio",
|
||||
documentsAndFiles: "Documents & Files",
|
||||
},
|
||||
tools: {
|
||||
resize: {
|
||||
name: "Groesse aendern",
|
||||
@@ -276,6 +282,14 @@ export const de: TranslationKeys = {
|
||||
description: "Mehrere Werkzeuge zu einem Workflow verketten",
|
||||
},
|
||||
processing: { canceled: "Processing canceled" },
|
||||
mediaPlayer: {
|
||||
unsupported: "Your browser does not support this media format.",
|
||||
},
|
||||
documentView: {
|
||||
loadFailed: "Failed to load document.",
|
||||
previousPage: "Previous page",
|
||||
nextPage: "Next page",
|
||||
},
|
||||
},
|
||||
toolSettings: {
|
||||
compress: {
|
||||
|
||||
@@ -47,6 +47,12 @@ export const en = {
|
||||
format: "Format & Conversion",
|
||||
ai: "AI Tools",
|
||||
},
|
||||
modalities: {
|
||||
image: "Image",
|
||||
video: "Video",
|
||||
audio: "Audio",
|
||||
documentsAndFiles: "Documents & Files",
|
||||
},
|
||||
tools: {
|
||||
resize: {
|
||||
name: "Resize",
|
||||
@@ -228,6 +234,14 @@ export const en = {
|
||||
},
|
||||
pipeline: { name: "Pipeline Builder", description: "Chain multiple tools into a workflow" },
|
||||
processing: { canceled: "Processing canceled" },
|
||||
mediaPlayer: {
|
||||
unsupported: "Your browser does not support this media format.",
|
||||
},
|
||||
documentView: {
|
||||
loadFailed: "Failed to load document.",
|
||||
previousPage: "Previous page",
|
||||
nextPage: "Next page",
|
||||
},
|
||||
},
|
||||
toolSettings: {
|
||||
compress: {
|
||||
|
||||
@@ -49,6 +49,12 @@ export const es: TranslationKeys = {
|
||||
format: "Formato y conversion",
|
||||
ai: "Herramientas de AI",
|
||||
},
|
||||
modalities: {
|
||||
image: "Image",
|
||||
video: "Video",
|
||||
audio: "Audio",
|
||||
documentsAndFiles: "Documents & Files",
|
||||
},
|
||||
tools: {
|
||||
resize: {
|
||||
name: "Redimensionar",
|
||||
@@ -261,6 +267,14 @@ export const es: TranslationKeys = {
|
||||
description: "Encadena multiples herramientas en un flujo de trabajo",
|
||||
},
|
||||
processing: { canceled: "Processing canceled" },
|
||||
mediaPlayer: {
|
||||
unsupported: "Your browser does not support this media format.",
|
||||
},
|
||||
documentView: {
|
||||
loadFailed: "Failed to load document.",
|
||||
previousPage: "Previous page",
|
||||
nextPage: "Next page",
|
||||
},
|
||||
},
|
||||
toolSettings: {
|
||||
compress: {
|
||||
|
||||
@@ -49,6 +49,12 @@ export const fr: TranslationKeys = {
|
||||
format: "Format et conversion",
|
||||
ai: "Outils AI",
|
||||
},
|
||||
modalities: {
|
||||
image: "Image",
|
||||
video: "Video",
|
||||
audio: "Audio",
|
||||
documentsAndFiles: "Documents & Files",
|
||||
},
|
||||
tools: {
|
||||
resize: {
|
||||
name: "Redimensionner",
|
||||
@@ -277,6 +283,14 @@ export const fr: TranslationKeys = {
|
||||
description: "Enchainez plusieurs outils dans un flux de travail",
|
||||
},
|
||||
processing: { canceled: "Processing canceled" },
|
||||
mediaPlayer: {
|
||||
unsupported: "Your browser does not support this media format.",
|
||||
},
|
||||
documentView: {
|
||||
loadFailed: "Failed to load document.",
|
||||
previousPage: "Previous page",
|
||||
nextPage: "Next page",
|
||||
},
|
||||
},
|
||||
toolSettings: {
|
||||
compress: {
|
||||
|
||||
@@ -49,6 +49,12 @@ export const hi: TranslationKeys = {
|
||||
format: "फॉर्मेट और कन्वर्शन",
|
||||
ai: "AI टूल्स",
|
||||
},
|
||||
modalities: {
|
||||
image: "Image",
|
||||
video: "Video",
|
||||
audio: "Audio",
|
||||
documentsAndFiles: "Documents & Files",
|
||||
},
|
||||
tools: {
|
||||
resize: {
|
||||
name: "रीसाइज़",
|
||||
@@ -268,6 +274,14 @@ export const hi: TranslationKeys = {
|
||||
description: "कई टूल्स को एक वर्कफ्लो में चेन करें",
|
||||
},
|
||||
processing: { canceled: "Processing canceled" },
|
||||
mediaPlayer: {
|
||||
unsupported: "Your browser does not support this media format.",
|
||||
},
|
||||
documentView: {
|
||||
loadFailed: "Failed to load document.",
|
||||
previousPage: "Previous page",
|
||||
nextPage: "Next page",
|
||||
},
|
||||
},
|
||||
toolSettings: {
|
||||
compress: {
|
||||
|
||||
@@ -49,6 +49,12 @@ export const id: TranslationKeys = {
|
||||
format: "Format & Konversi",
|
||||
ai: "Alat AI",
|
||||
},
|
||||
modalities: {
|
||||
image: "Image",
|
||||
video: "Video",
|
||||
audio: "Audio",
|
||||
documentsAndFiles: "Documents & Files",
|
||||
},
|
||||
tools: {
|
||||
resize: {
|
||||
name: "Ubah Ukuran",
|
||||
@@ -276,6 +282,14 @@ export const id: TranslationKeys = {
|
||||
description: "Rangkai beberapa alat menjadi alur kerja",
|
||||
},
|
||||
processing: { canceled: "Processing canceled" },
|
||||
mediaPlayer: {
|
||||
unsupported: "Your browser does not support this media format.",
|
||||
},
|
||||
documentView: {
|
||||
loadFailed: "Failed to load document.",
|
||||
previousPage: "Previous page",
|
||||
nextPage: "Next page",
|
||||
},
|
||||
},
|
||||
toolSettings: {
|
||||
compress: {
|
||||
|
||||
@@ -49,6 +49,12 @@ export const it: TranslationKeys = {
|
||||
format: "Formato e conversione",
|
||||
ai: "Strumenti IA",
|
||||
},
|
||||
modalities: {
|
||||
image: "Image",
|
||||
video: "Video",
|
||||
audio: "Audio",
|
||||
documentsAndFiles: "Documents & Files",
|
||||
},
|
||||
tools: {
|
||||
resize: {
|
||||
name: "Ridimensiona",
|
||||
@@ -275,6 +281,14 @@ export const it: TranslationKeys = {
|
||||
description: "Concatena piu strumenti in un flusso di lavoro",
|
||||
},
|
||||
processing: { canceled: "Processing canceled" },
|
||||
mediaPlayer: {
|
||||
unsupported: "Your browser does not support this media format.",
|
||||
},
|
||||
documentView: {
|
||||
loadFailed: "Failed to load document.",
|
||||
previousPage: "Previous page",
|
||||
nextPage: "Next page",
|
||||
},
|
||||
},
|
||||
toolSettings: {
|
||||
compress: {
|
||||
|
||||
@@ -49,6 +49,12 @@ export const ja: TranslationKeys = {
|
||||
format: "フォーマットと変換",
|
||||
ai: "AIツール",
|
||||
},
|
||||
modalities: {
|
||||
image: "Image",
|
||||
video: "Video",
|
||||
audio: "Audio",
|
||||
documentsAndFiles: "Documents & Files",
|
||||
},
|
||||
tools: {
|
||||
resize: {
|
||||
name: "リサイズ",
|
||||
@@ -236,6 +242,14 @@ export const ja: TranslationKeys = {
|
||||
},
|
||||
pipeline: { name: "Pipelineビルダー", description: "複数のツールをワークフローに連結" },
|
||||
processing: { canceled: "Processing canceled" },
|
||||
mediaPlayer: {
|
||||
unsupported: "Your browser does not support this media format.",
|
||||
},
|
||||
documentView: {
|
||||
loadFailed: "Failed to load document.",
|
||||
previousPage: "Previous page",
|
||||
nextPage: "Next page",
|
||||
},
|
||||
},
|
||||
toolSettings: {
|
||||
compress: {
|
||||
|
||||
@@ -49,6 +49,12 @@ export const ko: TranslationKeys = {
|
||||
format: "포맷 및 변환",
|
||||
ai: "AI 도구",
|
||||
},
|
||||
modalities: {
|
||||
image: "Image",
|
||||
video: "Video",
|
||||
audio: "Audio",
|
||||
documentsAndFiles: "Documents & Files",
|
||||
},
|
||||
tools: {
|
||||
resize: {
|
||||
name: "리사이즈",
|
||||
@@ -223,6 +229,14 @@ export const ko: TranslationKeys = {
|
||||
},
|
||||
pipeline: { name: "Pipeline 빌더", description: "여러 도구를 워크플로로 연결" },
|
||||
processing: { canceled: "Processing canceled" },
|
||||
mediaPlayer: {
|
||||
unsupported: "Your browser does not support this media format.",
|
||||
},
|
||||
documentView: {
|
||||
loadFailed: "Failed to load document.",
|
||||
previousPage: "Previous page",
|
||||
nextPage: "Next page",
|
||||
},
|
||||
},
|
||||
toolSettings: {
|
||||
compress: {
|
||||
|
||||
@@ -49,6 +49,12 @@ export const nl: TranslationKeys = {
|
||||
format: "Formaat & Conversie",
|
||||
ai: "AI-tools",
|
||||
},
|
||||
modalities: {
|
||||
image: "Image",
|
||||
video: "Video",
|
||||
audio: "Audio",
|
||||
documentsAndFiles: "Documents & Files",
|
||||
},
|
||||
tools: {
|
||||
resize: {
|
||||
name: "Formaat wijzigen",
|
||||
@@ -276,6 +282,14 @@ export const nl: TranslationKeys = {
|
||||
description: "Meerdere tools koppelen tot een workflow",
|
||||
},
|
||||
processing: { canceled: "Processing canceled" },
|
||||
mediaPlayer: {
|
||||
unsupported: "Your browser does not support this media format.",
|
||||
},
|
||||
documentView: {
|
||||
loadFailed: "Failed to load document.",
|
||||
previousPage: "Previous page",
|
||||
nextPage: "Next page",
|
||||
},
|
||||
},
|
||||
toolSettings: {
|
||||
compress: {
|
||||
|
||||
@@ -49,6 +49,12 @@ export const pl: TranslationKeys = {
|
||||
format: "Format i konwersja",
|
||||
ai: "Narzędzia AI",
|
||||
},
|
||||
modalities: {
|
||||
image: "Image",
|
||||
video: "Video",
|
||||
audio: "Audio",
|
||||
documentsAndFiles: "Documents & Files",
|
||||
},
|
||||
tools: {
|
||||
resize: {
|
||||
name: "Zmiana rozmiaru",
|
||||
@@ -277,6 +283,14 @@ export const pl: TranslationKeys = {
|
||||
description: "Łączenie wielu narzędzi w przepływ pracy",
|
||||
},
|
||||
processing: { canceled: "Processing canceled" },
|
||||
mediaPlayer: {
|
||||
unsupported: "Your browser does not support this media format.",
|
||||
},
|
||||
documentView: {
|
||||
loadFailed: "Failed to load document.",
|
||||
previousPage: "Previous page",
|
||||
nextPage: "Next page",
|
||||
},
|
||||
},
|
||||
toolSettings: {
|
||||
compress: {
|
||||
|
||||
@@ -49,6 +49,12 @@ export const ptBR: TranslationKeys = {
|
||||
format: "Formato e conversao",
|
||||
ai: "Ferramentas de AI",
|
||||
},
|
||||
modalities: {
|
||||
image: "Image",
|
||||
video: "Video",
|
||||
audio: "Audio",
|
||||
documentsAndFiles: "Documents & Files",
|
||||
},
|
||||
tools: {
|
||||
resize: {
|
||||
name: "Redimensionar",
|
||||
@@ -274,6 +280,14 @@ export const ptBR: TranslationKeys = {
|
||||
description: "Encadeie varias ferramentas em um fluxo de trabalho",
|
||||
},
|
||||
processing: { canceled: "Processing canceled" },
|
||||
mediaPlayer: {
|
||||
unsupported: "Your browser does not support this media format.",
|
||||
},
|
||||
documentView: {
|
||||
loadFailed: "Failed to load document.",
|
||||
previousPage: "Previous page",
|
||||
nextPage: "Next page",
|
||||
},
|
||||
},
|
||||
toolSettings: {
|
||||
compress: {
|
||||
|
||||
@@ -49,6 +49,12 @@ export const ru: TranslationKeys = {
|
||||
format: "Формат и конвертация",
|
||||
ai: "AI-инструменты",
|
||||
},
|
||||
modalities: {
|
||||
image: "Image",
|
||||
video: "Video",
|
||||
audio: "Audio",
|
||||
documentsAndFiles: "Documents & Files",
|
||||
},
|
||||
tools: {
|
||||
resize: {
|
||||
name: "Изменение размера",
|
||||
@@ -276,6 +282,14 @@ export const ru: TranslationKeys = {
|
||||
description: "Объединение нескольких инструментов в рабочий процесс",
|
||||
},
|
||||
processing: { canceled: "Processing canceled" },
|
||||
mediaPlayer: {
|
||||
unsupported: "Your browser does not support this media format.",
|
||||
},
|
||||
documentView: {
|
||||
loadFailed: "Failed to load document.",
|
||||
previousPage: "Previous page",
|
||||
nextPage: "Next page",
|
||||
},
|
||||
},
|
||||
toolSettings: {
|
||||
compress: {
|
||||
|
||||
@@ -49,6 +49,12 @@ export const sv: TranslationKeys = {
|
||||
format: "Format & Konvertering",
|
||||
ai: "AI-verktyg",
|
||||
},
|
||||
modalities: {
|
||||
image: "Image",
|
||||
video: "Video",
|
||||
audio: "Audio",
|
||||
documentsAndFiles: "Documents & Files",
|
||||
},
|
||||
tools: {
|
||||
resize: {
|
||||
name: "Andra storlek",
|
||||
@@ -274,6 +280,14 @@ export const sv: TranslationKeys = {
|
||||
description: "Kedja samman flera verktyg till ett arbetsflode",
|
||||
},
|
||||
processing: { canceled: "Processing canceled" },
|
||||
mediaPlayer: {
|
||||
unsupported: "Your browser does not support this media format.",
|
||||
},
|
||||
documentView: {
|
||||
loadFailed: "Failed to load document.",
|
||||
previousPage: "Previous page",
|
||||
nextPage: "Next page",
|
||||
},
|
||||
},
|
||||
toolSettings: {
|
||||
compress: {
|
||||
|
||||
@@ -49,6 +49,12 @@ export const th: TranslationKeys = {
|
||||
format: "รูปแบบและการแปลง",
|
||||
ai: "เครื่องมือ AI",
|
||||
},
|
||||
modalities: {
|
||||
image: "Image",
|
||||
video: "Video",
|
||||
audio: "Audio",
|
||||
documentsAndFiles: "Documents & Files",
|
||||
},
|
||||
tools: {
|
||||
resize: {
|
||||
name: "ปรับขนาด",
|
||||
@@ -269,6 +275,14 @@ export const th: TranslationKeys = {
|
||||
description: "เชื่อมต่อหลายเครื่องมือเป็นขั้นตอนทำงาน",
|
||||
},
|
||||
processing: { canceled: "Processing canceled" },
|
||||
mediaPlayer: {
|
||||
unsupported: "Your browser does not support this media format.",
|
||||
},
|
||||
documentView: {
|
||||
loadFailed: "Failed to load document.",
|
||||
previousPage: "Previous page",
|
||||
nextPage: "Next page",
|
||||
},
|
||||
},
|
||||
toolSettings: {
|
||||
compress: {
|
||||
|
||||
@@ -49,6 +49,12 @@ export const tr: TranslationKeys = {
|
||||
format: "Biçim ve Dönüştürme",
|
||||
ai: "AI Araçları",
|
||||
},
|
||||
modalities: {
|
||||
image: "Image",
|
||||
video: "Video",
|
||||
audio: "Audio",
|
||||
documentsAndFiles: "Documents & Files",
|
||||
},
|
||||
tools: {
|
||||
resize: {
|
||||
name: "Boyutlandır",
|
||||
@@ -277,6 +283,14 @@ export const tr: TranslationKeys = {
|
||||
description: "Birden fazla aracı bir iş akışında zincirleyin",
|
||||
},
|
||||
processing: { canceled: "Processing canceled" },
|
||||
mediaPlayer: {
|
||||
unsupported: "Your browser does not support this media format.",
|
||||
},
|
||||
documentView: {
|
||||
loadFailed: "Failed to load document.",
|
||||
previousPage: "Previous page",
|
||||
nextPage: "Next page",
|
||||
},
|
||||
},
|
||||
toolSettings: {
|
||||
compress: {
|
||||
|
||||
@@ -49,6 +49,12 @@ export const uk: TranslationKeys = {
|
||||
format: "Формат і конвертація",
|
||||
ai: "AI-інструменти",
|
||||
},
|
||||
modalities: {
|
||||
image: "Image",
|
||||
video: "Video",
|
||||
audio: "Audio",
|
||||
documentsAndFiles: "Documents & Files",
|
||||
},
|
||||
tools: {
|
||||
resize: {
|
||||
name: "Зміна розміру",
|
||||
@@ -276,6 +282,14 @@ export const uk: TranslationKeys = {
|
||||
description: "Об'єднання кількох інструментів у робочий процес",
|
||||
},
|
||||
processing: { canceled: "Processing canceled" },
|
||||
mediaPlayer: {
|
||||
unsupported: "Your browser does not support this media format.",
|
||||
},
|
||||
documentView: {
|
||||
loadFailed: "Failed to load document.",
|
||||
previousPage: "Previous page",
|
||||
nextPage: "Next page",
|
||||
},
|
||||
},
|
||||
toolSettings: {
|
||||
compress: {
|
||||
|
||||
@@ -49,6 +49,12 @@ export const vi: TranslationKeys = {
|
||||
format: "Định dạng & Chuyển đổi",
|
||||
ai: "Công cụ AI",
|
||||
},
|
||||
modalities: {
|
||||
image: "Image",
|
||||
video: "Video",
|
||||
audio: "Audio",
|
||||
documentsAndFiles: "Documents & Files",
|
||||
},
|
||||
tools: {
|
||||
resize: {
|
||||
name: "Thay đổi kích thước",
|
||||
@@ -277,6 +283,14 @@ export const vi: TranslationKeys = {
|
||||
description: "Kết nối nhiều công cụ thành một quy trình làm việc",
|
||||
},
|
||||
processing: { canceled: "Processing canceled" },
|
||||
mediaPlayer: {
|
||||
unsupported: "Your browser does not support this media format.",
|
||||
},
|
||||
documentView: {
|
||||
loadFailed: "Failed to load document.",
|
||||
previousPage: "Previous page",
|
||||
nextPage: "Next page",
|
||||
},
|
||||
},
|
||||
toolSettings: {
|
||||
compress: {
|
||||
|
||||
@@ -49,6 +49,12 @@ export const zhCN: TranslationKeys = {
|
||||
format: "格式与转换",
|
||||
ai: "AI 工具",
|
||||
},
|
||||
modalities: {
|
||||
image: "Image",
|
||||
video: "Video",
|
||||
audio: "Audio",
|
||||
documentsAndFiles: "Documents & Files",
|
||||
},
|
||||
tools: {
|
||||
resize: {
|
||||
name: "调整大小",
|
||||
@@ -223,6 +229,14 @@ export const zhCN: TranslationKeys = {
|
||||
},
|
||||
pipeline: { name: "Pipeline 构建器", description: "将多个工具串联为工作流" },
|
||||
processing: { canceled: "Processing canceled" },
|
||||
mediaPlayer: {
|
||||
unsupported: "Your browser does not support this media format.",
|
||||
},
|
||||
documentView: {
|
||||
loadFailed: "Failed to load document.",
|
||||
previousPage: "Previous page",
|
||||
nextPage: "Next page",
|
||||
},
|
||||
},
|
||||
toolSettings: {
|
||||
compress: {
|
||||
|
||||
@@ -49,6 +49,12 @@ export const zhTW: TranslationKeys = {
|
||||
format: "格式與轉換",
|
||||
ai: "AI工具",
|
||||
},
|
||||
modalities: {
|
||||
image: "Image",
|
||||
video: "Video",
|
||||
audio: "Audio",
|
||||
documentsAndFiles: "Documents & Files",
|
||||
},
|
||||
tools: {
|
||||
resize: {
|
||||
name: "調整大小",
|
||||
@@ -222,6 +228,14 @@ export const zhTW: TranslationKeys = {
|
||||
},
|
||||
pipeline: { name: "Pipeline建構器", description: "將多個工具串聯為工作流程" },
|
||||
processing: { canceled: "Processing canceled" },
|
||||
mediaPlayer: {
|
||||
unsupported: "Your browser does not support this media format.",
|
||||
},
|
||||
documentView: {
|
||||
loadFailed: "Failed to load document.",
|
||||
previousPage: "Previous page",
|
||||
nextPage: "Next page",
|
||||
},
|
||||
},
|
||||
toolSettings: {
|
||||
compress: {
|
||||
|
||||
@@ -4,5 +4,6 @@ export * from "./analytics/types.js";
|
||||
export * from "./constants.js";
|
||||
export * from "./features.js";
|
||||
export * from "./i18n/index.js";
|
||||
export * from "./modality.js";
|
||||
export * from "./permissions.js";
|
||||
export * from "./types.js";
|
||||
|
||||
@@ -0,0 +1,137 @@
|
||||
export type Modality = "image" | "video" | "audio" | "document" | "file";
|
||||
|
||||
export interface ModalityInfo {
|
||||
id: Modality;
|
||||
name: string;
|
||||
icon: string; // lucide icon name, same convention as CategoryInfo
|
||||
color: string;
|
||||
}
|
||||
|
||||
// Five modality values in code; "document" and "file" group as one
|
||||
// "Documents & Files" area in the UI (see spec 4.5).
|
||||
export const MODALITIES: ModalityInfo[] = [
|
||||
{ id: "image", name: "Image", icon: "Image", color: "#3B82F6" },
|
||||
{ id: "video", name: "Video", icon: "Video", color: "#EF4444" },
|
||||
{ id: "audio", name: "Audio", icon: "AudioLines", color: "#10B981" },
|
||||
{ id: "document", name: "Documents", icon: "FileText", color: "#8B5CF6" },
|
||||
{ id: "file", name: "Files", icon: "FileArchive", color: "#F59E0B" },
|
||||
];
|
||||
|
||||
// Which BullMQ pool a modality's tools run on. AI tools override to "ai"
|
||||
// at enqueue time regardless of modality.
|
||||
export const MODALITY_POOL: Record<Modality, "image" | "media" | "docs"> = {
|
||||
image: "image",
|
||||
video: "media",
|
||||
audio: "media",
|
||||
document: "docs",
|
||||
file: "docs",
|
||||
};
|
||||
|
||||
// Default accepted input extensions per modality (with dots; drives the
|
||||
// file picker accept attribute and docs). Tools may narrow this.
|
||||
export const IMAGE_INPUTS = [
|
||||
".jpg",
|
||||
".jpeg",
|
||||
".png",
|
||||
".webp",
|
||||
".gif",
|
||||
".bmp",
|
||||
".tiff",
|
||||
".tif",
|
||||
".avif",
|
||||
".heic",
|
||||
".heif",
|
||||
".svg",
|
||||
".svgz",
|
||||
".ico",
|
||||
".jxl",
|
||||
".jp2",
|
||||
".psd",
|
||||
".tga",
|
||||
".exr",
|
||||
".hdr",
|
||||
".dng",
|
||||
".cr2",
|
||||
".nef",
|
||||
".arw",
|
||||
".orf",
|
||||
".rw2",
|
||||
".ppm",
|
||||
".pgm",
|
||||
".pbm",
|
||||
".qoi",
|
||||
".dds",
|
||||
".fits",
|
||||
".dpx",
|
||||
".apng",
|
||||
".cur",
|
||||
".eps",
|
||||
];
|
||||
export const VIDEO_INPUTS = [
|
||||
".mp4",
|
||||
".mov",
|
||||
".webm",
|
||||
".mkv",
|
||||
".avi",
|
||||
".m4v",
|
||||
".mts",
|
||||
".m2ts",
|
||||
".3gp",
|
||||
".flv",
|
||||
".wmv",
|
||||
".mpg",
|
||||
".mpeg",
|
||||
".ts",
|
||||
".ogv",
|
||||
];
|
||||
export const AUDIO_INPUTS = [
|
||||
".mp3",
|
||||
".wav",
|
||||
".flac",
|
||||
".aac",
|
||||
".m4a",
|
||||
".ogg",
|
||||
".opus",
|
||||
".wma",
|
||||
".aiff",
|
||||
".amr",
|
||||
".ac3",
|
||||
];
|
||||
export const DOCUMENT_INPUTS = [
|
||||
".pdf",
|
||||
".docx",
|
||||
".doc",
|
||||
".xlsx",
|
||||
".xls",
|
||||
".pptx",
|
||||
".ppt",
|
||||
".odt",
|
||||
".ods",
|
||||
".odp",
|
||||
".rtf",
|
||||
".txt",
|
||||
".md",
|
||||
".html",
|
||||
".epub",
|
||||
".mobi",
|
||||
".azw3",
|
||||
];
|
||||
export const FILE_INPUTS = [".csv", ".json", ".xml", ".yaml", ".yml", ".zip"];
|
||||
|
||||
export function detectModalityFromMime(mime: string): Modality {
|
||||
if (mime.startsWith("image/")) return "image";
|
||||
if (mime.startsWith("video/")) return "video";
|
||||
if (mime.startsWith("audio/")) return "audio";
|
||||
if (
|
||||
mime === "application/pdf" ||
|
||||
mime.includes("officedocument") ||
|
||||
mime.includes("msword") ||
|
||||
mime.includes("ms-excel") ||
|
||||
mime.includes("ms-powerpoint") ||
|
||||
mime === "application/epub+zip" ||
|
||||
mime.startsWith("text/html")
|
||||
) {
|
||||
return "document";
|
||||
}
|
||||
return "file";
|
||||
}
|
||||
@@ -1,3 +1,5 @@
|
||||
import type { Modality } from "./modality.js";
|
||||
|
||||
export interface Tool {
|
||||
id: string;
|
||||
name: string;
|
||||
@@ -5,6 +7,10 @@ export interface Tool {
|
||||
category: ToolCategory;
|
||||
icon: string;
|
||||
route: string;
|
||||
modality: Modality;
|
||||
acceptedInputs: string[];
|
||||
executionHint: "fast" | "long";
|
||||
maxInputSizeMB?: number;
|
||||
shortcut?: string;
|
||||
disabled?: boolean;
|
||||
experimental?: boolean;
|
||||
|
||||
Reference in New Issue
Block a user