style: formatting

This commit is contained in:
ByteAtATime 2025-06-11 14:58:11 -07:00
parent 85c9595d15
commit fc7c835c66
38 changed files with 4202 additions and 2343 deletions

View file

@ -1 +1,8 @@
sidecar/plugin-host.js sidecar/plugin-host.js
sidecar/dist
src-tauri
package-lock.json
pnpm-lock.yaml
yarn.lock
bun.lock
bun.lockb

View file

@ -1,7 +1,3 @@
{ {
"recommendations": [ "recommendations": ["svelte.svelte-vscode", "tauri-apps.tauri-vscode", "rust-lang.rust-analyzer"]
"svelte.svelte-vscode",
"tauri-apps.tauri-vscode",
"rust-lang.rust-analyzer"
]
} }

2478
pnpm-lock.yaml generated

File diff suppressed because it is too large Load diff

768
sidecar/pnpm-lock.yaml generated

File diff suppressed because it is too large Load diff

View file

@ -1,4 +1,4 @@
declare module "*.txt" { declare module '*.txt' {
const content: string; const content: string;
export default content; export default content;
} }

View file

@ -1,4 +1,4 @@
import type { HostConfig } from "react-reconciler"; import type { HostConfig } from 'react-reconciler';
import type { import type {
ComponentType, ComponentType,
ComponentProps, ComponentProps,
@ -7,18 +7,18 @@ import type {
TextInstance, TextInstance,
UpdatePayload, UpdatePayload,
ParentInstance, ParentInstance,
AnyInstance, AnyInstance
} from "./types"; } from './types';
import { import {
instances, instances,
getNextInstanceId, getNextInstanceId,
commitBuffer, commitBuffer,
addToCommitBuffer, addToCommitBuffer,
clearCommitBuffer, clearCommitBuffer
} from "./state"; } from './state';
import { writeOutput } from "./io"; import { writeOutput } from './io';
import { serializeProps, optimizeCommitBuffer } from "./utils"; import { serializeProps, optimizeCommitBuffer } from './utils';
import React from "react"; import React from 'react';
const appendChildToParent = (parent: ParentInstance, child: AnyInstance) => { const appendChildToParent = (parent: ParentInstance, child: AnyInstance) => {
const existingIndex = parent.children.findIndex(({ id }) => id === child.id); const existingIndex = parent.children.findIndex(({ id }) => id === child.id);
@ -27,8 +27,8 @@ const appendChildToParent = (parent: ParentInstance, child: AnyInstance) => {
} }
parent.children.push(child); parent.children.push(child);
addToCommitBuffer({ addToCommitBuffer({
type: "APPEND_CHILD", type: 'APPEND_CHILD',
payload: { parentId: parent.id, childId: child.id }, payload: { parentId: parent.id, childId: child.id }
}); });
}; };
@ -42,18 +42,16 @@ const insertChildBefore = (
parent.children.splice(existingIndex, 1); parent.children.splice(existingIndex, 1);
} }
const beforeIndex = parent.children.findIndex( const beforeIndex = parent.children.findIndex(({ id }) => id === beforeChild.id);
({ id }) => id === beforeChild.id
);
if (beforeIndex !== -1) { if (beforeIndex !== -1) {
parent.children.splice(beforeIndex, 0, child); parent.children.splice(beforeIndex, 0, child);
addToCommitBuffer({ addToCommitBuffer({
type: "INSERT_BEFORE", type: 'INSERT_BEFORE',
payload: { payload: {
parentId: parent.id, parentId: parent.id,
childId: child.id, childId: child.id,
beforeId: beforeChild.id, beforeId: beforeChild.id
}, }
}); });
} else { } else {
appendChildToParent(parent, child); appendChildToParent(parent, child);
@ -63,8 +61,8 @@ const insertChildBefore = (
const removeChildFromParent = (parent: ParentInstance, child: AnyInstance) => { const removeChildFromParent = (parent: ParentInstance, child: AnyInstance) => {
parent.children = parent.children.filter(({ id }) => id !== child.id); parent.children = parent.children.filter(({ id }) => id !== child.id);
addToCommitBuffer({ addToCommitBuffer({
type: "REMOVE_CHILD", type: 'REMOVE_CHILD',
payload: { parentId: parent.id, childId: child.id }, payload: { parentId: parent.id, childId: child.id }
}); });
}; };
@ -99,8 +97,8 @@ export const hostConfig: HostConfig<
if (commitBuffer.length > 0) { if (commitBuffer.length > 0) {
const optimizedPayload = optimizeCommitBuffer(commitBuffer); const optimizedPayload = optimizeCommitBuffer(commitBuffer);
writeOutput({ writeOutput({
type: "BATCH_UPDATE", type: 'BATCH_UPDATE',
payload: optimizedPayload, payload: optimizedPayload
}); });
clearCommitBuffer(); clearCommitBuffer();
} }
@ -108,32 +106,30 @@ export const hostConfig: HostConfig<
createInstance(type, props, root, hostContext, internalInstanceHandle) { createInstance(type, props, root, hostContext, internalInstanceHandle) {
const componentType = const componentType =
typeof type === "string" typeof type === 'string' ? type : type.displayName || type.name || 'Anonymous';
? type
: type.displayName || type.name || "Anonymous";
const id = getNextInstanceId(); const id = getNextInstanceId();
const instance: RaycastInstance = { const instance: RaycastInstance = {
id, id,
type: componentType, type: componentType,
children: [], children: [],
props: serializeProps(props), props: serializeProps(props),
_internalFiber: internalInstanceHandle, _internalFiber: internalInstanceHandle
}; };
(internalInstanceHandle as any).stateNode = instance; (internalInstanceHandle as any).stateNode = instance;
instances.set(id, instance); instances.set(id, instance);
addToCommitBuffer({ addToCommitBuffer({
type: "CREATE_INSTANCE", type: 'CREATE_INSTANCE',
payload: { id, type: componentType, props: instance.props }, payload: { id, type: componentType, props: instance.props }
}); });
return instance; return instance;
}, },
createTextInstance(text) { createTextInstance(text) {
const id = getNextInstanceId(); const id = getNextInstanceId();
const instance: TextInstance = { id, type: "TEXT", text }; const instance: TextInstance = { id, type: 'TEXT', text };
instances.set(id, instance); instances.set(id, instance);
addToCommitBuffer({ type: "CREATE_TEXT_INSTANCE", payload: instance }); addToCommitBuffer({ type: 'CREATE_TEXT_INSTANCE', payload: instance });
return instance; return instance;
}, },
@ -148,16 +144,16 @@ export const hostConfig: HostConfig<
commitUpdate(instance, type, oldProps, newProps, internalHandle) { commitUpdate(instance, type, oldProps, newProps, internalHandle) {
instance.props = serializeProps(newProps); instance.props = serializeProps(newProps);
addToCommitBuffer({ addToCommitBuffer({
type: "UPDATE_PROPS", type: 'UPDATE_PROPS',
payload: { id: instance.id, props: instance.props }, payload: { id: instance.id, props: instance.props }
}); });
}, },
commitTextUpdate(textInstance, oldText, newText) { commitTextUpdate(textInstance, oldText, newText) {
textInstance.text = newText; textInstance.text = newText;
addToCommitBuffer({ addToCommitBuffer({
type: "UPDATE_TEXT", type: 'UPDATE_TEXT',
payload: { id: textInstance.id, text: newText }, payload: { id: textInstance.id, text: newText }
}); });
}, },
@ -167,8 +163,8 @@ export const hostConfig: HostConfig<
clearContainer: (container) => { clearContainer: (container) => {
container.children = []; container.children = [];
addToCommitBuffer({ addToCommitBuffer({
type: "CLEAR_CONTAINER", type: 'CLEAR_CONTAINER',
payload: { containerId: container.id }, payload: { containerId: container.id }
}); });
}, },
@ -202,33 +198,33 @@ export const hostConfig: HostConfig<
HostTransitionContext: React.createContext(0), HostTransitionContext: React.createContext(0),
resetFormInstance: function (): void { resetFormInstance: function (): void {
throw new Error("Function not implemented."); throw new Error('Function not implemented.');
}, },
requestPostPaintCallback: function (): void { requestPostPaintCallback: function (): void {
throw new Error("Function not implemented."); throw new Error('Function not implemented.');
}, },
shouldAttemptEagerTransition: function (): boolean { shouldAttemptEagerTransition: function (): boolean {
throw new Error("Function not implemented."); throw new Error('Function not implemented.');
}, },
trackSchedulerEvent: function (): void { trackSchedulerEvent: function (): void {
throw new Error("Function not implemented."); throw new Error('Function not implemented.');
}, },
resolveEventType: function (): null | string { resolveEventType: function (): null | string {
throw new Error("Function not implemented."); throw new Error('Function not implemented.');
}, },
resolveEventTimeStamp: function (): number { resolveEventTimeStamp: function (): number {
throw new Error("Function not implemented."); throw new Error('Function not implemented.');
}, },
preloadInstance: function (): boolean { preloadInstance: function (): boolean {
throw new Error("Function not implemented."); throw new Error('Function not implemented.');
}, },
startSuspendingCommit: function (): void { startSuspendingCommit: function (): void {
throw new Error("Function not implemented."); throw new Error('Function not implemented.');
}, },
suspendInstance: function (): void { suspendInstance: function (): void {
throw new Error("Function not implemented."); throw new Error('Function not implemented.');
}, },
waitForCommitToBeReady: function () { waitForCommitToBeReady: function () {
throw new Error("Function not implemented."); throw new Error('Function not implemented.');
}, }
}; };

View file

@ -1,30 +1,27 @@
import { createInterface } from "readline"; import { createInterface } from 'readline';
import { writeLog, writeOutput } from "./io"; import { writeLog, writeOutput } from './io';
import { runPlugin } from "./plugin"; import { runPlugin } from './plugin';
import { instances } from "./state"; import { instances } from './state';
import { batchedUpdates } from "./reconciler"; import { batchedUpdates } from './reconciler';
process.on("unhandledRejection", (reason: unknown) => { process.on('unhandledRejection', (reason: unknown) => {
writeLog(`--- UNHANDLED PROMISE REJECTION ---`); writeLog(`--- UNHANDLED PROMISE REJECTION ---`);
const stack = const stack = reason && typeof reason === 'object' && 'stack' in reason ? reason.stack : reason;
reason && typeof reason === "object" && "stack" in reason
? reason.stack
: reason;
writeLog(stack); writeLog(stack);
}); });
const rl = createInterface({ input: process.stdin }); const rl = createInterface({ input: process.stdin });
rl.on("line", (line) => { rl.on('line', (line) => {
batchedUpdates(() => { batchedUpdates(() => {
try { try {
const command: { action: string; payload: unknown } = JSON.parse(line); const command: { action: string; payload: unknown } = JSON.parse(line);
switch (command.action) { switch (command.action) {
case "run-plugin": case 'run-plugin':
runPlugin(); runPlugin();
break; break;
case "dispatch-event": { case 'dispatch-event': {
const { instanceId, handlerName, args } = command.payload as { const { instanceId, handlerName, args } = command.payload as {
instanceId: number; instanceId: number;
handlerName: string; handlerName: string;
@ -39,12 +36,10 @@ rl.on("line", (line) => {
const handler = instance._internalFiber?.memoizedProps?.[handlerName]; const handler = instance._internalFiber?.memoizedProps?.[handlerName];
if (typeof handler === "function") { if (typeof handler === 'function') {
handler(...args); handler(...args);
} else { } else {
writeLog( writeLog(`Handler ${handlerName} not found on instance ${instanceId}`);
`Handler ${handlerName} not found on instance ${instanceId}`
);
} }
break; break;
} }
@ -56,10 +51,10 @@ rl.on("line", (line) => {
err instanceof Error err instanceof Error
? { message: err.message, stack: err.stack } ? { message: err.message, stack: err.stack }
: { message: String(err) }; : { message: String(err) };
writeLog(`ERROR: ${error.message} \n ${error.stack ?? ""}`); writeLog(`ERROR: ${error.message} \n ${error.stack ?? ''}`);
writeOutput({ type: "error", payload: error.message }); writeOutput({ type: 'error', payload: error.message });
} }
}); });
}); });
writeLog("Node.js Sidecar started successfully with React Reconciler."); writeLog('Node.js Sidecar started successfully with React Reconciler.');

View file

@ -1,4 +1,4 @@
import { Packr } from "msgpackr"; import { Packr } from 'msgpackr';
const packr = new Packr(); const packr = new Packr();
@ -12,7 +12,7 @@ export const writeOutput = (data: object): void => {
process.stdout.write(payload); process.stdout.write(payload);
} catch (e: unknown) { } catch (e: unknown) {
const errorString = e instanceof Error ? e.toString() : String(e); const errorString = e instanceof Error ? e.toString() : String(e);
const errorPayload = packr.pack({ type: "log", payload: errorString }); const errorPayload = packr.pack({ type: 'log', payload: errorString });
const errorHeader = Buffer.alloc(4); const errorHeader = Buffer.alloc(4);
errorHeader.writeUInt32BE(errorPayload.length); errorHeader.writeUInt32BE(errorPayload.length);
process.stdout.write(errorHeader); process.stdout.write(errorHeader);
@ -21,5 +21,5 @@ export const writeOutput = (data: object): void => {
}; };
export const writeLog = (message: unknown): void => { export const writeLog = (message: unknown): void => {
writeOutput({ type: "log", payload: message }); writeOutput({ type: 'log', payload: message });
}; };

View file

@ -1,23 +1,23 @@
import React from "react"; import React from 'react';
import { jsx } from "react/jsx-runtime"; import { jsx } from 'react/jsx-runtime';
import plugin from "../dist/plugin/emoji.txt"; import plugin from '../dist/plugin/emoji.txt';
import { updateContainer } from "./reconciler"; import { updateContainer } from './reconciler';
import { writeLog } from "./io"; import { writeLog } from './io';
const createPluginRequire = const createPluginRequire =
() => () =>
(moduleName: string): unknown => { (moduleName: string): unknown => {
if (moduleName === "react") { if (moduleName === 'react') {
return React; return React;
} }
if (moduleName.startsWith("@raycast/api")) { if (moduleName.startsWith('@raycast/api')) {
const storage = new Map<string, string>(); const storage = new Map<string, string>();
const LocalStorage = { const LocalStorage = {
getItem: async (key: string) => storage.get(key), getItem: async (key: string) => storage.get(key),
setItem: async (key: string, value: string) => storage.set(key, value), setItem: async (key: string, value: string) => storage.set(key, value),
removeItem: async (key: string) => storage.delete(key), removeItem: async (key: string) => storage.delete(key),
clear: async () => storage.clear(), clear: async () => storage.clear()
}; };
const createWrapperComponent = const createWrapperComponent =
@ -25,33 +25,31 @@ const createPluginRequire =
({ children, ...rest }: { children?: React.ReactNode }) => ({ children, ...rest }: { children?: React.ReactNode }) =>
jsx(name, { ...rest, children }); jsx(name, { ...rest, children });
const ListComponent = createWrapperComponent("List"); const ListComponent = createWrapperComponent('List');
const ListSectionComponent = createWrapperComponent("ListSection"); const ListSectionComponent = createWrapperComponent('ListSection');
const ListDropdownComponent = createWrapperComponent("ListDropdown"); const ListDropdownComponent = createWrapperComponent('ListDropdown');
const ActionPanelComponent = createWrapperComponent("ActionPanel"); const ActionPanelComponent = createWrapperComponent('ActionPanel');
const ActionPanelSectionComponent = const ActionPanelSectionComponent = createWrapperComponent('ActionPanelSection');
createWrapperComponent("ActionPanelSection");
Object.assign(ListComponent, { Object.assign(ListComponent, {
Item: "ListItem", Item: 'ListItem',
Section: ListSectionComponent, Section: ListSectionComponent,
Dropdown: ListDropdownComponent, Dropdown: ListDropdownComponent
}); });
Object.assign(ListDropdownComponent, { Item: "ListDropdownItem" }); Object.assign(ListDropdownComponent, { Item: 'ListDropdownItem' });
Object.assign(ActionPanelComponent, { Object.assign(ActionPanelComponent, {
Section: ActionPanelSectionComponent, Section: ActionPanelSectionComponent
}); });
return { return {
LocalStorage, LocalStorage,
environment: { environment: {
assetsPath: assetsPath: '/home/byte/code/raycast-linux/sidecar/dist/plugin/assets/'
"/home/byte/code/raycast-linux/sidecar/dist/plugin/assets/",
}, },
getPreferenceValues: () => ({ getPreferenceValues: () => ({
primaryAction: "paste", primaryAction: 'paste',
unicodeVersion: "14.0", unicodeVersion: '14.0',
shortCodes: true, shortCodes: true
}), }),
usePersistentState: <T>( usePersistentState: <T>(
key: string, key: string,
@ -63,10 +61,10 @@ const createPluginRequire =
List: ListComponent, List: ListComponent,
ActionPanel: ActionPanelComponent, ActionPanel: ActionPanelComponent,
Action: { Action: {
Paste: "Action.Paste", Paste: 'Action.Paste',
CopyToClipboard: "Action.CopyToClipboard", CopyToClipboard: 'Action.CopyToClipboard',
OpenInBrowser: "Action.OpenInBrowser", OpenInBrowser: 'Action.OpenInBrowser'
}, }
}; };
} }
@ -76,32 +74,21 @@ const createPluginRequire =
export const runPlugin = (): void => { export const runPlugin = (): void => {
const scriptText = plugin; const scriptText = plugin;
const pluginModule = { const pluginModule = {
exports: {} as { default: React.ComponentType | null }, exports: {} as { default: React.ComponentType | null }
}; };
const scriptFunction = new Function( const scriptFunction = new Function('require', 'module', 'exports', 'React', scriptText);
"require",
"module",
"exports",
"React",
scriptText
);
scriptFunction( scriptFunction(createPluginRequire(), pluginModule, pluginModule.exports, React);
createPluginRequire(),
pluginModule,
pluginModule.exports,
React
);
const PluginRootComponent = pluginModule.exports.default; const PluginRootComponent = pluginModule.exports.default;
if (!PluginRootComponent) { if (!PluginRootComponent) {
throw new Error("Plugin did not export a default component."); throw new Error('Plugin did not export a default component.');
} }
writeLog("Plugin loaded. Initializing React render..."); writeLog('Plugin loaded. Initializing React render...');
const AppElement = React.createElement(PluginRootComponent); const AppElement = React.createElement(PluginRootComponent);
updateContainer(AppElement, () => { updateContainer(AppElement, () => {
writeLog("Initial render complete"); writeLog('Initial render complete');
}); });
}; };

View file

@ -1,8 +1,8 @@
import Reconciler, { type RootTag } from "react-reconciler"; import Reconciler, { type RootTag } from 'react-reconciler';
import type React from "react"; import type React from 'react';
import { root } from "./state"; import { root } from './state';
import { hostConfig } from "./hostConfig"; import { hostConfig } from './hostConfig';
import { writeLog } from "./io"; import { writeLog } from './io';
const reconciler = Reconciler(hostConfig); const reconciler = Reconciler(hostConfig);
@ -17,15 +17,12 @@ export const container = reconciler.createContainer(
null, null,
false, false,
null, null,
"", '',
onRecoverableError, onRecoverableError,
null null
); );
export const updateContainer = ( export const updateContainer = (element: React.ReactElement, callback?: () => void) => {
element: React.ReactElement,
callback?: () => void
) => {
reconciler.updateContainer(element, container, null, callback); reconciler.updateContainer(element, container, null, callback);
}; };

View file

@ -1,7 +1,7 @@
import type { AnyInstance, Commit, Container } from "./types"; import type { AnyInstance, Commit, Container } from './types';
export const instances = new Map<number, AnyInstance>(); export const instances = new Map<number, AnyInstance>();
export const root: Container = { id: "root", children: [] }; export const root: Container = { id: 'root', children: [] };
let instanceCounter = 0; let instanceCounter = 0;
export const getNextInstanceId = (): number => ++instanceCounter; export const getNextInstanceId = (): number => ++instanceCounter;

View file

@ -1,5 +1,5 @@
import type React from "react"; import type React from 'react';
import type Reconciler from "react-reconciler"; import type Reconciler from 'react-reconciler';
export type ComponentType = string | React.ComponentType<any>; export type ComponentType = string | React.ComponentType<any>;
export type ComponentProps = Record<string, unknown>; export type ComponentProps = Record<string, unknown>;
@ -16,12 +16,12 @@ export interface RaycastInstance extends BaseInstance {
} }
export interface TextInstance extends BaseInstance { export interface TextInstance extends BaseInstance {
type: "TEXT"; type: 'TEXT';
text: string; text: string;
} }
export interface Container { export interface Container {
id: "root"; id: 'root';
children: (RaycastInstance | TextInstance)[]; children: (RaycastInstance | TextInstance)[];
} }
@ -35,7 +35,7 @@ export interface Commit {
} }
export interface SerializedReactElement { export interface SerializedReactElement {
$$typeof: "react.element.serialized"; $$typeof: 'react.element.serialized';
type: string; type: string;
props: Record<string, unknown>; props: Record<string, unknown>;
} }

View file

@ -1,41 +1,29 @@
import React from "react"; import React from 'react';
import type { import type { ComponentType, Commit, SerializedReactElement, ParentInstance } from './types';
ComponentType, import { root, instances } from './state';
Commit,
SerializedReactElement,
ParentInstance,
} from "./types";
import { root, instances } from "./state";
export const getComponentDisplayName = (type: ComponentType): string => { export const getComponentDisplayName = (type: ComponentType): string => {
if (typeof type === "string") { if (typeof type === 'string') {
return type; return type;
} }
return type.displayName ?? type.name ?? "Anonymous"; return type.displayName ?? type.name ?? 'Anonymous';
}; };
const isSerializableReactElement = ( const isSerializableReactElement = (value: unknown): value is React.ReactElement =>
value: unknown React.isValidElement(value);
): value is React.ReactElement => React.isValidElement(value);
function serializeReactElement( function serializeReactElement(element: React.ReactElement): SerializedReactElement {
element: React.ReactElement
): SerializedReactElement {
return { return {
$$typeof: "react.element.serialized", $$typeof: 'react.element.serialized',
type: getComponentDisplayName(element.type as ComponentType), type: getComponentDisplayName(element.type as ComponentType),
props: serializeProps(element.props as Record<string, unknown>), props: serializeProps(element.props as Record<string, unknown>)
}; };
} }
export function serializeProps( export function serializeProps(props: Record<string, unknown>): Record<string, unknown> {
props: Record<string, unknown>
): Record<string, unknown> {
return Object.fromEntries( return Object.fromEntries(
Object.entries(props) Object.entries(props)
.filter( .filter(([key, value]) => key !== 'children' && typeof value !== 'function')
([key, value]) => key !== "children" && typeof value !== "function"
)
.map(([key, value]) => { .map(([key, value]) => {
if (isSerializableReactElement(value)) { if (isSerializableReactElement(value)) {
return [key, serializeReactElement(value)]; return [key, serializeReactElement(value)];
@ -44,10 +32,8 @@ export function serializeProps(
return [ return [
key, key,
value.map((item) => value.map((item) =>
isSerializableReactElement(item) isSerializableReactElement(item) ? serializeReactElement(item) : item
? serializeReactElement(item) )
: item
),
]; ];
} }
return [key, value]; return [key, value];
@ -57,23 +43,18 @@ export function serializeProps(
export function optimizeCommitBuffer(buffer: Commit[]): Commit[] { export function optimizeCommitBuffer(buffer: Commit[]): Commit[] {
const OPTIMIZATION_THRESHOLD = 10; const OPTIMIZATION_THRESHOLD = 10;
const childOpsByParent = new Map<ParentInstance["id"], Commit[]>(); const childOpsByParent = new Map<ParentInstance['id'], Commit[]>();
const otherOps: Commit[] = []; const otherOps: Commit[] = [];
for (const op of buffer) { for (const op of buffer) {
const { type, payload } = op; const { type, payload } = op;
const parentId = (payload as { parentId?: ParentInstance["id"] })?.parentId; const parentId = (payload as { parentId?: ParentInstance['id'] })?.parentId;
const isChildOp = const isChildOp =
type === "APPEND_CHILD" || type === 'APPEND_CHILD' || type === 'REMOVE_CHILD' || type === 'INSERT_BEFORE';
type === "REMOVE_CHILD" ||
type === "INSERT_BEFORE";
if (isChildOp && parentId) { if (isChildOp && parentId) {
childOpsByParent.set( childOpsByParent.set(parentId, (childOpsByParent.get(parentId) ?? []).concat(op));
parentId,
(childOpsByParent.get(parentId) ?? []).concat(op)
);
} else { } else {
otherOps.push(op); otherOps.push(op);
} }
@ -91,14 +72,13 @@ export function optimizeCommitBuffer(buffer: Commit[]): Commit[] {
continue; continue;
} }
const parentInstance = const parentInstance = parentId === 'root' ? root : instances.get(parentId as number);
parentId === "root" ? root : instances.get(parentId as number);
if (parentInstance && "children" in parentInstance) { if (parentInstance && 'children' in parentInstance) {
const childrenIds = parentInstance.children.map(({ id }) => id); const childrenIds = parentInstance.children.map(({ id }) => id);
finalOps.push({ finalOps.push({
type: "REPLACE_CHILDREN", type: 'REPLACE_CHILDREN',
payload: { parentId, childrenIds }, payload: { parentId, childrenIds }
}); });
} else { } else {
finalOps.push(...ops); finalOps.push(...ops);

View file

@ -1,22 +1,18 @@
import { setResults } from "../results.svelte"; import { setResults } from '../results.svelte';
import type * as api from "@raycast/api"; import type * as api from '@raycast/api';
import { Toast } from "./toast"; import { Toast } from './toast';
import { import { writeText, writeHtml, writeImage } from '@tauri-apps/plugin-clipboard-manager';
writeText,
writeHtml,
writeImage,
} from "@tauri-apps/plugin-clipboard-manager";
export const mockRaycastApi = { export const mockRaycastApi = {
updateCommandMetadata: async (metadata: { subtitle?: string | null }) => { updateCommandMetadata: async (metadata: { subtitle?: string | null }) => {
setResults([{ subtitle: metadata.subtitle }]); setResults([{ subtitle: metadata.subtitle }]);
}, },
environment: { environment: {
launchType: "userInitiated", launchType: 'userInitiated'
}, },
LaunchType: { LaunchType: {
UserInitiated: "userInitiated", UserInitiated: 'userInitiated',
Background: "background", Background: 'background'
}, },
Toast: Toast as typeof api.Toast, Toast: Toast as typeof api.Toast,
Clipboard: { Clipboard: {
@ -24,17 +20,17 @@ export const mockRaycastApi = {
content: string | number | api.Clipboard.Content, content: string | number | api.Clipboard.Content,
options?: api.Clipboard.CopyOptions options?: api.Clipboard.CopyOptions
) => { ) => {
if (typeof content === "string" || typeof content === "number") { if (typeof content === 'string' || typeof content === 'number') {
await writeText(content.toString()); await writeText(content.toString());
} else { } else {
if ("html" in content) { if ('html' in content) {
await writeHtml(content.html); await writeHtml(content.html);
} else if ("file" in content) { } else if ('file' in content) {
await writeImage(content.file); await writeImage(content.file);
} else { } else {
await writeText(content.text); await writeText(content.text);
} }
} }
}, }
}, }
} satisfies typeof api; } satisfies typeof api;

View file

@ -1,21 +1,21 @@
import { setToast } from "$lib/results.svelte"; import { setToast } from '$lib/results.svelte';
import type * as api from "@raycast/api"; import type * as api from '@raycast/api';
export class Toast { export class Toast {
public static readonly Style = { public static readonly Style = {
Success: "SUCCESS", Success: 'SUCCESS',
Failure: "FAILURE", Failure: 'FAILURE',
Animated: "ANIMATED", Animated: 'ANIMATED'
}; };
public style: "SUCCESS" | "FAILURE" | "ANIMATED"; public style: 'SUCCESS' | 'FAILURE' | 'ANIMATED';
public title: string; public title: string;
public message: string | undefined; public message: string | undefined;
public primaryAction: api.Toast.ActionOptions | undefined; public primaryAction: api.Toast.ActionOptions | undefined;
public secondaryAction: api.Toast.ActionOptions | undefined; public secondaryAction: api.Toast.ActionOptions | undefined;
constructor(props: api.Toast.Options) { constructor(props: api.Toast.Options) {
this.style = props.style ?? "SUCCESS"; // TODO: is this default value correct? this.style = props.style ?? 'SUCCESS'; // TODO: is this default value correct?
this.title = props.title; this.title = props.title;
this.message = props.message; this.message = props.message;
this.primaryAction = props.primaryAction; this.primaryAction = props.primaryAction;

View file

@ -1,6 +1,6 @@
<!-- src/ResultsList.svelte --> <!-- src/ResultsList.svelte -->
<script lang="ts"> <script lang="ts">
import { getResults } from "$lib/results.svelte"; import { getResults } from '$lib/results.svelte';
const pluginResults = $derived(getResults()); const pluginResults = $derived(getResults());
</script> </script>
@ -8,10 +8,8 @@
<div class="flex-grow overflow-y-auto"> <div class="flex-grow overflow-y-auto">
<ul> <ul>
{#each pluginResults as result} {#each pluginResults as result}
<li <li class="flex cursor-pointer items-center rounded-lg p-2 hover:bg-blue-500/20">
class="flex items-center p-2 rounded-lg hover:bg-blue-500/20 cursor-pointer" <div class="ml-2 flex flex-col">
>
<div class="flex flex-col ml-2">
<span class="text-xs text-gray-400">{result.subtitle}</span> <span class="text-xs text-gray-400">{result.subtitle}</span>
</div> </div>
</li> </li>

View file

@ -1,22 +1,22 @@
<script lang="ts"> <script lang="ts">
import { Separator } from "./ui/separator"; import { Separator } from './ui/separator';
import { getToast } from "../results.svelte"; import { getToast } from '../results.svelte';
import { Toast } from "$lib/api/toast"; import { Toast } from '$lib/api/toast';
import { Button } from "./ui/button"; import { Button } from './ui/button';
import type * as api from "@raycast/api"; import type * as api from '@raycast/api';
import { Kbd } from "./ui/kbd"; import { Kbd } from './ui/kbd';
import { shortcutToText } from "$lib/renderKey"; import { shortcutToText } from '$lib/renderKey';
const toast = getToast(); const toast = getToast();
const styles = $derived.by(() => { const styles = $derived.by(() => {
switch (toast?.style) { switch (toast?.style) {
case Toast.Style.Success: case Toast.Style.Success:
return "bg-green-500/20"; return 'bg-green-500/20';
case Toast.Style.Failure: case Toast.Style.Failure:
return "bg-red-500/20"; return 'bg-red-500/20';
default: default:
return "bg-gray-500/20"; return 'bg-gray-500/20';
} }
}); });
</script> </script>
@ -26,13 +26,13 @@
{#if toast} {#if toast}
{@const actualToast = toast as Toast} {@const actualToast = toast as Toast}
<div class="py-2 px-4 flex items-center gap-4 {styles}"> <div class="flex items-center gap-4 px-4 py-2 {styles}">
{#if actualToast.style === "SUCCESS"} {#if actualToast.style === 'SUCCESS'}
<div class="w-2.5 h-2.5 bg-green-500 rounded-full"></div> <div class="h-2.5 w-2.5 rounded-full bg-green-500"></div>
{:else if actualToast.style === "FAILURE"} {:else if actualToast.style === 'FAILURE'}
<div class="w-2.5 h-2.5 bg-red-500 rounded-full"></div> <div class="h-2.5 w-2.5 rounded-full bg-red-500"></div>
{:else if actualToast.style === "ANIMATED"} {:else if actualToast.style === 'ANIMATED'}
<div class="w-2.5 h-2.5 bg-gray-500 rounded-full"></div> <div class="h-2.5 w-2.5 rounded-full bg-gray-500"></div>
{/if} {/if}
{actualToast.title} {actualToast.title}
@ -42,8 +42,7 @@
{#if actualToast.primaryAction} {#if actualToast.primaryAction}
<Button <Button
variant="ghost" variant="ghost"
onclick={() => onclick={() => actualToast.primaryAction!.onAction(actualToast as api.Toast)}
actualToast.primaryAction!.onAction(actualToast as api.Toast)}
> >
{actualToast.primaryAction.title} {actualToast.primaryAction.title}

View file

@ -1,36 +1,36 @@
<script lang="ts" module> <script lang="ts" module>
import { cn, type WithElementRef } from "$lib/utils.js"; import { cn, type WithElementRef } from '$lib/utils.js';
import type { HTMLAnchorAttributes, HTMLButtonAttributes } from "svelte/elements"; import type { HTMLAnchorAttributes, HTMLButtonAttributes } from 'svelte/elements';
import { type VariantProps, tv } from "tailwind-variants"; import { type VariantProps, tv } from 'tailwind-variants';
export const buttonVariants = tv({ export const buttonVariants = tv({
base: "focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive inline-flex shrink-0 items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium outline-none transition-all focus-visible:ring-[3px] disabled:pointer-events-none disabled:opacity-50 aria-disabled:pointer-events-none aria-disabled:opacity-50 [&_svg:not([class*='size-'])]:size-4 [&_svg]:pointer-events-none [&_svg]:shrink-0", base: "focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive inline-flex shrink-0 items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium outline-none transition-all focus-visible:ring-[3px] disabled:pointer-events-none disabled:opacity-50 aria-disabled:pointer-events-none aria-disabled:opacity-50 [&_svg:not([class*='size-'])]:size-4 [&_svg]:pointer-events-none [&_svg]:shrink-0",
variants: { variants: {
variant: { variant: {
default: "bg-primary text-primary-foreground shadow-xs hover:bg-primary/90", default: 'bg-primary text-primary-foreground shadow-xs hover:bg-primary/90',
destructive: destructive:
"bg-destructive shadow-xs hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60 text-white", 'bg-destructive shadow-xs hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60 text-white',
outline: outline:
"bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50 border", 'bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50 border',
secondary: "bg-secondary text-secondary-foreground shadow-xs hover:bg-secondary/80", secondary: 'bg-secondary text-secondary-foreground shadow-xs hover:bg-secondary/80',
ghost: "hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50", ghost: 'hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50',
link: "text-primary underline-offset-4 hover:underline", link: 'text-primary underline-offset-4 hover:underline'
}, },
size: { size: {
default: "h-9 px-4 py-2 has-[>svg]:px-3", default: 'h-9 px-4 py-2 has-[>svg]:px-3',
sm: "h-8 gap-1.5 rounded-md px-3 has-[>svg]:px-2.5", sm: 'h-8 gap-1.5 rounded-md px-3 has-[>svg]:px-2.5',
lg: "h-10 rounded-md px-6 has-[>svg]:px-4", lg: 'h-10 rounded-md px-6 has-[>svg]:px-4',
icon: "size-9", icon: 'size-9'
}, }
}, },
defaultVariants: { defaultVariants: {
variant: "default", variant: 'default',
size: "default", size: 'default'
}, }
}); });
export type ButtonVariant = VariantProps<typeof buttonVariants>["variant"]; export type ButtonVariant = VariantProps<typeof buttonVariants>['variant'];
export type ButtonSize = VariantProps<typeof buttonVariants>["size"]; export type ButtonSize = VariantProps<typeof buttonVariants>['size'];
export type ButtonProps = WithElementRef<HTMLButtonAttributes> & export type ButtonProps = WithElementRef<HTMLButtonAttributes> &
WithElementRef<HTMLAnchorAttributes> & { WithElementRef<HTMLAnchorAttributes> & {
@ -42,11 +42,11 @@
<script lang="ts"> <script lang="ts">
let { let {
class: className, class: className,
variant = "default", variant = 'default',
size = "default", size = 'default',
ref = $bindable(null), ref = $bindable(null),
href = undefined, href = undefined,
type = "button", type = 'button',
disabled, disabled,
children, children,
...restProps ...restProps
@ -60,7 +60,7 @@
class={cn(buttonVariants({ variant, size }), className)} class={cn(buttonVariants({ variant, size }), className)}
href={disabled ? undefined : href} href={disabled ? undefined : href}
aria-disabled={disabled} aria-disabled={disabled}
role={disabled ? "link" : undefined} role={disabled ? 'link' : undefined}
tabindex={disabled ? -1 : undefined} tabindex={disabled ? -1 : undefined}
{...restProps} {...restProps}
> >

View file

@ -2,8 +2,8 @@ import Root, {
type ButtonProps, type ButtonProps,
type ButtonSize, type ButtonSize,
type ButtonVariant, type ButtonVariant,
buttonVariants, buttonVariants
} from "./button.svelte"; } from './button.svelte';
export { export {
Root, Root,
@ -13,5 +13,5 @@ export {
buttonVariants, buttonVariants,
type ButtonProps, type ButtonProps,
type ButtonSize, type ButtonSize,
type ButtonVariant, type ButtonVariant
}; };

View file

@ -1,7 +1,7 @@
import Root from "./input.svelte"; import Root from './input.svelte';
export { export {
Root, Root,
// //
Root as Input, Root as Input
}; };

View file

@ -1,18 +1,12 @@
<script lang="ts"> <script lang="ts">
import type { import type { HTMLInputAttributes, HTMLInputTypeAttribute } from 'svelte/elements';
HTMLInputAttributes, import { cn, type WithElementRef } from '$lib/utils.js';
HTMLInputTypeAttribute,
} from "svelte/elements";
import { cn, type WithElementRef } from "$lib/utils.js";
type InputType = Exclude<HTMLInputTypeAttribute, "file">; type InputType = Exclude<HTMLInputTypeAttribute, 'file'>;
type Props = WithElementRef< type Props = WithElementRef<
Omit<HTMLInputAttributes, "type"> & Omit<HTMLInputAttributes, 'type'> &
( ({ type: 'file'; files?: FileList } | { type?: InputType; files?: undefined })
| { type: "file"; files?: FileList }
| { type?: InputType; files?: undefined }
)
>; >;
let { let {
@ -25,14 +19,14 @@
}: Props = $props(); }: Props = $props();
</script> </script>
{#if type === "file"} {#if type === 'file'}
<input <input
bind:this={ref} bind:this={ref}
data-slot="input" data-slot="input"
class={cn( class={cn(
"selection:bg-primary dark:bg-input/30 selection:text-primary-foreground border-input ring-offset-background placeholder:text-muted-foreground shadow-xs flex h-9 w-full min-w-0 rounded-md border bg-transparent px-3 pt-1.5 text-sm font-medium outline-none transition-[color,box-shadow] disabled:cursor-not-allowed disabled:opacity-50 md:text-sm", 'selection:bg-primary dark:bg-input/30 selection:text-primary-foreground border-input ring-offset-background placeholder:text-muted-foreground flex h-9 w-full min-w-0 rounded-md border bg-transparent px-3 pt-1.5 text-sm font-medium shadow-xs transition-[color,box-shadow] outline-none disabled:cursor-not-allowed disabled:opacity-50 md:text-sm',
"focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px]", 'focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px]',
"aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive", 'aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive',
className className
)} )}
type="file" type="file"
@ -45,8 +39,8 @@
bind:this={ref} bind:this={ref}
data-slot="input" data-slot="input"
class={cn( class={cn(
"border-input bg-background selection:bg-primary dark:bg-input/30 selection:text-primary-foreground ring-offset-background placeholder:text-muted-foreground shadow-xs flex h-9 w-full min-w-0 rounded-md border px-3 py-1 text-base outline-none transition-[color,box-shadow] disabled:cursor-not-allowed disabled:opacity-50 md:text-sm", 'border-input bg-background selection:bg-primary dark:bg-input/30 selection:text-primary-foreground ring-offset-background placeholder:text-muted-foreground flex h-9 w-full min-w-0 rounded-md border px-3 py-1 text-base shadow-xs transition-[color,box-shadow] outline-none disabled:cursor-not-allowed disabled:opacity-50 md:text-sm',
"aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive", 'aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive',
className className
)} )}
{type} {type}

View file

@ -1,3 +1,3 @@
import Kbd from "./kbd.svelte"; import Kbd from './kbd.svelte';
export { Kbd }; export { Kbd };

View file

@ -1,25 +1,25 @@
<script lang="ts" module> <script lang="ts" module>
import { tv, type VariantProps } from "tailwind-variants"; import { tv, type VariantProps } from 'tailwind-variants';
import type { WithChildren } from "bits-ui"; import type { WithChildren } from 'bits-ui';
const style = tv({ const style = tv({
base: "inline-flex place-items-center justify-center gap-1 rounded-md p-0.5", base: 'inline-flex place-items-center justify-center gap-1 rounded-md p-0.5',
variants: { variants: {
variant: { variant: {
outline: "border-border bg-background text-muted-foreground border", outline: 'border-border bg-background text-muted-foreground border',
secondary: "bg-secondary text-muted-foreground", secondary: 'bg-secondary text-muted-foreground',
primary: "bg-primary text-primary-foreground", primary: 'bg-primary text-primary-foreground'
}, },
size: { size: {
sm: "min-w-6 gap-1.5 p-0.5 px-1 text-sm", sm: 'min-w-6 gap-1.5 p-0.5 px-1 text-sm',
default: "min-w-8 gap-1.5 p-1 px-2", default: 'min-w-8 gap-1.5 p-1 px-2',
lg: "min-w-9 gap-2 p-1 px-3 text-lg", lg: 'min-w-9 gap-2 p-1 px-3 text-lg'
}, }
}, }
}); });
type Size = VariantProps<typeof style>["size"]; type Size = VariantProps<typeof style>['size'];
type Variant = VariantProps<typeof style>["variant"]; type Variant = VariantProps<typeof style>['variant'];
export type KbdPropsWithoutHTML = WithChildren<{ export type KbdPropsWithoutHTML = WithChildren<{
ref?: HTMLElement | null; ref?: HTMLElement | null;
@ -32,14 +32,14 @@
</script> </script>
<script lang="ts"> <script lang="ts">
import { cn } from "$lib/utils"; import { cn } from '$lib/utils';
let { let {
ref = $bindable(null), ref = $bindable(null),
class: className, class: className,
size = "default", size = 'default',
variant = "outline", variant = 'outline',
children, children
}: KbdProps = $props(); }: KbdProps = $props();
</script> </script>

View file

@ -1,7 +1,7 @@
import Root from "./separator.svelte"; import Root from './separator.svelte';
export { export {
Root, Root,
// //
Root as Separator, Root as Separator
}; };

View file

@ -1,6 +1,6 @@
<script lang="ts"> <script lang="ts">
import { Separator as SeparatorPrimitive } from "bits-ui"; import { Separator as SeparatorPrimitive } from 'bits-ui';
import { cn } from "$lib/utils.js"; import { cn } from '$lib/utils.js';
let { let {
ref = $bindable(null), ref = $bindable(null),
@ -13,7 +13,7 @@
bind:ref bind:ref
data-slot="separator" data-slot="separator"
class={cn( class={cn(
"bg-border shrink-0 data-[orientation=horizontal]:h-px data-[orientation=vertical]:h-full data-[orientation=horizontal]:w-full data-[orientation=vertical]:w-px", 'bg-border shrink-0 data-[orientation=horizontal]:h-px data-[orientation=horizontal]:w-full data-[orientation=vertical]:h-full data-[orientation=vertical]:w-px',
className className
)} )}
{...restProps} {...restProps}

View file

@ -1,5 +1,5 @@
import { mockRaycastApi } from "./api"; import { mockRaycastApi } from './api';
import plugin from "./plugin.js?raw"; import plugin from './plugin.js?raw';
declare global { declare global {
interface Window { interface Window {
@ -10,42 +10,36 @@ declare global {
window.require = function (moduleName) { window.require = function (moduleName) {
console.log(`Plugin is requesting module: ${moduleName}`); console.log(`Plugin is requesting module: ${moduleName}`);
if (moduleName === "@raycast/api") { if (moduleName === '@raycast/api') {
return mockRaycastApi; return mockRaycastApi;
} }
throw new Error( throw new Error(`Module not found: ${moduleName}. Our fake 'require' is very limited!`);
`Module not found: ${moduleName}. Our fake 'require' is very limited!`
);
}; };
window.module = { window.module = {
exports: {}, exports: {}
}; };
export async function runPlugin() { export async function runPlugin() {
console.log("Requesting plugin script from Rust backend..."); console.log('Requesting plugin script from Rust backend...');
const scriptText = plugin; const scriptText = plugin;
console.log("Executing plugin script in a try/catch block..."); console.log('Executing plugin script in a try/catch block...');
try { try {
// TOOD: don't use eval // TOOD: don't use eval
eval(scriptText); eval(scriptText);
} catch (e) { } catch (e) {
console.error("Error evaluating plugin script:", e); console.error('Error evaluating plugin script:', e);
return; return;
} }
const pluginMainFunction = window.module.exports.default; const pluginMainFunction = window.module.exports.default;
if (pluginMainFunction && typeof pluginMainFunction === "function") { if (pluginMainFunction && typeof pluginMainFunction === 'function') {
console.log( console.log('Plugin script loaded successfully. Running its main command...');
"Plugin script loaded successfully. Running its main command..."
);
await pluginMainFunction(); await pluginMainFunction();
console.log("Plugin main command finished."); console.log('Plugin main command finished.');
} else { } else {
console.error( console.error('Could not find a default export function in the plugin script.');
"Could not find a default export function in the plugin script."
);
} }
} }

View file

@ -1,65 +1,61 @@
"use strict"; 'use strict';
var __defProp = Object.defineProperty; var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames; var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty; var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => { var __export = (target, all) => {
for (var name in all) for (var name in all) __defProp(target, name, { get: all[name], enumerable: true });
__defProp(target, name, { get: all[name], enumerable: true });
}; };
var __copyProps = (to, from, except, desc) => { var __copyProps = (to, from, except, desc) => {
if ((from && typeof from === "object") || typeof from === "function") { if ((from && typeof from === 'object') || typeof from === 'function') {
for (let key of __getOwnPropNames(from)) for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except) if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { __defProp(to, key, {
get: () => from[key], get: () => from[key],
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable, enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
}); });
} }
return to; return to;
}; };
var __toCommonJS = (mod) => var __toCommonJS = (mod) => __copyProps(__defProp({}, '__esModule', { value: true }), mod);
__copyProps(__defProp({}, "__esModule", { value: true }), mod);
// src/index.ts // src/index.ts
var src_exports = {}; var src_exports = {};
__export(src_exports, { __export(src_exports, {
default: () => src_default, default: () => src_default
}); });
module.exports = __toCommonJS(src_exports); module.exports = __toCommonJS(src_exports);
var import_api = require("@raycast/api"); var import_api = require('@raycast/api');
var command = async () => { var command = async () => {
const now = new Date(); const now = new Date();
const london = now.toLocaleString(void 0, { const london = now.toLocaleString(void 0, {
timeZone: "Europe/London", timeZone: 'Europe/London',
timeStyle: "short", timeStyle: 'short'
}); });
const berlin = now.toLocaleString(void 0, { const berlin = now.toLocaleString(void 0, {
timeZone: "Europe/Berlin", timeZone: 'Europe/Berlin',
timeStyle: "short", timeStyle: 'short'
}); });
const moscow = now.toLocaleString(void 0, { const moscow = now.toLocaleString(void 0, {
timeZone: "Europe/Moscow", timeZone: 'Europe/Moscow',
timeStyle: "short", timeStyle: 'short'
}); });
const india = now.toLocaleString(void 0, { const india = now.toLocaleString(void 0, {
timeZone: "Asia/Kolkata", timeZone: 'Asia/Kolkata',
timeStyle: "short", timeStyle: 'short'
}); });
const subtitle = `\u{1F1EC}\u{1F1E7} ${london} \u{1F1F3}\u{1F1F1}\u{1F1E9}\u{1F1EA}\u{1F1F3}\u{1F1F4}\u{1F1E9}\u{1F1F0}\u{1F1F5}\u{1F1F1} ${berlin} \u{1F1F7}\u{1F1FA} ${moscow} \u{1F1EE}\u{1F1F3} ${india}`; const subtitle = `\u{1F1EC}\u{1F1E7} ${london} \u{1F1F3}\u{1F1F1}\u{1F1E9}\u{1F1EA}\u{1F1F3}\u{1F1F4}\u{1F1E9}\u{1F1F0}\u{1F1F5}\u{1F1F1} ${berlin} \u{1F1F7}\u{1F1FA} ${moscow} \u{1F1EE}\u{1F1F3} ${india}`;
await (0, import_api.updateCommandMetadata)({ subtitle }); await (0, import_api.updateCommandMetadata)({ subtitle });
if ( if (import_api.environment.launchType === import_api.LaunchType.UserInitiated) {
import_api.environment.launchType === import_api.LaunchType.UserInitiated
) {
const toast = new import_api.Toast({ const toast = new import_api.Toast({
style: import_api.Toast.Style.Success, style: import_api.Toast.Style.Success,
title: "Refreshed!", title: 'Refreshed!',
message: subtitle, message: subtitle
}); });
toast.primaryAction = { toast.primaryAction = {
title: "Copy to Clipboard", title: 'Copy to Clipboard',
shortcut: { modifiers: ["cmd", "shift"], key: "c" }, shortcut: { modifiers: ['cmd', 'shift'], key: 'c' },
onAction: () => import_api.Clipboard.copy(subtitle), onAction: () => import_api.Clipboard.copy(subtitle)
}; };
await toast.show(); await toast.show();
} }

View file

@ -1,4 +1,4 @@
import type { Keyboard } from "@raycast/api"; import type { Keyboard } from '@raycast/api';
type ShortcutParts = { type ShortcutParts = {
modifiers: Keyboard.KeyModifier[]; modifiers: Keyboard.KeyModifier[];
@ -8,32 +8,32 @@ type ShortcutParts = {
function formatShortcutParts(parts: ShortcutParts, isMac: boolean): string { function formatShortcutParts(parts: ShortcutParts, isMac: boolean): string {
const modifierMap = { const modifierMap = {
mac: { mac: {
cmd: "⌘", cmd: '⌘',
ctrl: "⌃", ctrl: '⌃',
opt: "⌥", opt: '⌥',
shift: "⇧", shift: '⇧'
}, },
other: { other: {
cmd: "Win", cmd: 'Win',
ctrl: "Ctrl", ctrl: 'Ctrl',
opt: "Alt", opt: 'Alt',
shift: "Shift", shift: 'Shift'
}, }
}; };
const keyMap: Partial<Record<Keyboard.KeyEquivalent, string>> = { const keyMap: Partial<Record<Keyboard.KeyEquivalent, string>> = {
return: "⏎", return: '⏎',
enter: "⏎", enter: '⏎',
delete: "⌫", delete: '⌫',
backspace: "⌫", backspace: '⌫',
deleteForward: "⌦", deleteForward: '⌦',
arrowUp: "↑", arrowUp: '↑',
arrowDown: "↓", arrowDown: '↓',
arrowLeft: "←", arrowLeft: '←',
arrowRight: "→", arrowRight: '→',
tab: "⇥", tab: '⇥',
escape: "⎋", escape: '⎋',
space: "␣", space: '␣'
}; };
const currentModifiers = isMac ? modifierMap.mac : modifierMap.other; const currentModifiers = isMac ? modifierMap.mac : modifierMap.other;
@ -44,18 +44,15 @@ function formatShortcutParts(parts: ShortcutParts, isMac: boolean): string {
const allParts = [...modifierStrings, keyString]; const allParts = [...modifierStrings, keyString];
return allParts.join(" + "); return allParts.join(' + ');
} }
export function shortcutToText( export function shortcutToText(shortcut: Keyboard.Shortcut, forceOS?: 'macOS' | 'windows'): string {
shortcut: Keyboard.Shortcut,
forceOS?: "macOS" | "windows"
): string {
const isMac = forceOS const isMac = forceOS
? forceOS === "macOS" ? forceOS === 'macOS'
: typeof navigator !== "undefined" && /Mac/i.test(navigator.platform); : typeof navigator !== 'undefined' && /Mac/i.test(navigator.platform);
if ("modifiers" in shortcut) { if ('modifiers' in shortcut) {
return formatShortcutParts(shortcut, isMac); return formatShortcutParts(shortcut, isMac);
} else { } else {
if (isMac) { if (isMac) {

View file

@ -1,4 +1,4 @@
import type { Toast } from "./api/toast"; import type { Toast } from './api/toast';
type Result = { type Result = {
subtitle?: string | null; subtitle?: string | null;

View file

@ -1,13 +1,13 @@
import { clsx, type ClassValue } from "clsx"; import { clsx, type ClassValue } from 'clsx';
import { twMerge } from "tailwind-merge"; import { twMerge } from 'tailwind-merge';
export function cn(...inputs: ClassValue[]) { export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs)); return twMerge(clsx(inputs));
} }
// eslint-disable-next-line @typescript-eslint/no-explicit-any // eslint-disable-next-line @typescript-eslint/no-explicit-any
export type WithoutChild<T> = T extends { child?: any } ? Omit<T, "child"> : T; export type WithoutChild<T> = T extends { child?: any } ? Omit<T, 'child'> : T;
// eslint-disable-next-line @typescript-eslint/no-explicit-any // eslint-disable-next-line @typescript-eslint/no-explicit-any
export type WithoutChildren<T> = T extends { children?: any } ? Omit<T, "children"> : T; export type WithoutChildren<T> = T extends { children?: any } ? Omit<T, 'children'> : T;
export type WithoutChildrenOrChild<T> = WithoutChildren<WithoutChild<T>>; export type WithoutChildrenOrChild<T> = WithoutChildren<WithoutChild<T>>;
export type WithElementRef<T, U extends HTMLElement = HTMLElement> = T & { ref?: U | null }; export type WithElementRef<T, U extends HTMLElement = HTMLElement> = T & { ref?: U | null };

View file

@ -1,9 +1,9 @@
<script lang="ts"> <script lang="ts">
import { Command, type Child } from "@tauri-apps/plugin-shell"; import { Command, type Child } from '@tauri-apps/plugin-shell';
import { SvelteMap } from "svelte/reactivity"; import { SvelteMap } from 'svelte/reactivity';
import { Unpackr } from "msgpackr"; import { Unpackr } from 'msgpackr';
import { tick } from "svelte"; import { tick } from 'svelte';
import { VList } from "virtua/svelte"; import { VList } from 'virtua/svelte';
interface UINode { interface UINode {
id: number; id: number;
@ -21,7 +21,7 @@
type ListItem = { type ListItem = {
id: number; id: number;
type: "header" | "item"; type: 'header' | 'item';
props: Record<string, any>; props: Record<string, any>;
height: number; height: number;
}; };
@ -41,21 +41,21 @@
for (const childId of root.children) { for (const childId of root.children) {
const sectionNode = uiTree.get(childId); const sectionNode = uiTree.get(childId);
if (sectionNode && sectionNode.type === "ListSection") { if (sectionNode && sectionNode.type === 'ListSection') {
newFlatList.push({ newFlatList.push({
id: sectionNode.id, id: sectionNode.id,
type: "header", type: 'header',
props: sectionNode.props, props: sectionNode.props,
height: HEADER_HEIGHT, height: HEADER_HEIGHT
}); });
for (const itemId of sectionNode.children) { for (const itemId of sectionNode.children) {
const itemNode = uiTree.get(itemId); const itemNode = uiTree.get(itemId);
if (itemNode) { if (itemNode) {
newFlatList.push({ newFlatList.push({
id: itemNode.id, id: itemNode.id,
type: "item", type: 'item',
props: itemNode.props, props: itemNode.props,
height: ITEM_HEIGHT, height: ITEM_HEIGHT
}); });
} }
} }
@ -80,7 +80,7 @@
const message = unpackr.unpack(messagePayload); const message = unpackr.unpack(messagePayload);
handleSidecarMessage(message); handleSidecarMessage(message);
} catch (e) { } catch (e) {
console.error("Failed to unpack sidecar message:", e); console.error('Failed to unpack sidecar message:', e);
} }
} else { } else {
break; break;
@ -89,39 +89,36 @@
} }
async function connectAndRun() { async function connectAndRun() {
const command = Command.sidecar("binaries/app", undefined, { const command = Command.sidecar('binaries/app', undefined, {
encoding: "raw", encoding: 'raw'
}); });
command.stdout.on("data", (chunk) => { command.stdout.on('data', (chunk) => {
try { try {
receiveBuffer = Buffer.concat([receiveBuffer, Buffer.from(chunk)]); receiveBuffer = Buffer.concat([receiveBuffer, Buffer.from(chunk)]);
processReceiveBuffer(); processReceiveBuffer();
} catch (e) { } catch (e) {
console.error("Failed to parse sidecar message:", chunk, e); console.error('Failed to parse sidecar message:', chunk, e);
} }
}); });
command.stderr.on("data", (line) => { command.stderr.on('data', (line) => {
sidecarLogs = [...sidecarLogs, `STDERR: ${line}`]; sidecarLogs = [...sidecarLogs, `STDERR: ${line}`];
}); });
sidecarChild = await command.spawn(); sidecarChild = await command.spawn();
sidecarLogs = [ sidecarLogs = [...sidecarLogs, `Sidecar spawned with PID: ${sidecarChild.pid}`];
...sidecarLogs,
`Sidecar spawned with PID: ${sidecarChild.pid}`,
];
if (sidecarChild) { if (sidecarChild) {
sidecarChild.write(JSON.stringify({ action: "run-plugin" }) + "\n"); sidecarChild.write(JSON.stringify({ action: 'run-plugin' }) + '\n');
} }
} }
connectAndRun(); connectAndRun();
return () => { return () => {
console.log("Component unmounting, killing sidecar..."); console.log('Component unmounting, killing sidecar...');
sidecarChild?.kill(); sidecarChild?.kill();
}; };
}); });
function sendToSidecar(message: object) { function sendToSidecar(message: object) {
if (sidecarChild) { if (sidecarChild) {
sidecarChild.write(JSON.stringify(message) + "\n"); sidecarChild.write(JSON.stringify(message) + '\n');
} }
} }
@ -132,7 +129,7 @@
getMutableNode: (id: number) => UINode | undefined getMutableNode: (id: number) => UINode | undefined
) { ) {
switch (command.type) { switch (command.type) {
case "REPLACE_CHILDREN": { case 'REPLACE_CHILDREN': {
const { parentId, childrenIds } = command.payload; const { parentId, childrenIds } = command.payload;
const parentNode = getMutableNode(parentId); const parentNode = getMutableNode(parentId);
if (parentNode) { if (parentNode) {
@ -140,17 +137,17 @@
} }
break; break;
} }
case "log": case 'log':
console.log("SIDECAR:", command.payload); console.log('SIDECAR:', command.payload);
sidecarLogs = [...sidecarLogs, command.payload]; sidecarLogs = [...sidecarLogs, command.payload];
break; break;
case "CREATE_TEXT_INSTANCE": case 'CREATE_TEXT_INSTANCE':
case "CREATE_INSTANCE": { case 'CREATE_INSTANCE': {
const { id, type, props } = command.payload; const { id, type, props } = command.payload;
tempTree.set(id, { id, type, props, children: [] }); tempTree.set(id, { id, type, props, children: [] });
break; break;
} }
case "UPDATE_PROPS": { case 'UPDATE_PROPS': {
const { id, props } = command.payload; const { id, props } = command.payload;
const node = getMutableNode(id); const node = getMutableNode(id);
if (node) { if (node) {
@ -158,9 +155,9 @@
} }
break; break;
} }
case "APPEND_CHILD": { case 'APPEND_CHILD': {
const { parentId, childId } = command.payload; const { parentId, childId } = command.payload;
if (parentId === "root") { if (parentId === 'root') {
tempState.rootNodeId = childId; tempState.rootNodeId = childId;
} else { } else {
const parentNode = getMutableNode(parentId); const parentNode = getMutableNode(parentId);
@ -172,7 +169,7 @@
} }
break; break;
} }
case "REMOVE_CHILD": { case 'REMOVE_CHILD': {
const { parentId, childId } = command.payload; const { parentId, childId } = command.payload;
const parentNode = getMutableNode(parentId); const parentNode = getMutableNode(parentId);
if (parentNode) { if (parentNode) {
@ -181,7 +178,7 @@
} }
break; break;
} }
case "INSERT_BEFORE": { case 'INSERT_BEFORE': {
const { parentId, childId, beforeId } = command.payload; const { parentId, childId, beforeId } = command.payload;
const parentNode = getMutableNode(parentId); const parentNode = getMutableNode(parentId);
if (parentNode) { if (parentNode) {
@ -200,8 +197,7 @@
} }
function handleSidecarMessage(message: any) { function handleSidecarMessage(message: any) {
const commands = const commands = message.type === 'BATCH_UPDATE' ? message.payload : [message];
message.type === "BATCH_UPDATE" ? message.payload : [message];
if (commands.length === 0) { if (commands.length === 0) {
updateCounter++; updateCounter++;
return; return;
@ -216,7 +212,7 @@
const clonedNode = { const clonedNode = {
...originalNode, ...originalNode,
props: { ...originalNode.props }, props: { ...originalNode.props },
children: [...originalNode.children], children: [...originalNode.children]
}; };
tempTree.set(id, clonedNode); tempTree.set(id, clonedNode);
mutatedIds.add(id); mutatedIds.add(id);
@ -236,41 +232,34 @@
} }
function dispatchEvent(instanceId: number, handlerName: string, args: any[]) { function dispatchEvent(instanceId: number, handlerName: string, args: any[]) {
console.log( console.log(`[EVENT] Dispatching '${handlerName}' to instance ${instanceId}`);
`[EVENT] Dispatching '${handlerName}' to instance ${instanceId}`
);
sendToSidecar({ sendToSidecar({
action: "dispatch-event", action: 'dispatch-event',
payload: { instanceId, handlerName, args }, payload: { instanceId, handlerName, args }
}); });
} }
</script> </script>
<main class="flex grow flex-col h-screen"> <main class="flex h-screen grow flex-col">
{#if rootNodeId} {#if rootNodeId}
{@const rootNode = uiTree.get(rootNodeId)} {@const rootNode = uiTree.get(rootNodeId)}
{#if rootNode?.type === "List"} {#if rootNode?.type === 'List'}
<div class="flex h-full flex-col"> <div class="flex h-full flex-col">
<input <input
type="text" type="text"
class="w-full border-b border-gray-300 px-4 py-3 text-lg focus:border-blue-500 focus:outline-none" class="w-full border-b border-gray-300 px-4 py-3 text-lg focus:border-blue-500 focus:outline-none"
placeholder="Search Emojis..." placeholder="Search Emojis..."
oninput={(e) => oninput={(e) => dispatchEvent(rootNode.id, 'onSearchTextChange', [e.currentTarget.value])}
dispatchEvent(rootNode.id, "onSearchTextChange", [
e.currentTarget.value,
])}
/> />
<div class="flex-grow"> <div class="flex-grow">
<VList data={flatList} getKey={(item) => item.id} class="h-full"> <VList data={flatList} getKey={(item) => item.id} class="h-full">
{#snippet children(item)} {#snippet children(item)}
{#if item.type === "header"} {#if item.type === 'header'}
<h3 <h3 class="px-4 pt-2.5 pb-1 text-xs font-semibold text-gray-500 uppercase">
class="px-4 pb-1 pt-2.5 text-xs font-semibold uppercase text-gray-500"
>
{item.props.title} {item.props.title}
</h3> </h3>
{:else if item.type === "item"} {:else if item.type === 'item'}
<div class="flex items-center gap-3 px-4 py-2"> <div class="flex items-center gap-3 px-4 py-2">
<span class="text-lg">{item.props.icon}</span> <span class="text-lg">{item.props.icon}</span>
<span>{item.props.title}</span> <span>{item.props.title}</span>

View file

@ -1,15 +1,15 @@
// Tauri doesn't have a Node.js server to do proper SSR // Tauri doesn't have a Node.js server to do proper SSR
// so we will use adapter-static to prerender the app (SSG) // so we will use adapter-static to prerender the app (SSG)
// See: https://v2.tauri.app/start/frontend/sveltekit/ for more info // See: https://v2.tauri.app/start/frontend/sveltekit/ for more info
import adapter from "@sveltejs/adapter-static"; import adapter from '@sveltejs/adapter-static';
import { vitePreprocess } from "@sveltejs/vite-plugin-svelte"; import { vitePreprocess } from '@sveltejs/vite-plugin-svelte';
/** @type {import('@sveltejs/kit').Config} */ /** @type {import('@sveltejs/kit').Config} */
const config = { const config = {
preprocess: vitePreprocess(), preprocess: vitePreprocess(),
kit: { kit: {
adapter: adapter(), adapter: adapter()
}, }
}; };
export default config; export default config;

View file

@ -1,7 +1,7 @@
import { defineConfig } from "vite"; import { defineConfig } from 'vite';
import tailwindcss from "@tailwindcss/vite"; import tailwindcss from '@tailwindcss/vite';
import { sveltekit } from "@sveltejs/kit/vite"; import { sveltekit } from '@sveltejs/kit/vite';
import { nodePolyfills } from "vite-plugin-node-polyfills"; import { nodePolyfills } from 'vite-plugin-node-polyfills';
const host = process.env.TAURI_DEV_HOST; const host = process.env.TAURI_DEV_HOST;
@ -20,14 +20,14 @@ export default defineConfig(async () => ({
host: host || false, host: host || false,
hmr: host hmr: host
? { ? {
protocol: "ws", protocol: 'ws',
host, host,
port: 1421, port: 1421
} }
: undefined, : undefined,
watch: { watch: {
// 3. tell vite to ignore watching `src-tauri` // 3. tell vite to ignore watching `src-tauri`
ignored: ["**/src-tauri/**"], ignored: ['**/src-tauri/**']
}, }
}, }
})); }));