Skip to content

Commit

Permalink
Add Theme Store
Browse files Browse the repository at this point in the history
  • Loading branch information
lylwx committed Nov 26, 2023
1 parent 26b8bf2 commit 1897ee1
Show file tree
Hide file tree
Showing 8 changed files with 205 additions and 20 deletions.
8 changes: 8 additions & 0 deletions Wox.UI.React/src/api/WoxAPI.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,14 @@ export async function getStorePlugins() {
return post<API.WoxRestResponse<StorePluginManifest[]>>(`/plugin/store`)
}

export async function getStoreThemes() {
return post<API.WoxRestResponse<Theme[]>>(`/theme/store`)
}

export async function getInstalledThemes() {
return post<API.WoxRestResponse<Theme[]>>(`/theme/installed`)
}

export async function getInstalledPlugins() {
return post<API.WoxRestResponse<StorePluginManifest[]>>(`/plugin/installed`)
}
Expand Down
4 changes: 2 additions & 2 deletions Wox.UI.React/src/components/plugins/WoxPluginList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@ import WoxScrollbar from "../tools/WoxScrollbar.tsx"
import { Box, Button, Divider, List, ListItemAvatar, ListItemButton, ListItemText, Tab, Tabs, Typography } from "@mui/material"
import ImageGallery from "react-image-gallery"
import { useWindowSize } from "usehooks-ts"
import { StorePluginManifest } from "../../entity/Plugin.typing"
import { InstalledPluginManifest, StorePluginManifest } from "../../entity/Plugin.typing"
import React, { useState } from "react"
import styled from "styled-components"
import { openUrl } from "../../api/WoxAPI.ts"
import WoxImage from "../tools/WoxImage.tsx"

