mirror of https://github.com/Wox-launcher/Wox
feat(mru): Enhance nodejs/python plugin API with MRU restore functionality and dynamic setting improvements
- Updated API interface to support dynamic settings returning PluginSettingDefinitionItem. - Implemented MRU restore callback in both Node.js and Python plugin hosts. - Added MRUData model for handling most recently used data. - Enhanced setting models to include new types and helper functions for creating settings. - Updated package dependencies for Node.js and Python plugins to the latest versions.
This commit is contained in:
parent
4b2018934d
commit
62fdd3b7c7
|
@ -10,6 +10,7 @@ import (
|
||||||
"wox/common"
|
"wox/common"
|
||||||
"wox/i18n"
|
"wox/i18n"
|
||||||
"wox/setting"
|
"wox/setting"
|
||||||
|
"wox/setting/definition"
|
||||||
"wox/util"
|
"wox/util"
|
||||||
|
|
||||||
"github.com/samber/lo"
|
"github.com/samber/lo"
|
||||||
|
@ -34,7 +35,7 @@ type API interface {
|
||||||
GetSetting(ctx context.Context, key string) string
|
GetSetting(ctx context.Context, key string) string
|
||||||
SaveSetting(ctx context.Context, key string, value string, isPlatformSpecific bool)
|
SaveSetting(ctx context.Context, key string, value string, isPlatformSpecific bool)
|
||||||
OnSettingChanged(ctx context.Context, callback func(key string, value string))
|
OnSettingChanged(ctx context.Context, callback func(key string, value string))
|
||||||
OnGetDynamicSetting(ctx context.Context, callback func(key string) string)
|
OnGetDynamicSetting(ctx context.Context, callback func(key string) definition.PluginSettingDefinitionItem)
|
||||||
OnDeepLink(ctx context.Context, callback func(arguments map[string]string))
|
OnDeepLink(ctx context.Context, callback func(arguments map[string]string))
|
||||||
OnUnload(ctx context.Context, callback func())
|
OnUnload(ctx context.Context, callback func())
|
||||||
OnMRURestore(ctx context.Context, callback func(mruData MRUData) (*QueryResult, error))
|
OnMRURestore(ctx context.Context, callback func(mruData MRUData) (*QueryResult, error))
|
||||||
|
@ -142,7 +143,7 @@ func (a *APIImpl) OnSettingChanged(ctx context.Context, callback func(key string
|
||||||
a.pluginInstance.SettingChangeCallbacks = append(a.pluginInstance.SettingChangeCallbacks, callback)
|
a.pluginInstance.SettingChangeCallbacks = append(a.pluginInstance.SettingChangeCallbacks, callback)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *APIImpl) OnGetDynamicSetting(ctx context.Context, callback func(key string) string) {
|
func (a *APIImpl) OnGetDynamicSetting(ctx context.Context, callback func(key string) definition.PluginSettingDefinitionItem) {
|
||||||
a.pluginInstance.DynamicSettingCallbacks = append(a.pluginInstance.DynamicSettingCallbacks, callback)
|
a.pluginInstance.DynamicSettingCallbacks = append(a.pluginInstance.DynamicSettingCallbacks, callback)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -344,24 +344,19 @@ func (w *WebsocketHost) handleRequestFromPlugin(ctx context.Context, request Jso
|
||||||
}
|
}
|
||||||
|
|
||||||
metadata := pluginInstance.Metadata
|
metadata := pluginInstance.Metadata
|
||||||
pluginInstance.API.OnGetDynamicSetting(ctx, func(key string) string {
|
pluginInstance.API.OnGetDynamicSetting(ctx, func(key string) definition.PluginSettingDefinitionItem {
|
||||||
result, err := w.invokeMethod(ctx, metadata, "onGetDynamicSetting", map[string]string{
|
result, err := w.invokeMethod(ctx, metadata, "onGetDynamicSetting", map[string]string{
|
||||||
"CallbackId": callbackId,
|
"CallbackId": callbackId,
|
||||||
"Key": key,
|
"Key": key,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
util.GetLogger().Error(ctx, fmt.Sprintf("[%s] failed to get dynamic setting: %s", request.PluginName, err))
|
util.GetLogger().Error(ctx, fmt.Sprintf("[%s] failed to get dynamic setting: %s", request.PluginName, err))
|
||||||
settingJson, marshalErr := json.Marshal(definition.PluginSettingDefinitionItem{
|
return definition.PluginSettingDefinitionItem{
|
||||||
Type: definition.PluginSettingDefinitionTypeLabel,
|
Type: definition.PluginSettingDefinitionTypeLabel,
|
||||||
Value: &definition.PluginSettingValueLabel{
|
Value: &definition.PluginSettingValueLabel{
|
||||||
Content: fmt.Sprintf("failed to get dynamic setting: %s", err),
|
Content: fmt.Sprintf("failed to get dynamic setting: %s", err),
|
||||||
},
|
},
|
||||||
})
|
|
||||||
if marshalErr != nil {
|
|
||||||
util.GetLogger().Error(ctx, fmt.Sprintf("[%s] failed to marshal dynamic setting: %s", request.PluginName, marshalErr))
|
|
||||||
return ""
|
|
||||||
}
|
}
|
||||||
return string(settingJson)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// validate the result is a valid definition.PluginSettingDefinitionItem json string
|
// validate the result is a valid definition.PluginSettingDefinitionItem json string
|
||||||
|
@ -369,20 +364,15 @@ func (w *WebsocketHost) handleRequestFromPlugin(ctx context.Context, request Jso
|
||||||
unmarshalErr := json.Unmarshal([]byte(result.(string)), &setting)
|
unmarshalErr := json.Unmarshal([]byte(result.(string)), &setting)
|
||||||
if unmarshalErr != nil {
|
if unmarshalErr != nil {
|
||||||
util.GetLogger().Error(ctx, fmt.Sprintf("[%s] failed to unmarshal dynamic setting: %s", request.PluginName, unmarshalErr))
|
util.GetLogger().Error(ctx, fmt.Sprintf("[%s] failed to unmarshal dynamic setting: %s", request.PluginName, unmarshalErr))
|
||||||
settingJson, marshalErr := json.Marshal(definition.PluginSettingDefinitionItem{
|
return definition.PluginSettingDefinitionItem{
|
||||||
Type: definition.PluginSettingDefinitionTypeLabel,
|
Type: definition.PluginSettingDefinitionTypeLabel,
|
||||||
Value: &definition.PluginSettingValueLabel{
|
Value: &definition.PluginSettingValueLabel{
|
||||||
Content: fmt.Sprintf("failed to unmarshal dynamic setting: %s", unmarshalErr),
|
Content: fmt.Sprintf("failed to unmarshal dynamic setting: %s", unmarshalErr),
|
||||||
},
|
},
|
||||||
})
|
|
||||||
if marshalErr != nil {
|
|
||||||
util.GetLogger().Error(ctx, fmt.Sprintf("[%s] failed to marshal dynamic setting: %s", request.PluginName, marshalErr))
|
|
||||||
return ""
|
|
||||||
}
|
}
|
||||||
return string(settingJson)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return result.(string)
|
return setting
|
||||||
})
|
})
|
||||||
w.sendResponseToHost(ctx, request, "")
|
w.sendResponseToHost(ctx, request, "")
|
||||||
case "OnDeepLink":
|
case "OnDeepLink":
|
||||||
|
|
|
@ -2,6 +2,7 @@ package plugin
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"wox/setting"
|
"wox/setting"
|
||||||
|
"wox/setting/definition"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Instance struct {
|
type Instance struct {
|
||||||
|
@ -15,7 +16,7 @@ type Instance struct {
|
||||||
Host Host // plugin host to run this plugin
|
Host Host // plugin host to run this plugin
|
||||||
Setting *setting.PluginSetting // setting for this plugin
|
Setting *setting.PluginSetting // setting for this plugin
|
||||||
|
|
||||||
DynamicSettingCallbacks []func(key string) string // dynamic setting callbacks
|
DynamicSettingCallbacks []func(key string) definition.PluginSettingDefinitionItem // dynamic setting callbacks
|
||||||
SettingChangeCallbacks []func(key string, value string)
|
SettingChangeCallbacks []func(key string, value string)
|
||||||
DeepLinkCallbacks []func(arguments map[string]string)
|
DeepLinkCallbacks []func(arguments map[string]string)
|
||||||
UnloadCallbacks []func()
|
UnloadCallbacks []func()
|
||||||
|
|
|
@ -202,13 +202,7 @@ func convertPluginDto(ctx context.Context, pluginDto dto.PluginDto, pluginInstan
|
||||||
if settingDefinition.Type == definition.PluginSettingDefinitionTypeDynamic {
|
if settingDefinition.Type == definition.PluginSettingDefinitionTypeDynamic {
|
||||||
replaced := false
|
replaced := false
|
||||||
for _, callback := range pluginInstance.DynamicSettingCallbacks {
|
for _, callback := range pluginInstance.DynamicSettingCallbacks {
|
||||||
newSettingDefinitionJson := callback(settingDefinition.Value.GetKey())
|
newSettingDefinition := callback(settingDefinition.Value.GetKey())
|
||||||
var newSettingDefinition definition.PluginSettingDefinitionItem
|
|
||||||
unmarshalErr := json.Unmarshal([]byte(newSettingDefinitionJson), &newSettingDefinition)
|
|
||||||
if unmarshalErr != nil {
|
|
||||||
logger.Error(ctx, fmt.Sprintf("failed to unmarshal dynamic setting: %s", unmarshalErr.Error()))
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if newSettingDefinition.Value != nil && newSettingDefinition.Type != definition.PluginSettingDefinitionTypeDynamic {
|
if newSettingDefinition.Value != nil && newSettingDefinition.Type != definition.PluginSettingDefinitionTypeDynamic {
|
||||||
logger.Debug(ctx, fmt.Sprintf("dynamic setting replaced: %s(%s) -> %s(%s)", settingDefinition.Value.GetKey(), settingDefinition.Type, newSettingDefinition.Value.GetKey(), newSettingDefinition.Type))
|
logger.Debug(ctx, fmt.Sprintf("dynamic setting replaced: %s(%s) -> %s(%s)", settingDefinition.Value.GetKey(), settingDefinition.Type, newSettingDefinition.Value.GetKey(), newSettingDefinition.Type))
|
||||||
pluginDto.SettingDefinitions[i] = newSettingDefinition
|
pluginDto.SettingDefinitions[i] = newSettingDefinition
|
||||||
|
|
|
@ -30,7 +30,7 @@
|
||||||
"typescript": "^5.2.2"
|
"typescript": "^5.2.2"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@wox-launcher/wox-plugin": "^0.0.82",
|
"@wox-launcher/wox-plugin": "^0.0.85",
|
||||||
"dayjs": "^1.11.13",
|
"dayjs": "^1.11.13",
|
||||||
"promise-deferred": "^2.0.4",
|
"promise-deferred": "^2.0.4",
|
||||||
"winston": "^3.17.0",
|
"winston": "^3.17.0",
|
||||||
|
|
|
@ -6,8 +6,8 @@ settings:
|
||||||
|
|
||||||
dependencies:
|
dependencies:
|
||||||
'@wox-launcher/wox-plugin':
|
'@wox-launcher/wox-plugin':
|
||||||
specifier: ^0.0.82
|
specifier: ^0.0.85
|
||||||
version: 0.0.82
|
version: 0.0.85
|
||||||
dayjs:
|
dayjs:
|
||||||
specifier: ^1.11.13
|
specifier: ^1.11.13
|
||||||
version: 1.11.13
|
version: 1.11.13
|
||||||
|
@ -1561,8 +1561,8 @@ packages:
|
||||||
hasBin: true
|
hasBin: true
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/@wox-launcher/wox-plugin@0.0.82:
|
/@wox-launcher/wox-plugin@0.0.85:
|
||||||
resolution: {integrity: sha512-BV2I/I7Bu2hperlqdJ2aqZd1j3ZZJk7bsWKChNVvctQ1D6mXoREEexgWYjF8cq11FNMKhMnj9nrRZZjccrD27g==}
|
resolution: {integrity: sha512-1QbLcd/RvA0oNpfj4CkdpBjiqW8wV5XLwN6Ps1VJGZMrKCuR5S8n/cvMYeVM/IlqT6i8sswKcn014uwPa27ADA==}
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/JSONStream@1.3.5:
|
/JSONStream@1.3.5:
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { logger } from "./logger"
|
import { logger } from "./logger"
|
||||||
import path from "path"
|
import path from "path"
|
||||||
import { PluginAPI } from "./pluginAPI"
|
import { PluginAPI } from "./pluginAPI"
|
||||||
import { Context, MapString, Plugin, PluginInitParams, Query, QueryEnv, RefreshableResult, Result, ResultAction, Selection } from "@wox-launcher/wox-plugin"
|
import { Context, MapString, Plugin, PluginInitParams, Query, QueryEnv, RefreshableResult, Result, ResultAction, Selection, MRUData } from "@wox-launcher/wox-plugin"
|
||||||
import { WebSocket } from "ws"
|
import { WebSocket } from "ws"
|
||||||
import * as crypto from "crypto"
|
import * as crypto from "crypto"
|
||||||
import { AI } from "@wox-launcher/wox-plugin/types/ai"
|
import { AI } from "@wox-launcher/wox-plugin/types/ai"
|
||||||
|
@ -41,6 +41,8 @@ export async function handleRequestFromWox(ctx: Context, request: PluginJsonRpcR
|
||||||
return onUnload(ctx, request)
|
return onUnload(ctx, request)
|
||||||
case "onLLMStream":
|
case "onLLMStream":
|
||||||
return onLLMStream(ctx, request)
|
return onLLMStream(ctx, request)
|
||||||
|
case "onMRURestore":
|
||||||
|
return onMRURestore(ctx, request)
|
||||||
default:
|
default:
|
||||||
logger.info(ctx, `unknown method handler: ${request.Method}`)
|
logger.info(ctx, `unknown method handler: ${request.Method}`)
|
||||||
throw new Error(`unknown method handler: ${request.Method}`)
|
throw new Error(`unknown method handler: ${request.Method}`)
|
||||||
|
@ -107,7 +109,8 @@ async function initPlugin(ctx: Context, request: PluginJsonRpcRequest, ws: WebSo
|
||||||
const init = getMethod(ctx, request, "init")
|
const init = getMethod(ctx, request, "init")
|
||||||
const pluginApi = new PluginAPI(ws, request.PluginId, request.PluginName)
|
const pluginApi = new PluginAPI(ws, request.PluginId, request.PluginName)
|
||||||
plugin.API = pluginApi
|
plugin.API = pluginApi
|
||||||
return init(ctx, { API: pluginApi, PluginDirectory: request.Params.PluginDirectory } as PluginInitParams)
|
const initParams: PluginInitParams = { API: pluginApi, PluginDirectory: request.Params.PluginDirectory }
|
||||||
|
return init(ctx, initParams)
|
||||||
}
|
}
|
||||||
|
|
||||||
async function onPluginSettingChange(ctx: Context, request: PluginJsonRpcRequest) {
|
async function onPluginSettingChange(ctx: Context, request: PluginJsonRpcRequest) {
|
||||||
|
@ -253,7 +256,7 @@ async function action(ctx: Context, request: PluginJsonRpcRequest) {
|
||||||
pluginAction({
|
pluginAction({
|
||||||
ContextData: request.Params.ContextData
|
ContextData: request.Params.ContextData
|
||||||
})
|
})
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -298,13 +301,48 @@ async function refresh(ctx: Context, request: PluginJsonRpcRequest) {
|
||||||
Tails: refreshedResult.Tails,
|
Tails: refreshedResult.Tails,
|
||||||
ContextData: refreshedResult.ContextData,
|
ContextData: refreshedResult.ContextData,
|
||||||
RefreshInterval: refreshedResult.RefreshInterval,
|
RefreshInterval: refreshedResult.RefreshInterval,
|
||||||
Actions: refreshedResult.Actions.map(action => ({
|
Actions: refreshedResult.Actions.map(
|
||||||
Id: action.Id,
|
action =>
|
||||||
Name: action.Name,
|
({
|
||||||
Icon: action.Icon,
|
Id: action.Id,
|
||||||
IsDefault: action.IsDefault,
|
Name: action.Name,
|
||||||
PreventHideAfterAction: action.PreventHideAfterAction,
|
Icon: action.Icon,
|
||||||
Hotkey: action.Hotkey,
|
IsDefault: action.IsDefault,
|
||||||
} as ResultActionUI))
|
PreventHideAfterAction: action.PreventHideAfterAction,
|
||||||
|
Hotkey: action.Hotkey
|
||||||
|
}) as ResultActionUI
|
||||||
|
)
|
||||||
} as RefreshableResultWithResultId
|
} as RefreshableResultWithResultId
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function onMRURestore(ctx: Context, request: PluginJsonRpcRequest): Promise<Result | null> {
|
||||||
|
const pluginInstance = pluginInstances.get(request.PluginId)
|
||||||
|
if (!pluginInstance) {
|
||||||
|
throw new Error(`plugin instance not found: ${request.PluginId}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
const callbackId = request.Params.callbackId
|
||||||
|
const mruDataRaw = JSON.parse(request.Params.mruData)
|
||||||
|
|
||||||
|
// Convert raw data to MRUData type
|
||||||
|
const mruData: MRUData = {
|
||||||
|
PluginID: mruDataRaw.PluginID || "",
|
||||||
|
Title: mruDataRaw.Title || "",
|
||||||
|
SubTitle: mruDataRaw.SubTitle || "",
|
||||||
|
Icon: mruDataRaw.Icon || { ImageType: "absolute", ImageData: "" },
|
||||||
|
ContextData: mruDataRaw.ContextData || ""
|
||||||
|
}
|
||||||
|
|
||||||
|
const callback = pluginInstance.API.mruRestoreCallbacks.get(callbackId)
|
||||||
|
if (!callback) {
|
||||||
|
throw new Error(`MRU restore callback not found: ${callbackId}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const result = await callback(mruData)
|
||||||
|
return result
|
||||||
|
} catch (error) {
|
||||||
|
logger.error(ctx, `MRU restore callback error: ${error}`)
|
||||||
|
throw error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { ChangeQueryParam, Context, MapString, PublicAPI } from "@wox-launcher/wox-plugin"
|
import { ChangeQueryParam, Context, MapString, PublicAPI, Result } from "@wox-launcher/wox-plugin"
|
||||||
import { WebSocket } from "ws"
|
import { WebSocket } from "ws"
|
||||||
import * as crypto from "crypto"
|
import * as crypto from "crypto"
|
||||||
import { waitingForResponse } from "./index"
|
import { waitingForResponse } from "./index"
|
||||||
|
@ -6,6 +6,7 @@ import Deferred from "promise-deferred"
|
||||||
import { logger } from "./logger"
|
import { logger } from "./logger"
|
||||||
import { MetadataCommand, PluginSettingDefinitionItem } from "@wox-launcher/wox-plugin/types/setting"
|
import { MetadataCommand, PluginSettingDefinitionItem } from "@wox-launcher/wox-plugin/types/setting"
|
||||||
import { AI } from "@wox-launcher/wox-plugin/types/ai"
|
import { AI } from "@wox-launcher/wox-plugin/types/ai"
|
||||||
|
import { MRUData } from "@wox-launcher/wox-plugin"
|
||||||
import { PluginJsonRpcTypeRequest } from "./jsonrpc"
|
import { PluginJsonRpcTypeRequest } from "./jsonrpc"
|
||||||
import { PluginJsonRpcRequest } from "./types"
|
import { PluginJsonRpcRequest } from "./types"
|
||||||
|
|
||||||
|
@ -18,6 +19,7 @@ export class PluginAPI implements PublicAPI {
|
||||||
deepLinkCallbacks: Map<string, (params: MapString) => void>
|
deepLinkCallbacks: Map<string, (params: MapString) => void>
|
||||||
unloadCallbacks: Map<string, () => Promise<void>>
|
unloadCallbacks: Map<string, () => Promise<void>>
|
||||||
llmStreamCallbacks: Map<string, AI.ChatStreamFunc>
|
llmStreamCallbacks: Map<string, AI.ChatStreamFunc>
|
||||||
|
mruRestoreCallbacks: Map<string, (mruData: MRUData) => Promise<Result | null>>
|
||||||
|
|
||||||
constructor(ws: WebSocket, pluginId: string, pluginName: string) {
|
constructor(ws: WebSocket, pluginId: string, pluginName: string) {
|
||||||
this.ws = ws
|
this.ws = ws
|
||||||
|
@ -28,6 +30,7 @@ export class PluginAPI implements PublicAPI {
|
||||||
this.deepLinkCallbacks = new Map<string, (params: MapString) => void>()
|
this.deepLinkCallbacks = new Map<string, (params: MapString) => void>()
|
||||||
this.unloadCallbacks = new Map<string, () => Promise<void>>()
|
this.unloadCallbacks = new Map<string, () => Promise<void>>()
|
||||||
this.llmStreamCallbacks = new Map<string, AI.ChatStreamFunc>()
|
this.llmStreamCallbacks = new Map<string, AI.ChatStreamFunc>()
|
||||||
|
this.mruRestoreCallbacks = new Map<string, (mruData: MRUData) => Promise<Result | null>>()
|
||||||
}
|
}
|
||||||
|
|
||||||
async invokeMethod(ctx: Context, method: string, params: { [key: string]: string }): Promise<unknown> {
|
async invokeMethod(ctx: Context, method: string, params: { [key: string]: string }): Promise<unknown> {
|
||||||
|
@ -124,4 +127,10 @@ export class PluginAPI implements PublicAPI {
|
||||||
this.llmStreamCallbacks.set(callbackId, callback)
|
this.llmStreamCallbacks.set(callbackId, callback)
|
||||||
await this.invokeMethod(ctx, "LLMStream", { callbackId, conversations: JSON.stringify(conversations) })
|
await this.invokeMethod(ctx, "LLMStream", { callbackId, conversations: JSON.stringify(conversations) })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async OnMRURestore(ctx: Context, callback: (mruData: MRUData) => Promise<Result | null>): Promise<void> {
|
||||||
|
const callbackId = crypto.randomUUID()
|
||||||
|
this.mruRestoreCallbacks.set(callbackId, callback)
|
||||||
|
await this.invokeMethod(ctx, "OnMRURestore", { callbackId })
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,7 @@ readme = "README.md"
|
||||||
requires-python = ">=3.10"
|
requires-python = ">=3.10"
|
||||||
license = "GPL-3.0"
|
license = "GPL-3.0"
|
||||||
authors = [{ name = "Wox Team", email = "qianlifeng@gmail.com" }]
|
authors = [{ name = "Wox Team", email = "qianlifeng@gmail.com" }]
|
||||||
dependencies = ["loguru", "websockets", "wox-plugin==0.0.48"]
|
dependencies = ["loguru", "websockets", "wox-plugin==0.0.49"]
|
||||||
|
|
||||||
[project.scripts]
|
[project.scripts]
|
||||||
run = "wox_plugin_host.__main__:run"
|
run = "wox_plugin_host.__main__:run"
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import json
|
import json
|
||||||
import importlib.util
|
import importlib
|
||||||
from os import path
|
from os import path
|
||||||
import sys
|
import sys
|
||||||
from typing import Any, Dict
|
from typing import Any, Dict
|
||||||
|
@ -12,6 +12,7 @@ from wox_plugin import (
|
||||||
RefreshableResult,
|
RefreshableResult,
|
||||||
PluginInitParams,
|
PluginInitParams,
|
||||||
ActionContext,
|
ActionContext,
|
||||||
|
MRUData,
|
||||||
)
|
)
|
||||||
from .plugin_manager import plugin_instances, PluginInstance
|
from .plugin_manager import plugin_instances, PluginInstance
|
||||||
from .plugin_api import PluginAPI
|
from .plugin_api import PluginAPI
|
||||||
|
@ -38,6 +39,8 @@ async def handle_request_from_wox(ctx: Context, request: Dict[str, Any], ws: web
|
||||||
return await refresh(ctx, request)
|
return await refresh(ctx, request)
|
||||||
elif method == "unloadPlugin":
|
elif method == "unloadPlugin":
|
||||||
return await unload_plugin(ctx, request)
|
return await unload_plugin(ctx, request)
|
||||||
|
elif method == "onMRURestore":
|
||||||
|
return await on_mru_restore(ctx, request)
|
||||||
else:
|
else:
|
||||||
await logger.info(ctx.get_trace_id(), f"unknown method handler: {method}")
|
await logger.info(ctx.get_trace_id(), f"unknown method handler: {method}")
|
||||||
raise Exception(f"unknown method handler: {method}")
|
raise Exception(f"unknown method handler: {method}")
|
||||||
|
@ -325,3 +328,49 @@ async def unload_plugin(ctx: Context, request: Dict[str, Any]) -> None:
|
||||||
f"<{plugin_name}> unload plugin failed: {str(e)}\nStack trace:\n{error_stack}",
|
f"<{plugin_name}> unload plugin failed: {str(e)}\nStack trace:\n{error_stack}",
|
||||||
)
|
)
|
||||||
raise e
|
raise e
|
||||||
|
|
||||||
|
|
||||||
|
async def on_mru_restore(ctx: Context, request: Dict[str, Any]) -> Any:
|
||||||
|
"""Handle MRU restore callback"""
|
||||||
|
plugin_id = request.get("PluginId")
|
||||||
|
if not plugin_id:
|
||||||
|
raise Exception("PluginId is required")
|
||||||
|
|
||||||
|
params = request.get("Params", {})
|
||||||
|
callback_id = params.get("callbackId")
|
||||||
|
mru_data_dict = json.loads(params.get("mruData", "{}"))
|
||||||
|
|
||||||
|
plugin_instance = plugin_instances.get(plugin_id)
|
||||||
|
if not plugin_instance:
|
||||||
|
raise Exception(f"plugin instance not found: {plugin_id}")
|
||||||
|
|
||||||
|
if not plugin_instance.api:
|
||||||
|
raise Exception(f"plugin API not found: {plugin_id}")
|
||||||
|
|
||||||
|
# Type cast to access implementation-specific attributes
|
||||||
|
from .plugin_api import PluginAPI
|
||||||
|
|
||||||
|
api = plugin_instance.api
|
||||||
|
if not isinstance(api, PluginAPI):
|
||||||
|
raise Exception(f"Invalid API type for plugin: {plugin_id}")
|
||||||
|
|
||||||
|
callback = api.mru_restore_callbacks.get(callback_id)
|
||||||
|
if not callback:
|
||||||
|
raise Exception(f"MRU restore callback not found: {callback_id}")
|
||||||
|
|
||||||
|
try:
|
||||||
|
# Convert dict to MRUData object for type safety
|
||||||
|
mru_data = MRUData.from_dict(mru_data_dict)
|
||||||
|
|
||||||
|
# Call the callback (may or may not be async)
|
||||||
|
result = callback(mru_data)
|
||||||
|
if hasattr(result, "__await__"):
|
||||||
|
result = await result # type: ignore
|
||||||
|
|
||||||
|
# Convert Result object back to dict for JSON serialization
|
||||||
|
if result is not None:
|
||||||
|
return result.__dict__
|
||||||
|
return None
|
||||||
|
except Exception as e:
|
||||||
|
await logger.error(ctx.get_trace_id(), f"MRU restore callback error: {str(e)}")
|
||||||
|
raise e
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import asyncio
|
import asyncio
|
||||||
import json
|
import json
|
||||||
import uuid
|
import uuid
|
||||||
from typing import Any, Dict, Callable
|
from typing import Any, Dict, Callable, Optional
|
||||||
import websockets
|
import websockets
|
||||||
from . import logger
|
from . import logger
|
||||||
from wox_plugin import (
|
from wox_plugin import (
|
||||||
|
@ -12,6 +12,9 @@ from wox_plugin import (
|
||||||
Conversation,
|
Conversation,
|
||||||
AIModel,
|
AIModel,
|
||||||
ChatStreamCallback,
|
ChatStreamCallback,
|
||||||
|
MRUData,
|
||||||
|
Result,
|
||||||
|
PluginSettingDefinitionItem,
|
||||||
)
|
)
|
||||||
from .constants import PLUGIN_JSONRPC_TYPE_REQUEST
|
from .constants import PLUGIN_JSONRPC_TYPE_REQUEST
|
||||||
from .plugin_manager import waiting_for_response
|
from .plugin_manager import waiting_for_response
|
||||||
|
@ -23,10 +26,11 @@ class PluginAPI(PublicAPI):
|
||||||
self.plugin_id = plugin_id
|
self.plugin_id = plugin_id
|
||||||
self.plugin_name = plugin_name
|
self.plugin_name = plugin_name
|
||||||
self.setting_change_callbacks: Dict[str, Callable[[str, str], None]] = {}
|
self.setting_change_callbacks: Dict[str, Callable[[str, str], None]] = {}
|
||||||
self.get_dynamic_setting_callbacks: Dict[str, Callable[[str], str]] = {}
|
self.get_dynamic_setting_callbacks: Dict[str, Callable[[str], PluginSettingDefinitionItem]] = {}
|
||||||
self.deep_link_callbacks: Dict[str, Callable[[Dict[str, str]], None]] = {}
|
self.deep_link_callbacks: Dict[str, Callable[[Dict[str, str]], None]] = {}
|
||||||
self.unload_callbacks: Dict[str, Callable[[], None]] = {}
|
self.unload_callbacks: Dict[str, Callable[[], None]] = {}
|
||||||
self.llm_stream_callbacks: Dict[str, ChatStreamCallback] = {}
|
self.llm_stream_callbacks: Dict[str, ChatStreamCallback] = {}
|
||||||
|
self.mru_restore_callbacks: Dict[str, Callable[[MRUData], Optional[Result]]] = {}
|
||||||
|
|
||||||
async def invoke_method(self, ctx: Context, method: str, params: Dict[str, Any]) -> Any:
|
async def invoke_method(self, ctx: Context, method: str, params: Dict[str, Any]) -> Any:
|
||||||
"""Invoke a method on Wox"""
|
"""Invoke a method on Wox"""
|
||||||
|
@ -110,7 +114,7 @@ class PluginAPI(PublicAPI):
|
||||||
self.setting_change_callbacks[callback_id] = callback
|
self.setting_change_callbacks[callback_id] = callback
|
||||||
await self.invoke_method(ctx, "OnSettingChanged", {"callbackId": callback_id})
|
await self.invoke_method(ctx, "OnSettingChanged", {"callbackId": callback_id})
|
||||||
|
|
||||||
async def on_get_dynamic_setting(self, ctx: Context, callback: Callable[[str], str]) -> None:
|
async def on_get_dynamic_setting(self, ctx: Context, callback: Callable[[str], PluginSettingDefinitionItem]) -> None:
|
||||||
"""Register dynamic setting callback"""
|
"""Register dynamic setting callback"""
|
||||||
callback_id = str(uuid.uuid4())
|
callback_id = str(uuid.uuid4())
|
||||||
self.get_dynamic_setting_callbacks[callback_id] = callback
|
self.get_dynamic_setting_callbacks[callback_id] = callback
|
||||||
|
@ -154,3 +158,9 @@ class PluginAPI(PublicAPI):
|
||||||
"conversations": json.dumps([conv.__dict__ for conv in conversations]),
|
"conversations": json.dumps([conv.__dict__ for conv in conversations]),
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
async def on_mru_restore(self, ctx: Context, callback: Callable[[MRUData], Optional[Result]]) -> None:
|
||||||
|
"""Register MRU restore callback"""
|
||||||
|
callback_id = str(uuid.uuid4())
|
||||||
|
self.mru_restore_callbacks[callback_id] = callback
|
||||||
|
await self.invoke_method(ctx, "OnMRURestore", {"callbackId": callback_id})
|
||||||
|
|
|
@ -253,11 +253,11 @@ wheels = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wox-plugin"
|
name = "wox-plugin"
|
||||||
version = "0.0.48"
|
version = "0.0.49"
|
||||||
source = { registry = "https://pypi.org/simple" }
|
source = { registry = "https://pypi.org/simple" }
|
||||||
sdist = { url = "https://files.pythonhosted.org/packages/59/e7/1a4f6701f653d7243d8ec35b29aad8e377760e03a61092e3950b53f2aadb/wox_plugin-0.0.48.tar.gz", hash = "sha256:132820a60dddc5d130c049ce302aba519b10e6a409e87453be393472f55153ba", size = 34994 }
|
sdist = { url = "https://files.pythonhosted.org/packages/6f/66/9961ded8a71981736334b2f95b70340903c233df6bdaf256bd6b2107255f/wox_plugin-0.0.49.tar.gz", hash = "sha256:734437d909d14f2b96509cf4d66e57731bff9fe6344dbfada4a93ce843f8eb44", size = 36975 }
|
||||||
wheels = [
|
wheels = [
|
||||||
{ url = "https://files.pythonhosted.org/packages/f2/68/0b131ae43f8a95fb2f8eb6f36c69606accb0c2729544ea92082256bd9957/wox_plugin-0.0.48-py3-none-any.whl", hash = "sha256:5a34cd5a59f8dbd218a45618ee00ce0c4c50e03d39f1dc237fcbd0ad34f013d5", size = 10386 },
|
{ url = "https://files.pythonhosted.org/packages/e6/53/dc3a17c6e9668b903fe5f4ff2d04c949f350cd3e556535c95c056b6d9491/wox_plugin-0.0.49-py3-none-any.whl", hash = "sha256:cd33ea38c5aecc576c2aefbe5427e04400ff13de997b1126f6d7d9e426edb446", size = 13125 },
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -284,5 +284,5 @@ requires-dist = [
|
||||||
{ name = "ruff", marker = "extra == 'dev'" },
|
{ name = "ruff", marker = "extra == 'dev'" },
|
||||||
{ name = "shiv", marker = "extra == 'dev'" },
|
{ name = "shiv", marker = "extra == 'dev'" },
|
||||||
{ name = "websockets" },
|
{ name = "websockets" },
|
||||||
{ name = "wox-plugin", specifier = "==0.0.48" },
|
{ name = "wox-plugin", specifier = "==0.0.49" },
|
||||||
]
|
]
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "@wox-launcher/wox-plugin",
|
"name": "@wox-launcher/wox-plugin",
|
||||||
"version": "0.0.83",
|
"version": "0.0.85",
|
||||||
"description": "All nodejs plugin for Wox should use types in this package",
|
"description": "All nodejs plugin for Wox should use types in this package",
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
|
@ -29,4 +29,4 @@
|
||||||
"typescript": "^5.4.5"
|
"typescript": "^5.4.5"
|
||||||
},
|
},
|
||||||
"dependencies": {}
|
"dependencies": {}
|
||||||
}
|
}
|
|
@ -152,6 +152,14 @@ export interface ActionContext {
|
||||||
ContextData: string
|
ContextData: string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface MRUData {
|
||||||
|
PluginID: string
|
||||||
|
Title: string
|
||||||
|
SubTitle: string
|
||||||
|
Icon: WoxImage
|
||||||
|
ContextData: string
|
||||||
|
}
|
||||||
|
|
||||||
export interface PluginInitParams {
|
export interface PluginInitParams {
|
||||||
API: PublicAPI
|
API: PublicAPI
|
||||||
PluginDirectory: string
|
PluginDirectory: string
|
||||||
|
@ -216,7 +224,7 @@ export interface PublicAPI {
|
||||||
/**
|
/**
|
||||||
* Get dynamic setting definition
|
* Get dynamic setting definition
|
||||||
*/
|
*/
|
||||||
OnGetDynamicSetting: (ctx: Context, callback: (key: string) => string) => Promise<void>
|
OnGetDynamicSetting: (ctx: Context, callback: (key: string) => PluginSettingDefinitionItem) => Promise<void>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Register deep link callback
|
* Register deep link callback
|
||||||
|
@ -237,6 +245,14 @@ export interface PublicAPI {
|
||||||
* Chat using LLM
|
* Chat using LLM
|
||||||
*/
|
*/
|
||||||
LLMStream: (ctx: Context, conversations: AI.Conversation[], callback: AI.ChatStreamFunc) => Promise<void>
|
LLMStream: (ctx: Context, conversations: AI.Conversation[], callback: AI.ChatStreamFunc) => Promise<void>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register MRU restore callback
|
||||||
|
* @param ctx Context
|
||||||
|
* @param callback Callback function that takes MRUData and returns Result or null
|
||||||
|
* Return null if the MRU data is no longer valid
|
||||||
|
*/
|
||||||
|
OnMRURestore: (ctx: Context, callback: (mruData: MRUData) => Promise<Result | null>) => Promise<void>
|
||||||
}
|
}
|
||||||
|
|
||||||
export type WoxImageType = "absolute" | "relative" | "base64" | "svg" | "url" | "emoji" | "lottie"
|
export type WoxImageType = "absolute" | "relative" | "base64" | "svg" | "url" | "emoji" | "lottie"
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
[project]
|
[project]
|
||||||
name = "wox-plugin"
|
name = "wox-plugin"
|
||||||
version = "0.0.48"
|
version = "0.0.49"
|
||||||
description = "Python plugin SDK for Wox launcher"
|
description = "Python plugin SDK for Wox launcher"
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
requires-python = ">=3.10"
|
requires-python = ">=3.10"
|
||||||
|
|
|
@ -35,6 +35,19 @@ from .models.ai import (
|
||||||
)
|
)
|
||||||
from .models.image import WoxImage, WoxImageType
|
from .models.image import WoxImage, WoxImageType
|
||||||
from .models.preview import WoxPreview, WoxPreviewType, WoxPreviewScrollPosition
|
from .models.preview import WoxPreview, WoxPreviewType, WoxPreviewScrollPosition
|
||||||
|
from .models.mru import MRUData, MRURestoreCallback
|
||||||
|
from .models.setting import (
|
||||||
|
PluginSettingDefinitionItem,
|
||||||
|
PluginSettingDefinitionType,
|
||||||
|
PluginSettingDefinitionValue,
|
||||||
|
PluginSettingValueStyle,
|
||||||
|
PluginSettingValueTextBox,
|
||||||
|
PluginSettingValueCheckBox,
|
||||||
|
PluginSettingValueLabel,
|
||||||
|
create_textbox_setting,
|
||||||
|
create_checkbox_setting,
|
||||||
|
create_label_setting,
|
||||||
|
)
|
||||||
|
|
||||||
__all__: List[str] = [
|
__all__: List[str] = [
|
||||||
# Plugin
|
# Plugin
|
||||||
|
@ -84,4 +97,18 @@ __all__: List[str] = [
|
||||||
"WoxPreviewScrollPosition",
|
"WoxPreviewScrollPosition",
|
||||||
# Result
|
# Result
|
||||||
"ResultTailType",
|
"ResultTailType",
|
||||||
|
# MRU
|
||||||
|
"MRUData",
|
||||||
|
"MRURestoreCallback",
|
||||||
|
# Settings
|
||||||
|
"PluginSettingDefinitionItem",
|
||||||
|
"PluginSettingDefinitionType",
|
||||||
|
"PluginSettingDefinitionValue",
|
||||||
|
"PluginSettingValueStyle",
|
||||||
|
"PluginSettingValueTextBox",
|
||||||
|
"PluginSettingValueCheckBox",
|
||||||
|
"PluginSettingValueLabel",
|
||||||
|
"create_textbox_setting",
|
||||||
|
"create_checkbox_setting",
|
||||||
|
"create_label_setting",
|
||||||
]
|
]
|
||||||
|
|
|
@ -1,9 +1,12 @@
|
||||||
from typing import Protocol, Callable, Dict, List
|
from typing import Protocol, Callable, Dict, List, Optional
|
||||||
|
|
||||||
from .models.query import MetadataCommand
|
from .models.query import MetadataCommand
|
||||||
from .models.context import Context
|
from .models.context import Context
|
||||||
from .models.query import ChangeQueryParam
|
from .models.query import ChangeQueryParam
|
||||||
from .models.ai import AIModel, Conversation, ChatStreamCallback
|
from .models.ai import AIModel, Conversation, ChatStreamCallback
|
||||||
|
from .models.mru import MRUData
|
||||||
|
from .models.result import Result
|
||||||
|
from .models.setting import PluginSettingDefinitionItem
|
||||||
|
|
||||||
|
|
||||||
class PublicAPI(Protocol):
|
class PublicAPI(Protocol):
|
||||||
|
@ -45,7 +48,7 @@ class PublicAPI(Protocol):
|
||||||
"""Register setting change callback"""
|
"""Register setting change callback"""
|
||||||
...
|
...
|
||||||
|
|
||||||
async def on_get_dynamic_setting(self, ctx: Context, callback: Callable[[str], str]) -> None:
|
async def on_get_dynamic_setting(self, ctx: Context, callback: Callable[[str], PluginSettingDefinitionItem]) -> None:
|
||||||
"""Register dynamic setting callback"""
|
"""Register dynamic setting callback"""
|
||||||
...
|
...
|
||||||
|
|
||||||
|
@ -81,3 +84,13 @@ class PublicAPI(Protocol):
|
||||||
- data: str, the stream content
|
- data: str, the stream content
|
||||||
"""
|
"""
|
||||||
...
|
...
|
||||||
|
|
||||||
|
async def on_mru_restore(self, ctx: Context, callback: Callable[[MRUData], Optional[Result]]) -> None:
|
||||||
|
"""Register MRU restore callback
|
||||||
|
|
||||||
|
Args:
|
||||||
|
ctx: Context
|
||||||
|
callback: Callback function that takes MRUData and returns Result or None
|
||||||
|
Return None if the MRU data is no longer valid
|
||||||
|
"""
|
||||||
|
...
|
||||||
|
|
|
@ -36,6 +36,11 @@ class WoxImage:
|
||||||
def from_json(cls, json_str: str) -> "WoxImage":
|
def from_json(cls, json_str: str) -> "WoxImage":
|
||||||
"""Create from JSON string with camelCase naming"""
|
"""Create from JSON string with camelCase naming"""
|
||||||
data = json.loads(json_str)
|
data = json.loads(json_str)
|
||||||
|
return cls.from_dict(data)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_dict(cls, data: dict) -> "WoxImage":
|
||||||
|
"""Create from dictionary with camelCase naming"""
|
||||||
if not data.get("ImageType"):
|
if not data.get("ImageType"):
|
||||||
data["ImageType"] = WoxImageType.ABSOLUTE
|
data["ImageType"] = WoxImageType.ABSOLUTE
|
||||||
|
|
||||||
|
@ -44,6 +49,13 @@ class WoxImage:
|
||||||
image_data=data.get("ImageData", ""),
|
image_data=data.get("ImageData", ""),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def to_dict(self) -> dict:
|
||||||
|
"""Convert to dictionary with camelCase naming"""
|
||||||
|
return {
|
||||||
|
"ImageData": self.image_data,
|
||||||
|
"ImageType": self.image_type,
|
||||||
|
}
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def new_base64(cls, data: str) -> "WoxImage":
|
def new_base64(cls, data: str) -> "WoxImage":
|
||||||
"""Create a new base64 image"""
|
"""Create a new base64 image"""
|
||||||
|
|
|
@ -0,0 +1,42 @@
|
||||||
|
from dataclasses import dataclass
|
||||||
|
from typing import Optional, Callable, TYPE_CHECKING
|
||||||
|
from .image import WoxImage
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from .result import Result
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class MRUData:
|
||||||
|
"""MRU (Most Recently Used) data structure"""
|
||||||
|
plugin_id: str
|
||||||
|
title: str
|
||||||
|
sub_title: str
|
||||||
|
icon: WoxImage
|
||||||
|
context_data: str
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_dict(cls, data: dict) -> 'MRUData':
|
||||||
|
"""Create MRUData from dictionary"""
|
||||||
|
return cls(
|
||||||
|
plugin_id=data.get('PluginID', ''),
|
||||||
|
title=data.get('Title', ''),
|
||||||
|
sub_title=data.get('SubTitle', ''),
|
||||||
|
icon=WoxImage.from_dict(data.get('Icon', {})),
|
||||||
|
context_data=data.get('ContextData', '')
|
||||||
|
)
|
||||||
|
|
||||||
|
def to_dict(self) -> dict:
|
||||||
|
"""Convert MRUData to dictionary"""
|
||||||
|
return {
|
||||||
|
'PluginID': self.plugin_id,
|
||||||
|
'Title': self.title,
|
||||||
|
'SubTitle': self.sub_title,
|
||||||
|
'Icon': self.icon.to_dict(),
|
||||||
|
'ContextData': self.context_data
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
# Type alias for MRU restore callback
|
||||||
|
# Note: We use forward reference to avoid circular import
|
||||||
|
MRURestoreCallback = Callable[['MRUData'], Optional['Result']]
|
|
@ -0,0 +1,242 @@
|
||||||
|
from dataclasses import dataclass, field
|
||||||
|
from typing import Dict, Any, List
|
||||||
|
from enum import Enum
|
||||||
|
import json
|
||||||
|
|
||||||
|
|
||||||
|
class PluginSettingDefinitionType(str, Enum):
|
||||||
|
"""Plugin setting definition type enum"""
|
||||||
|
HEAD = "head"
|
||||||
|
TEXTBOX = "textbox"
|
||||||
|
CHECKBOX = "checkbox"
|
||||||
|
SELECT = "select"
|
||||||
|
LABEL = "label"
|
||||||
|
NEWLINE = "newline"
|
||||||
|
TABLE = "table"
|
||||||
|
DYNAMIC = "dynamic"
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class PluginSettingValueStyle:
|
||||||
|
"""Style configuration for plugin settings"""
|
||||||
|
padding_left: int = field(default=0)
|
||||||
|
padding_top: int = field(default=0)
|
||||||
|
padding_right: int = field(default=0)
|
||||||
|
padding_bottom: int = field(default=0)
|
||||||
|
width: int = field(default=0)
|
||||||
|
label_width: int = field(default=0)
|
||||||
|
|
||||||
|
def to_dict(self) -> Dict[str, Any]:
|
||||||
|
"""Convert to dictionary with camelCase naming"""
|
||||||
|
return {
|
||||||
|
"PaddingLeft": self.padding_left,
|
||||||
|
"PaddingTop": self.padding_top,
|
||||||
|
"PaddingRight": self.padding_right,
|
||||||
|
"PaddingBottom": self.padding_bottom,
|
||||||
|
"Width": self.width,
|
||||||
|
"LabelWidth": self.label_width,
|
||||||
|
}
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_dict(cls, data: Dict[str, Any]) -> 'PluginSettingValueStyle':
|
||||||
|
"""Create from dictionary with camelCase naming"""
|
||||||
|
return cls(
|
||||||
|
padding_left=data.get('PaddingLeft', 0),
|
||||||
|
padding_top=data.get('PaddingTop', 0),
|
||||||
|
padding_right=data.get('PaddingRight', 0),
|
||||||
|
padding_bottom=data.get('PaddingBottom', 0),
|
||||||
|
width=data.get('Width', 0),
|
||||||
|
label_width=data.get('LabelWidth', 0),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class PluginSettingDefinitionValue:
|
||||||
|
"""Base class for plugin setting values"""
|
||||||
|
key: str
|
||||||
|
default_value: str = field(default="")
|
||||||
|
|
||||||
|
def get_key(self) -> str:
|
||||||
|
"""Get the setting key"""
|
||||||
|
return self.key
|
||||||
|
|
||||||
|
def get_default_value(self) -> str:
|
||||||
|
"""Get the default value"""
|
||||||
|
return self.default_value
|
||||||
|
|
||||||
|
def to_dict(self) -> Dict[str, Any]:
|
||||||
|
"""Convert to dictionary"""
|
||||||
|
return {
|
||||||
|
"Key": self.key,
|
||||||
|
"DefaultValue": self.default_value,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class PluginSettingValueTextBox(PluginSettingDefinitionValue):
|
||||||
|
"""Text box setting value"""
|
||||||
|
label: str = field(default="")
|
||||||
|
suffix: str = field(default="")
|
||||||
|
tooltip: str = field(default="")
|
||||||
|
max_lines: int = field(default=1)
|
||||||
|
style: PluginSettingValueStyle = field(default_factory=PluginSettingValueStyle)
|
||||||
|
|
||||||
|
def to_dict(self) -> Dict[str, Any]:
|
||||||
|
"""Convert to dictionary with camelCase naming"""
|
||||||
|
return {
|
||||||
|
"Key": self.key,
|
||||||
|
"Label": self.label,
|
||||||
|
"Suffix": self.suffix,
|
||||||
|
"DefaultValue": self.default_value,
|
||||||
|
"Tooltip": self.tooltip,
|
||||||
|
"MaxLines": self.max_lines,
|
||||||
|
"Style": self.style.to_dict(),
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class PluginSettingValueCheckBox(PluginSettingDefinitionValue):
|
||||||
|
"""Checkbox setting value"""
|
||||||
|
label: str = field(default="")
|
||||||
|
tooltip: str = field(default="")
|
||||||
|
style: PluginSettingValueStyle = field(default_factory=PluginSettingValueStyle)
|
||||||
|
|
||||||
|
def to_dict(self) -> Dict[str, Any]:
|
||||||
|
"""Convert to dictionary with camelCase naming"""
|
||||||
|
return {
|
||||||
|
"Key": self.key,
|
||||||
|
"Label": self.label,
|
||||||
|
"DefaultValue": self.default_value,
|
||||||
|
"Tooltip": self.tooltip,
|
||||||
|
"Style": self.style.to_dict(),
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class PluginSettingValueLabel(PluginSettingDefinitionValue):
|
||||||
|
"""Label setting value"""
|
||||||
|
content: str = field(default="")
|
||||||
|
tooltip: str = field(default="")
|
||||||
|
style: PluginSettingValueStyle = field(default_factory=PluginSettingValueStyle)
|
||||||
|
|
||||||
|
def to_dict(self) -> Dict[str, Any]:
|
||||||
|
"""Convert to dictionary with camelCase naming"""
|
||||||
|
return {
|
||||||
|
"Content": self.content,
|
||||||
|
"Tooltip": self.tooltip,
|
||||||
|
"Style": self.style.to_dict(),
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class PluginSettingDefinitionItem:
|
||||||
|
"""Plugin setting definition item"""
|
||||||
|
type: PluginSettingDefinitionType
|
||||||
|
value: PluginSettingDefinitionValue
|
||||||
|
disabled_in_platforms: List[str] = field(default_factory=list)
|
||||||
|
is_platform_specific: bool = field(default=False)
|
||||||
|
|
||||||
|
def to_dict(self) -> Dict[str, Any]:
|
||||||
|
"""Convert to dictionary with camelCase naming"""
|
||||||
|
return {
|
||||||
|
"Type": self.type,
|
||||||
|
"Value": self.value.to_dict(),
|
||||||
|
"DisabledInPlatforms": self.disabled_in_platforms,
|
||||||
|
"IsPlatformSpecific": self.is_platform_specific,
|
||||||
|
}
|
||||||
|
|
||||||
|
def to_json(self) -> str:
|
||||||
|
"""Convert to JSON string"""
|
||||||
|
return json.dumps(self.to_dict())
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_dict(cls, data: Dict[str, Any]) -> 'PluginSettingDefinitionItem':
|
||||||
|
"""Create from dictionary"""
|
||||||
|
setting_type = PluginSettingDefinitionType(data.get('Type', 'textbox'))
|
||||||
|
|
||||||
|
# Create appropriate value object based on type
|
||||||
|
value_data = data.get('Value', {})
|
||||||
|
value: PluginSettingDefinitionValue
|
||||||
|
if setting_type == PluginSettingDefinitionType.TEXTBOX:
|
||||||
|
value = PluginSettingValueTextBox(
|
||||||
|
key=value_data.get('Key', ''),
|
||||||
|
label=value_data.get('Label', ''),
|
||||||
|
suffix=value_data.get('Suffix', ''),
|
||||||
|
default_value=value_data.get('DefaultValue', ''),
|
||||||
|
tooltip=value_data.get('Tooltip', ''),
|
||||||
|
max_lines=value_data.get('MaxLines', 1),
|
||||||
|
style=PluginSettingValueStyle.from_dict(value_data.get('Style', {}))
|
||||||
|
)
|
||||||
|
elif setting_type == PluginSettingDefinitionType.CHECKBOX:
|
||||||
|
value = PluginSettingValueCheckBox(
|
||||||
|
key=value_data.get('Key', ''),
|
||||||
|
label=value_data.get('Label', ''),
|
||||||
|
default_value=value_data.get('DefaultValue', ''),
|
||||||
|
tooltip=value_data.get('Tooltip', ''),
|
||||||
|
style=PluginSettingValueStyle.from_dict(value_data.get('Style', {}))
|
||||||
|
)
|
||||||
|
elif setting_type == PluginSettingDefinitionType.LABEL:
|
||||||
|
value = PluginSettingValueLabel(
|
||||||
|
key=value_data.get('Key', ''),
|
||||||
|
content=value_data.get('Content', ''),
|
||||||
|
tooltip=value_data.get('Tooltip', ''),
|
||||||
|
style=PluginSettingValueStyle.from_dict(value_data.get('Style', {}))
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
# Default to basic value
|
||||||
|
value = PluginSettingDefinitionValue(
|
||||||
|
key=value_data.get('Key', ''),
|
||||||
|
default_value=value_data.get('DefaultValue', '')
|
||||||
|
)
|
||||||
|
|
||||||
|
return cls(
|
||||||
|
type=setting_type,
|
||||||
|
value=value,
|
||||||
|
disabled_in_platforms=data.get('DisabledInPlatforms', []),
|
||||||
|
is_platform_specific=data.get('IsPlatformSpecific', False)
|
||||||
|
)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_json(cls, json_str: str) -> 'PluginSettingDefinitionItem':
|
||||||
|
"""Create from JSON string"""
|
||||||
|
data = json.loads(json_str)
|
||||||
|
return cls.from_dict(data)
|
||||||
|
|
||||||
|
|
||||||
|
# Helper functions for creating common setting types
|
||||||
|
def create_textbox_setting(key: str, label: str, default_value: str = "", tooltip: str = "") -> PluginSettingDefinitionItem:
|
||||||
|
"""Create a textbox setting"""
|
||||||
|
return PluginSettingDefinitionItem(
|
||||||
|
type=PluginSettingDefinitionType.TEXTBOX,
|
||||||
|
value=PluginSettingValueTextBox(
|
||||||
|
key=key,
|
||||||
|
label=label,
|
||||||
|
default_value=default_value,
|
||||||
|
tooltip=tooltip
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def create_checkbox_setting(key: str, label: str, default_value: str = "false", tooltip: str = "") -> PluginSettingDefinitionItem:
|
||||||
|
"""Create a checkbox setting"""
|
||||||
|
return PluginSettingDefinitionItem(
|
||||||
|
type=PluginSettingDefinitionType.CHECKBOX,
|
||||||
|
value=PluginSettingValueCheckBox(
|
||||||
|
key=key,
|
||||||
|
label=label,
|
||||||
|
default_value=default_value,
|
||||||
|
tooltip=tooltip
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def create_label_setting(content: str, tooltip: str = "") -> PluginSettingDefinitionItem:
|
||||||
|
"""Create a label setting"""
|
||||||
|
return PluginSettingDefinitionItem(
|
||||||
|
type=PluginSettingDefinitionType.LABEL,
|
||||||
|
value=PluginSettingValueLabel(
|
||||||
|
key="", # Labels don't need keys
|
||||||
|
content=content,
|
||||||
|
tooltip=tooltip
|
||||||
|
)
|
||||||
|
)
|
|
@ -624,7 +624,7 @@ wheels = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wox-plugin"
|
name = "wox-plugin"
|
||||||
version = "0.0.48"
|
version = "0.0.49"
|
||||||
source = { editable = "." }
|
source = { editable = "." }
|
||||||
|
|
||||||
[package.optional-dependencies]
|
[package.optional-dependencies]
|
||||||
|
|
Loading…
Reference in New Issue