Skip to content

本页提供从零到一的任务型示例,帮助你在最短时间内完成一个可运行的插件(包含一个 View 命令和一个 No-View 命令)。

前置:

  • 已安装 如快 v0.9.0+
  • 已使用脚手架创建工程,并能运行 pnpm devpnpm build

任务一:添加一个 View 命令(带 UI)

  • 目标:在 UI 中读取/清空搜索框内容、触发截图、并使用插件存储。
  • 步骤:
    1. 在工程中安装依赖:@sofastapp/api
    2. 在 UI 代码中调用 API(示例基于 React,框架不限)。

示例 UI 组件:

tsx
import { useEffect, useMemo, useRef, useState } from 'react';
import { Context, Screenshot, LocalStorage } from '@sofastapp/api';

export default function App() {
  const sid = useMemo(
    () => new URLSearchParams(location.search).get('sid') || '',
    [],
  );
  const [ctx, setCtx] = useState('');
  const unref = useRef<null | (() => void)>(null);
  const [store, setStore] = useState<Record<string, any>>({});

  useEffect(
    () => () => {
      unref.current?.();
      unref.current = null;
    },
    [],
  );

  return (
    <div style={{ padding: 16 }}>
      <h3>My View Command</h3>
      <div style={{ opacity: 0.6, fontSize: 12 }}>sessionId: {sid}</div>

      <div style={{ marginTop: 12, display: 'flex', gap: 8, flexWrap: 'wrap' }}>
        <button onClick={async () => setCtx(await Context.getSearchContent())}>
          读取 Context
        </button>
        <button
          onClick={async () => {
            await Context.clearSearchContent();
            setCtx('');
          }}
        >
          清空 Context
        </button>
        <button
          onClick={() => {
            if (!unref.current)
              unref.current = Context.watchSearchContent(setCtx);
            else {
              unref.current();
              unref.current = null;
            }
          }}
        >
          {unref.current ? '停止监听' : '开始监听'}
        </button>
        <button onClick={() => Screenshot.start()}>开始截图</button>
        <button
          onClick={async () => {
            const k = 'counter';
            const n = (await LocalStorage.getItem<number>(k)) ?? 0;
            await LocalStorage.setItem(k, n + 1);
            setStore(await LocalStorage.allItems<Record<string, any>>());
          }}
        >
          计数+1
        </button>
        <button
          onClick={async () => {
            await LocalStorage.clear();
            setStore({});
          }}
        >
          清空存储
        </button>
      </div>

      <div style={{ marginTop: 12 }}>
        当前 Context:<b>{ctx || '(empty)'}</b>
      </div>
      <pre style={{ marginTop: 12, background: '#fafafa', padding: 8 }}>
        {JSON.stringify(store, null, 2)}
      </pre>
    </div>
  );
}

在清单中声明该命令(节选):

json
{
  "commands": [
    {
      "name": "my-view",
      "title": "My View Command",
      "mode": "view",
      "searchable": true
    }
  ]
}

任务二:添加一个 No-View 命令(无 UI)

  • 目标:在后台执行逻辑、输出日志与进度,并使用插件存储。
  • 步骤:
    1. 安装依赖:@sofastapp/api/node
    2. 新建源文件(如 src/no-view/hello.ts),并通过独立构建配置产出 dist/hello.mjs

最小入口:

ts
import {
  ctx,
  log,
  progress,
  done,
  onError,
  LocalStorage,
} from '@sofastapp/api/node';

onError();
(async () => {
  const { args } = ctx();
  log('hello: start', { args });
  progress(0.2);
  const key = 'demo.counter';
  const n = (await LocalStorage.getItem<number>(key)) ?? 0;
  await LocalStorage.setItem(key, n + 1);
  progress(1);
  done({ ok: true, counter: n + 1 });
})();

No-View 构建(自动发现 src/no-view/*.ts):

ts
// vite.worker.config.ts(要点)
import { defineConfig } from 'vite';
import fs from 'node:fs';
import path from 'node:path';

function discoverNoViewInputs() {
  const inputs: Record<string, string> = {};
  const dir = path.resolve(__dirname, 'src/no-view');
  try {
    for (const it of fs.readdirSync(dir, { withFileTypes: true }))
      if (it.isFile() && it.name.endsWith('.ts'))
        inputs[it.name.replace(/\.ts$/, '')] = path.join(dir, it.name);
  } catch {}
  return inputs;
}

export default defineConfig({
  build: {
    outDir: 'dist',
    emptyOutDir: false,
    rollupOptions: {
      external: ['worker_threads', /^node:.*/],
      input: discoverNoViewInputs(),
      output: {
        entryFileNames: '[name].mjs',
        chunkFileNames: 'assets/[name]-[hash].mjs',
        manualChunks: undefined,
      },
    },
  },
});

在清单中声明该命令(节选):

json
{
  "commands": [
    { "name": "hello", "title": "Hello (No View)", "mode": "no-view" }
  ]
}

任务三:构建与安装

  • 在工程根目录执行:
bash
pnpm build
  • 构建输出目录需要包含:

    • UI 入口与静态资源(如 index.html
    • package.json(包含顶层 commands 清单)
    • No-View 产物(如 hello.mjs
  • 将构建输出目录放置到应用安装目录:

    • <应用安装目录>/extensions/<your-plugin>

完成后,打开应用,通过命令面板搜索并运行:

  • 运行 My View Command 体验 UI 能力
  • 搜索并运行 Hello (No View) 触发后台任务