export default (props: { plugins: StorePluginManifest[]; type: string }) => {
export default (props: { plugins: StorePluginManifest[] | InstalledPluginManifest[]; type: string }) => {
const { plugins } = props
const [selectedTab, setSelectedTab] = useState(0)
const [selectedIndex, setSelectedIndex] = useState(0)
Expand Down
11 changes: 7 additions & 4 deletions Wox.UI.React/src/components/query/WoxQueryResult.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,8 @@ export default React.forwardRef((_props: WoxQueryResultProps, ref: React.Ref<Wox
const [actionActiveIndex, setActionActiveIndex] = useState<number>(0)
const [resultList, setResultList] = useState<WOXMESSAGE.WoxMessageResponseResult[]>(_props.initResultList ? _props.initResultList : [])
const [hasPreview, setHasPreview] = useState<boolean>(false)
const [actionList, setActionList] = useState<WOXMESSAGE.WoxResultAction[]>([])
const [showActionList, setShowActionList] = useState<boolean>(false)
const [showActionList, setShowActionList] = useState<boolean>(!!_props.isPreview)
const [actionList, setActionList] = useState<WOXMESSAGE.WoxResultAction[]>(_props.isPreview ? (_props.initResultList ? _props.initResultList[0].Actions : []) : [])
const filterInputRef = React.createRef<HTMLInputElement>()

const resetResultList = (rsList: WOXMESSAGE.WoxMessageResponseResult[]) => {
Expand Down Expand Up @@ -258,7 +258,7 @@ export default React.forwardRef((_props: WoxQueryResultProps, ref: React.Ref<Wox
}))

return (
<Style theme={WoxThemeHelper.getInstance().getTheme()} itemHeight={getResultListHeight(_props.isPreview ? 3 : 10)}>
<Style theme={WoxThemeHelper.getInstance().getTheme()} itemHeight={getResultListHeight(_props.isPreview ? 6 : 10)}>
<WoxScrollbar
ref={currentResultScrollbarRef}
className={"wox-result-scrollbars"}
Expand Down Expand Up @@ -340,6 +340,7 @@ export default React.forwardRef((_props: WoxQueryResultProps, ref: React.Ref<Wox
})}
<div className={"wox-action-list-filter"}>
<input
disabled={_props.isPreview}
ref={filterInputRef}
className={"wox-action-list-filter-input mousetrap"}
type="text"
Expand All @@ -364,6 +365,7 @@ const Style = styled.div<{ theme: Theme; itemHeight: number }>`
display: flex;
flex-direction: row;
min-height: ${props => props.itemHeight}px;
position: relative;
.wox-result-container {
padding-top: ${props => props.theme.ResultContainerPaddingTop}px;
Expand Down Expand Up @@ -450,8 +452,8 @@ const Style = styled.div<{ theme: Theme; itemHeight: number }>`
position: absolute;
bottom: 10px;
right: 20px;
background-color: ${props => props.theme.ActionContainerBackgroundColor};
min-width: 300px;
background-color: ${props => props.theme.ActionContainerBackgroundColor};
padding-left: ${props => props.theme.ActionContainerPaddingLeft}px;
padding-right: ${props => props.theme.ActionContainerPaddingRight}px;
padding-top: ${props => props.theme.ActionContainerPaddingTop}px;
Expand All @@ -469,6 +471,7 @@ const Style = styled.div<{ theme: Theme; itemHeight: number }>`
align-items: center;
padding: 5px 10px;
color: ${props => props.theme.ActionItemFontColor};
cursor: pointer;
.wox-image {
margin-right: 8px;
Expand Down
60 changes: 58 additions & 2 deletions Wox.UI.React/src/components/settings/WoxSettingThemes.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,72 @@
import React, { useImperativeHandle } from "react"
import React, { useEffect, useImperativeHandle, useState } from "react"
import styled from "styled-components"
import WoxThemePreview from "../themes/WoxThemePreview.tsx"
import { WoxThemeHelper } from "../../utils/WoxThemeHelper.ts"
import { Box, Card, CardContent, Chip, Divider, Skeleton, Typography } from "@mui/material"
import { Theme } from "../../entity/Theme.typings"

export type WoxSettingThemesRefHandler = {}

export type WoxSettingThemesProps = {}

export default React.forwardRef((_props: WoxSettingThemesProps, ref: React.Ref<WoxSettingThemesRefHandler>) => {
const [loading, setLoading] = useState(true)
const [selectedThemeIndex, setSelectedThemeIndex] = useState(0)
const [currentTheme, setCurrentTheme] = useState<Theme>({} as Theme)
const [themes, setThemes] = useState<Theme[]>([])
useImperativeHandle(ref, () => ({}))

useEffect(() => {
WoxThemeHelper.getInstance()
.loadStoreThemes()
.then(() => {
const currentStoreThemes = WoxThemeHelper.getInstance().getStoreThemes()
setThemes(currentStoreThemes)
setCurrentTheme(currentStoreThemes[0])
setLoading(false)
})
}, [])

return (
<Style>
<WoxThemePreview />
<WoxThemePreview theme={currentTheme} />
{loading && (
<Box sx={{ width: "100%", padding: "16px" }}>
<Skeleton />
<Skeleton animation="wave" />
<Skeleton animation={false} />
</Box>
)}
{!loading && (
<Card sx={{ width: "100%", borderRadius: 0, background: "transparent", boxShadow: "initial" }}>
<Typography variant="h6" component="div" sx={{ padding: "5px 16px", color: "white" }}>
Available Themes
</Typography>
<Divider />
<CardContent>
{themes.map((item, index) => {
return index === selectedThemeIndex ? (
<Chip
label={item.ThemeName}
color={"primary"}
onClick={() => {
setSelectedThemeIndex(index)
setCurrentTheme(themes[index])
}}
/>
) : (
<Chip
label={item.ThemeName}
onClick={() => {
setSelectedThemeIndex(index)
setCurrentTheme(themes[index])
}}
/>
)
})}
</CardContent>
</Card>
)}
</Style>
)
})
Expand Down
45 changes: 34 additions & 11 deletions Wox.UI.React/src/components/themes/WoxThemePreview.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,36 +6,59 @@ import WoxQueryResult from "../query/WoxQueryResult.tsx"
import { WOX_QUERY_RESULT_ITEM_HEIGHT } from "../../utils/WoxConst.ts"
import { WOXMESSAGE } from "../../entity/WoxMessage.typings"

export default () => {
export default (props: { theme: Theme }) => {
const getInitResultList = () => {
return [
{
QueryId: "641a84d9-e832-4976-9af8-71552e0f7799",
Id: "cb792d11-dfd4-4899-87df-42d10c18979c",
Title: "旁白",
Title: "VoiceOver",
SubTitle: "/System/Library/CoreServices/VoiceOver.app",
Icon: { ImageType: "url", ImageData: "/voice_over.png" } as WOXMESSAGE.WoxImage,
Score: 8,
Preview: {},
ContextData: "",
Actions: [
{
Id: "",
Name: "",
Id: "e5ec9374-facf-4733-9439-a3b9e067c4c1",
Name: "打开",
Icon: {
ImageType: "",
ImageData: ""
ImageType: "svg",
ImageData:
'<svg xmlns="http://www.w3.org/2000/svg" x="0px" y="0px" width="64" height="64" viewBox="0 0 32 32"><polygon fill="#0f518c" points="30,30 2,30 2,2 17,2 17,6 6,6 6,26 26,26 26,15 30,15"></polygon><polygon fill="#ed0049" points="19,2 19,6 23.172,6 14.586,14.586 17.414,17.414 26,8.828 26,13 30,13 30,2"></polygon></svg>'
},
IsDefault: true,
PreventHideAfterAction: false
},
{
Id: "115697fe-a395-4254-aaed-a8b570848df5",
Name: "打开所在文件夹",
Icon: {
ImageType: "svg",
ImageData:
'<svg xmlns="http://www.w3.org/2000/svg" x="0px" y="0px" width="48" height="48" viewBox="0 0 48 48"><path fill="#FFA000" d="M40,12H22l-4-4H8c-2.2,0-4,1.8-4,4v8h40v-4C44,13.8,42.2,12,40,12z"></path><path fill="#FFCA28" d="M40,12H8c-2.2,0-4,1.8-4,4v20c0,2.2,1.8,4,4,4h32c2.2,0,4-1.8,4-4V16C44,13.8,42.2,12,40,12z"></path></svg>'
},
IsDefault: false,
PreventHideAfterAction: false
},
{
Id: "ec482e87-099b-49fa-a9b7-8e1829abe1ce",
Name: "复制路径",
Icon: {
ImageType: "base64",
ImageData:
"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAYAAABXAvmHAAAACXBIWXMAAAsTAAALEwEAmpwYAAAA+0lEQVR4nO3VLQ7CQBAF4L3Eih6HOQScAIfmDMhq0NVgcegaCAq7GoMpBJKyBEc2/GyZaafTzkue36/7sjVGo9FoICs8toOsmLF9Sezhh8szLwIL2B5LP1oxIrAAV9x5ERQAR41I86vHdrK+VAI4SgT28PPdLRrhXgBkCCzgcCr9IhLhAgAJAgt4HiIW4d4AQgQLoAoCfpQNQIUwnAAKhOEGYBGmDYAQAW0GxNSONx+rgLSBGwDpEwLpAPtl87UAkun+73YSANInBNIBtun/AHYynQOA9AmBdICV/oxa6QCQPiFQQN+e0UQ6ICWuqTMKyPUGej4hjUZjROQBwgDUDcPYwFwAAAAASUVORK5CYII="
},
IsDefault: false,
PreventHideAfterAction: false
}
],
RefreshInterval: 0
},
{
QueryId: "641a84d9-e832-4976-9af8-71552e0f7799",
Id: "cb792d11-dfd4-4899-87df-42d10c18980c",
Title: "无边记",
Title: "Freeform",
SubTitle: "/System/Applications/Freeform.app",
Icon: { ImageType: "url", ImageData: "/free_form.png" } as WOXMESSAGE.WoxImage,
Score: 8,
Expand All @@ -58,7 +81,7 @@ export default () => {
{
QueryId: "641a84d9-e832-4976-9af8-71552e0f7799",
Id: "cb792d11-dfd4-4899-87df-42d10c18990c",
Title: "截屏",
Title: "Screenshot",
SubTitle: "/System/Applications/Utilities/Screenshot.app",
Icon: { ImageType: "url", ImageData: "/screen_shot.png" } as WOXMESSAGE.WoxImage,
Score: 8,
Expand All @@ -84,11 +107,11 @@ export default () => {
const getLauncherHeight = () => {
const theme = WoxThemeHelper.getInstance().getTheme()
const baseItemHeight = WOX_QUERY_RESULT_ITEM_HEIGHT + theme.ResultItemPaddingTop + theme.ResultItemPaddingBottom
return 50 + baseItemHeight * 3 + theme.ResultContainerPaddingTop + theme.ResultContainerPaddingBottom + theme.AppPaddingTop + theme.AppPaddingBottom
return 50 + baseItemHeight * 6 + theme.ResultContainerPaddingTop + theme.ResultContainerPaddingBottom + theme.AppPaddingTop + theme.AppPaddingBottom
}

return (
<Style theme={WoxThemeHelper.getInstance().getTheme()} height={getLauncherHeight()}>
<Style theme={props.theme} height={getLauncherHeight()}>
<div className={"wox-preview-launcher"}>
<div className={"wox-launcher"}>
<WoxQueryBox defaultValue={"Hello World!"} isPreview={true} />
Expand All @@ -103,7 +126,7 @@ const Style = styled.div<{ theme: Theme; height: number }>`
.wox-preview-launcher {
position: relative;
width: 100%;
height: 300px;
height: 440px;
background: url("/wox-preview-bg.jpg") no-repeat center center;
background-size: cover;
display: flex;
Expand Down
61 changes: 61 additions & 0 deletions Wox.UI.React/src/entity/Plugin.typing.d.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import { WOXMESSAGE } from "./WoxMessage.typings"
import { PluginSettingDefinitionType } from "../enums/PluginSettingDefinitionTypeEnum.ts"
import WoxImage = WOXMESSAGE.WoxImage

export interface StorePluginManifest {
Id: string
Expand All @@ -16,3 +18,62 @@ export interface StorePluginManifest {
IsInstalled: boolean
NeedUpdate: boolean
}

export interface MetadataCommand {
Command: string
Description: string
}

export interface PluginQueryCommand {
Command: string
Description: string
}

export interface PluginSettingDefinitionValue {
Key: string
DefaultValue: string
}

export interface PluginSettingDefinitionItem {
Type: PluginSettingDefinitionType
Value: PluginSettingDefinitionValue
}

export interface PluginSetting {
// Is this plugin disabled by user
Disabled: boolean

// User defined keywords, will be used to trigger this plugin. User may not set custom trigger keywords, which will cause this property to be null
//
// So don't use this property directly, use Instance.TriggerKeywords instead
TriggerKeywords: string[]

// plugin author can register query command dynamically
// the final query command will be the combination of plugin's metadata commands defined in plugin.json and customized query command registered here
//
// So don't use this directly, use Instance.GetQueryCommands instead
QueryCommands: PluginQueryCommand[]

Settings: {}
}

export interface InstalledPluginManifest {
Id: string
Name: string
Author: string
Version: string
MinWoxVersion: string
Runtime: string
Description: string
Icon: WoxImage
Website: string
Entry: string
TriggerKeywords: string[] //User can add/update/delete trigger keywords
Commands: MetadataCommand[]
SupportedOS: string[]
SettingDefinitions: PluginSettingDefinitionItem[]
Settings: PluginSetting[]
IsInstalled?: boolean
NeedUpdate?: boolean
ScreenshotUrls?: string[]
}
12 changes: 12 additions & 0 deletions Wox.UI.React/src/enums/PluginSettingDefinitionTypeEnum.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { BaseEnum } from "./base/BaseEnum.ts"

export type PluginSettingDefinitionType = string

export class PluginSettingDefinitionTypeEnum extends BaseEnum {
static readonly PluginSettingDefinitionTypeHead = PluginSettingDefinitionTypeEnum.define("head", "head")
static readonly PluginSettingDefinitionTypeTextBox = PluginSettingDefinitionTypeEnum.define("textbox", "textbox")
static readonly PluginSettingDefinitionTypeCheckBox = PluginSettingDefinitionTypeEnum.define("checkbox", "checkbox")
static readonly PluginSettingDefinitionTypeSelect = PluginSettingDefinitionTypeEnum.define("select", "select")
static readonly PluginSettingDefinitionTypeLabel = PluginSettingDefinitionTypeEnum.define("label", "label")
static readonly PluginSettingDefinitionTypeNewLine = PluginSettingDefinitionTypeEnum.define("newline", "newline")
}
24 changes: 23 additions & 1 deletion Wox.UI.React/src/utils/WoxThemeHelper.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import { Theme } from "../entity/Theme.typings"
import { getTheme } from "../api/WoxAPI.ts"
import { getInstalledThemes, getStoreThemes, getTheme } from "../api/WoxAPI.ts"
import { WoxLogHelper } from "./WoxLogHelper.ts"
import { WoxUIHelper } from "./WoxUIHelper.ts"

export class WoxThemeHelper {
private static instance: WoxThemeHelper
private static currentTheme: Theme
private static storeThemes: Theme[]
private static installedThemes: Theme[]

static getInstance(): WoxThemeHelper {
if (!WoxThemeHelper.instance) {
Expand All @@ -22,6 +24,18 @@ export class WoxThemeHelper {
WoxThemeHelper.currentTheme = apiResponse.Data
}

public async loadStoreThemes() {
const apiResponse = await getStoreThemes()
WoxLogHelper.getInstance().log(`load store themes: ${JSON.stringify(apiResponse.Data)}`)
WoxThemeHelper.storeThemes = apiResponse.Data
}

public async loadInstalledThemes() {
const apiResponse = await getInstalledThemes()
WoxLogHelper.getInstance().log(`load installed themes: ${JSON.stringify(apiResponse.Data)}`)
WoxThemeHelper.installedThemes = apiResponse.Data
}

public async changeTheme(theme: Theme) {
WoxLogHelper.getInstance().log(`change theme: ${JSON.stringify(theme.ThemeName)}`)
WoxThemeHelper.currentTheme = theme
Expand All @@ -31,4 +45,12 @@ export class WoxThemeHelper {
public getTheme() {
return WoxThemeHelper.currentTheme || ({} as Theme)
}

public getStoreThemes() {
return WoxThemeHelper.storeThemes || ({} as Theme)
}

public getInstalledThemes() {
return WoxThemeHelper.installedThemes || ({} as Theme)
}
}

0 comments on commit 1897ee1

Please sign in to comment.