diff --git a/build.mjs b/build.mjs index 069fffc..2a51741 100644 --- a/build.mjs +++ b/build.mjs @@ -135,11 +135,23 @@ async function buildExt(ext, side, copyManifest, fileExt) { const wpModulesDir = `packages/core-extensions/src/${ext}/webpackModules`; if (fs.existsSync(wpModulesDir) && side === "index") { - const wpModules = fs.readdirSync(wpModulesDir); - for (const wpModule of wpModules) { - entryPoints.push( - `packages/core-extensions/src/${ext}/webpackModules/${wpModule}` - ); + const wpModules = fs.opendirSync(wpModulesDir); + for await (const wpModule of wpModules) { + if (wpModule.isFile()) { + entryPoints.push( + `packages/core-extensions/src/${ext}/webpackModules/${wpModule.name}` + ); + } else { + for (const fileExt of ["ts", "tsx"]) { + const path = `packages/core-extensions/src/${ext}/webpackModules/${wpModule.name}/index.${fileExt}`; + if (fs.existsSync(path)) { + entryPoints.push({ + in: path, + out: `webpackModules/${wpModule.name}` + }); + } + } + } } } diff --git a/env.d.ts b/env.d.ts deleted file mode 100644 index 55c16fb..0000000 --- a/env.d.ts +++ /dev/null @@ -1 +0,0 @@ -/// diff --git a/packages/core-extensions/src/moonbase/index.tsx b/packages/core-extensions/src/moonbase/index.tsx index e1eaa52..6a4f3fc 100644 --- a/packages/core-extensions/src/moonbase/index.tsx +++ b/packages/core-extensions/src/moonbase/index.tsx @@ -1,26 +1,6 @@ -import { ExtensionWebExports, WebpackRequireType } from "@moonlight-mod/types"; -import extensionsPage from "./ui/extensions"; -import configPage from "./ui/config"; +import { ExtensionWebExports } from "@moonlight-mod/types"; import { CircleXIconSVG, DownloadIconSVG, TrashIconSVG } from "./types"; -import ui from "./ui"; - -export const pageModules: (require: WebpackRequireType) => Record< - string, - { - name: string; - element: React.FunctionComponent; - } -> = (require) => ({ - extensions: { - name: "Extensions", - element: extensionsPage(require) - }, - config: { - name: "Config", - element: configPage(require) - } -}); export const webpackModules: ExtensionWebExports["webpackModules"] = { stores: { @@ -30,10 +10,9 @@ export const webpackModules: ExtensionWebExports["webpackModules"] = { ] }, - moonbase: { + ui: { dependencies: [ { ext: "spacepack", id: "spacepack" }, - { ext: "settings", id: "settings" }, { ext: "common", id: "react" }, { ext: "common", id: "components" }, { ext: "moonbase", id: "stores" }, @@ -44,62 +23,17 @@ export const webpackModules: ExtensionWebExports["webpackModules"] = { "removeButtonContainer:", '"Missing channel in Channel.openChannelContextMenu"', ".default.HEADER_BAR" - ], - entrypoint: true, - run: (module, exports, require) => { - const settings = require("settings_settings").Settings; - const React = require("common_react"); - const spacepack = require("spacepack_spacepack").spacepack; - const { MoonbaseSettingsStore } = - require("moonbase_stores") as typeof import("./webpackModules/stores"); - - const addSection = (name: string, element: React.FunctionComponent) => { - settings.addSection(name, name, element, null, -2, { - stores: [MoonbaseSettingsStore], - element: () => { - // Require it here because lazy loading SUX - const SettingsNotice = - spacepack.findByCode("onSaveButtonColor")[0].exports.default; - return ( - { - MoonbaseSettingsStore.reset(); - }} - onSave={() => { - MoonbaseSettingsStore.writeConfig(); - }} - /> - ); - } - }); - }; - - if (moonlight.getConfigOption("moonbase", "sections")) { - const pages = pageModules(require); - - const { Text } = require("common_components"); - const Margins = spacepack.findByCode("marginCenterHorz:")[0].exports; + ] + }, - settings.addHeader("Moonbase", -2); - for (const page of Object.values(pages)) { - addSection(page.name, () => ( - <> - - Extensions - - - - )); - } - } else { - addSection("Moonbase", ui(require)); - } - } + moonbase: { + dependencies: [ + { ext: "spacepack", id: "spacepack" }, + { ext: "settings", id: "settings" }, + { ext: "common", id: "react" }, + { ext: "moonbase", id: "ui" } + ], + entrypoint: true } }; diff --git a/packages/core-extensions/src/moonbase/ui/config/index.tsx b/packages/core-extensions/src/moonbase/ui/config/index.tsx deleted file mode 100644 index 01f3882..0000000 --- a/packages/core-extensions/src/moonbase/ui/config/index.tsx +++ /dev/null @@ -1,157 +0,0 @@ -import { LogLevel, WebpackRequireType } from "@moonlight-mod/types"; -import { CircleXIconSVG } from "../../types"; - -const logLevels = Object.values(LogLevel).filter( - (v) => typeof v === "string" -) as string[]; - -export default (require: WebpackRequireType) => { - const React = require("common_react"); - const spacepack = require("spacepack_spacepack").spacepack; - const CommonComponents = require("common_components"); - const { - FormDivider, - FormItem, - FormText, - FormSwitch, - TextInput, - Flex, - Button, - SingleSelect - } = CommonComponents; - - const { MoonbaseSettingsStore } = - require("moonbase_stores") as typeof import("../../webpackModules/stores"); - - const FormClasses = spacepack.findByCode("dividerDefault:")[0].exports; - const Margins = spacepack.findByCode("marginCenterHorz:")[0].exports; - - const RemoveButtonClasses = spacepack.findByCode("removeButtonContainer")[0] - .exports; - const CircleXIcon = spacepack.findByCode(CircleXIconSVG)[0].exports.default; - function RemoveEntryButton({ onClick }: { onClick: () => void }) { - const { Tooltip, Clickable } = CommonComponents; - return ( -
- - {(props: any) => ( - - - - )} - -
- ); - } - - function ArrayFormItem({ - config - }: { - config: "repositories" | "devSearchPaths"; - }) { - const items = MoonbaseSettingsStore.getConfigOption(config) ?? []; - return ( - - {items.map((val, i) => ( -
- { - items[i] = newVal; - MoonbaseSettingsStore.setConfigOption(config, items); - }} - /> - { - items.splice(i, 1); - MoonbaseSettingsStore.setConfigOption(config, items); - }} - /> -
- ))} - - -
- ); - } - - return function ConfigPage() { - return ( - <> - - - A list of remote repositories to display extensions from - - - - - - - A list of local directories to search for built extensions - - - - - { - MoonbaseSettingsStore.setConfigOption("patchAll", value); - }} - note="Wraps every webpack module in a function, separating them in DevTools" - > - Patch all - - - ({ - value: o.toLowerCase(), - label: o[0] + o.slice(1).toLowerCase() - }))} - onChange={(v) => - MoonbaseSettingsStore.setConfigOption("loggerLevel", v) - } - /> - - - ); - }; -}; diff --git a/packages/core-extensions/src/moonbase/ui/extensions/card.tsx b/packages/core-extensions/src/moonbase/ui/extensions/card.tsx deleted file mode 100644 index f70bbe8..0000000 --- a/packages/core-extensions/src/moonbase/ui/extensions/card.tsx +++ /dev/null @@ -1,223 +0,0 @@ -import WebpackRequire from "@moonlight-mod/types/discord/require"; -import { - DangerIconSVG, - DownloadIconSVG, - ExtensionState, - TrashIconSVG -} from "../../types"; -import { ExtensionLoadSource } from "@moonlight-mod/types"; -import info from "./info"; -import settings from "./settings"; - -export enum ExtensionPage { - Info, - Description, - Settings -} - -export default (require: typeof WebpackRequire) => { - const React = require("common_react"); - const spacepack = require("spacepack_spacepack").spacepack; - const CommonComponents = require("common_components"); - const Flux = require("common_flux"); - - const { ExtensionInfo } = info(require); - const Settings = settings(require); - const { MoonbaseSettingsStore } = - require("moonbase_stores") as typeof import("../../webpackModules/stores"); - - const UserProfileClasses = spacepack.findByCode( - "tabBarContainer", - "topSection" - )[0].exports; - - const DownloadIcon = - spacepack.findByCode(DownloadIconSVG)[0].exports.DownloadIcon; - const TrashIcon = spacepack.findByCode(TrashIconSVG)[0].exports.default; - const DangerIcon = - spacepack.findByCode(DangerIconSVG)[0].exports.CircleExclamationPointIcon; - - const PanelButton = - spacepack.findByCode("Masks.PANEL_BUTTON")[0].exports.default; - - return function ExtensionCard({ id }: { id: string }) { - const [tab, setTab] = React.useState(ExtensionPage.Info); - const [restartNeeded, setRestartNeeded] = React.useState(false); - - const { ext, enabled, busy, update } = Flux.useStateFromStores( - [MoonbaseSettingsStore], - () => { - return { - ext: MoonbaseSettingsStore.getExtension(id), - enabled: MoonbaseSettingsStore.getExtensionEnabled(id), - busy: MoonbaseSettingsStore.busy, - update: MoonbaseSettingsStore.getExtensionUpdate(id) - }; - } - ); - - // Why it work like that :sob: - if (ext == null) return <>; - - const { - Card, - CardClasses, - Flex, - Text, - MarkdownParser, - Switch, - TabBar, - Button - } = CommonComponents; - - const tagline = ext.manifest?.meta?.tagline; - const settings = ext.manifest?.settings; - const description = ext.manifest?.meta?.description; - - return ( - -
- - - - {ext.manifest?.meta?.name ?? ext.id} - - - - {tagline != null && ( - - {MarkdownParser.parse(tagline)} - - )} - - - - {ext.state === ExtensionState.NotDownloaded ? ( - - ) : ( -
works lmao - style={{ - display: "flex", - alignItems: "center", - gap: "1rem" - }} - > - {ext.source.type === ExtensionLoadSource.Normal && ( - { - MoonbaseSettingsStore.deleteExtension(id); - }} - /> - )} - - {update !== null && ( - { - MoonbaseSettingsStore.installExtension(id); - }} - /> - )} - - {restartNeeded && ( - ( - - )} - onClick={() => window.location.reload()} - tooltipText="You will need to reload/restart your client for this extension to work properly." - /> - )} - - { - setRestartNeeded(true); - MoonbaseSettingsStore.setExtensionEnabled(id, !enabled); - }} - /> -
- )} -
-
- -
- {(description != null || settings != null) && ( -
- - - Info - - - {description != null && ( - - Description - - )} - - {settings != null && ( - - Settings - - )} - -
- )} - - - {tab === ExtensionPage.Info && } - {tab === ExtensionPage.Description && ( - - {MarkdownParser.parse(description ?? "*No description*")} - - )} - {tab === ExtensionPage.Settings && } - -
-
- ); - }; -}; diff --git a/packages/core-extensions/src/moonbase/ui/extensions/filterBar.tsx b/packages/core-extensions/src/moonbase/ui/extensions/filterBar.tsx deleted file mode 100644 index 095e0f3..0000000 --- a/packages/core-extensions/src/moonbase/ui/extensions/filterBar.tsx +++ /dev/null @@ -1,373 +0,0 @@ -import { WebpackRequireType } from "@moonlight-mod/types"; -import { tagNames } from "./info"; -import { - ArrowsUpDownIconSVG, - ChevronSmallDownIconSVG, - ChevronSmallUpIconSVG -} from "../../types"; - -export enum Filter { - Core = 1 << 0, - Normal = 1 << 1, - Developer = 1 << 2, - Enabled = 1 << 3, - Disabled = 1 << 4, - Installed = 1 << 5, - Repository = 1 << 6 -} -export const defaultFilter = ~(~0 << 7); - -export default async (require: WebpackRequireType) => { - const spacepack = require("spacepack_spacepack").spacepack; - const React = require("common_react"); - const Flux = require("common_flux"); - const { WindowStore } = require("common_stores"); - - const { - Button, - Text, - Heading, - Popout, - Dialog - } = require("common_components"); - - const channelModule = - require.m[ - spacepack.findByCode( - '"Missing channel in Channel.openChannelContextMenu"' - )[0].id - ].toString(); - const moduleId = channelModule.match(/webpackId:"(.+?)"/)![1]; - await require.el(moduleId); - - const Margins = spacepack.findByCode("marginCenterHorz:")[0].exports; - const SortMenuClasses = spacepack.findByCode("container:", "clearText:")[0] - .exports; - const FilterDialogClasses = spacepack.findByCode( - "countContainer:", - "tagContainer:" - )[0].exports; - const FilterBarClasses = spacepack.findByCode("tagsButtonWithCount:")[0] - .exports; - - const TagItem = spacepack.findByCode("IncreasedActivityForumTagPill:")[0] - .exports.default; - - const ChevronSmallDownIcon = spacepack.findByCode(ChevronSmallDownIconSVG)[0] - .exports.default; - const ChevronSmallUpIcon = spacepack.findByCode(ChevronSmallUpIconSVG)[0] - .exports.default; - const ArrowsUpDownIcon = - spacepack.findByCode(ArrowsUpDownIconSVG)[0].exports.default; - - function toggleTag( - selectedTags: Set, - setSelectedTags: (tags: Set) => void, - tag: string - ) { - const newState = new Set(selectedTags); - if (newState.has(tag)) newState.delete(tag); - else newState.add(tag); - setSelectedTags(newState); - } - - function FilterButtonPopout({ - filter, - setFilter, - closePopout - }: { - filter: Filter; - setFilter: (filter: Filter) => void; - closePopout: () => void; - }) { - const { - Menu, - MenuItem, - MenuGroup, - MenuCheckboxItem - } = require("common_components"); - - const toggleFilter = (set: Filter) => - setFilter(filter & set ? filter & ~set : filter | set); - - return ( -
- - - toggleFilter(Filter.Core)} - /> - toggleFilter(Filter.Normal)} - /> - toggleFilter(Filter.Developer)} - /> - - - toggleFilter(Filter.Enabled)} - /> - toggleFilter(Filter.Disabled)} - /> - - - toggleFilter(Filter.Installed)} - /> - toggleFilter(Filter.Repository)} - /> - - - - Reset to default - - } - action={() => { - setFilter(defaultFilter); - closePopout(); - }} - /> - - -
- ); - } - - function TagButtonPopout({ - selectedTags, - setSelectedTags, - setPopoutRef, - closePopout - }: any) { - return ( - -
-
- - Select tags - -
- - {selectedTags.size} - -
-
-
-
- {Object.keys(tagNames).map((tag) => ( - toggleTag(selectedTags, setSelectedTags, tag)} - selected={selectedTags.has(tag)} - /> - ))} -
-
- -
- ); - } - - return function FilterBar({ - filter, - setFilter, - selectedTags, - setSelectedTags - }: { - filter: Filter; - setFilter: (filter: Filter) => void; - selectedTags: Set; - setSelectedTags: (tags: Set) => void; - }) { - const windowSize = Flux.useStateFromStores([WindowStore], () => - WindowStore.windowSize() - ); - - const tagsContainer = React.useRef(null); - const tagListInner = React.useRef(null); - const [tagsButtonOffset, setTagsButtonOffset] = React.useState(0); - React.useLayoutEffect(() => { - if (tagsContainer.current === null || tagListInner.current === null) - return; - const { left: containerX, top: containerY } = - tagsContainer.current.getBoundingClientRect(); - let offset = 0; - for (const child of tagListInner.current.children) { - const { - right: childX, - top: childY, - height - } = child.getBoundingClientRect(); - if (childY - containerY > height) break; - const newOffset = childX - containerX; - if (newOffset > offset) { - offset = newOffset; - } - } - setTagsButtonOffset(offset); - }, [windowSize]); - - return ( -
- ( - - )} - position="bottom" - align="left" - > - {(props: any, { isShown }: { isShown: boolean }) => ( - - )} - -
-
-
- {Object.keys(tagNames).map((tag) => ( - toggleTag(selectedTags, setSelectedTags, tag)} - selected={selectedTags.has(tag)} - /> - ))} -
-
- ( - - )} - position="bottom" - align="right" - > - {(props: any, { isShown }: { isShown: boolean }) => ( - - )} - -
- ); - }; -}; diff --git a/packages/core-extensions/src/moonbase/ui/extensions/index.tsx b/packages/core-extensions/src/moonbase/ui/extensions/index.tsx deleted file mode 100644 index b1b5e5e..0000000 --- a/packages/core-extensions/src/moonbase/ui/extensions/index.tsx +++ /dev/null @@ -1,118 +0,0 @@ -import { - ExtensionLoadSource, - ExtensionTag, - WebpackRequireType -} from "@moonlight-mod/types"; -import { ExtensionState } from "../../types"; -import filterBar, { Filter, defaultFilter } from "./filterBar"; -import card from "./card"; - -export default (require: WebpackRequireType) => { - const React = require("common_react"); - const spacepack = require("spacepack_spacepack").spacepack; - const Flux = require("common_flux"); - - const { MoonbaseSettingsStore } = - require("moonbase_stores") as typeof import("../../webpackModules/stores"); - - const ExtensionCard = card(require); - const FilterBar = React.lazy(() => - filterBar(require).then((c) => ({ default: c })) - ); - - const Margins = spacepack.findByCode("marginCenterHorz:")[0].exports; - const SearchBar = spacepack.findByCode("Messages.SEARCH", "hideSearchIcon")[0] - .exports.default; - - return function ExtensionsPage() { - const { extensions, savedFilter } = Flux.useStateFromStoresObject( - [MoonbaseSettingsStore], - () => { - return { - extensions: MoonbaseSettingsStore.extensions, - savedFilter: MoonbaseSettingsStore.getExtensionConfig( - "moonbase", - "filter" - ) - }; - } - ); - - const [query, setQuery] = React.useState(""); - - let filter: Filter, setFilter: (filter: Filter) => void; - if (moonlight.getConfigOption("moonbase", "saveFilter")) { - filter = savedFilter ?? defaultFilter; - setFilter = (filter) => - MoonbaseSettingsStore.setExtensionConfig("moonbase", "filter", filter); - } else { - const state = React.useState(defaultFilter); - filter = state[0]; - setFilter = state[1]; - } - const [selectedTags, setSelectedTags] = React.useState(new Set()); - const sorted = Object.values(extensions).sort((a, b) => { - const aName = a.manifest.meta?.name ?? a.id; - const bName = b.manifest.meta?.name ?? b.id; - return aName.localeCompare(bName); - }); - - const filtered = sorted.filter( - (ext) => - (ext.manifest.meta?.name?.toLowerCase().includes(query) || - ext.manifest.meta?.tagline?.toLowerCase().includes(query) || - ext.manifest.meta?.description?.toLowerCase().includes(query)) && - [...selectedTags.values()].every( - (tag) => ext.manifest.meta?.tags?.includes(tag as ExtensionTag) - ) && - // This seems very bad, sorry - !( - (!(filter & Filter.Core) && - ext.source.type === ExtensionLoadSource.Core) || - (!(filter & Filter.Normal) && - ext.source.type === ExtensionLoadSource.Normal) || - (!(filter & Filter.Developer) && - ext.source.type === ExtensionLoadSource.Developer) || - (!(filter & Filter.Enabled) && - MoonbaseSettingsStore.getExtensionEnabled(ext.id)) || - (!(filter & Filter.Disabled) && - !MoonbaseSettingsStore.getExtensionEnabled(ext.id)) || - (!(filter & Filter.Installed) && - ext.state !== ExtensionState.NotDownloaded) || - (!(filter & Filter.Repository) && - ext.state === ExtensionState.NotDownloaded) - ) - ); - - return ( - <> - setQuery(v.toLowerCase())} - onClear={() => setQuery("")} - autoFocus={true} - autoComplete="off" - inputProps={{ - autoCapitalize: "none", - autoCorrect: "off", - spellCheck: "false" - }} - /> -
} - > - - - {filtered.map((ext) => ( - - ))} - - ); - }; -}; diff --git a/packages/core-extensions/src/moonbase/ui/extensions/info.tsx b/packages/core-extensions/src/moonbase/ui/extensions/info.tsx deleted file mode 100644 index 94b07fc..0000000 --- a/packages/core-extensions/src/moonbase/ui/extensions/info.tsx +++ /dev/null @@ -1,209 +0,0 @@ -import WebpackRequire from "@moonlight-mod/types/discord/require"; -import { ExtensionTag } from "@moonlight-mod/types"; -import { MoonbaseExtension } from "../../types"; - -type Dependency = { - id: string; - type: DependencyType; -}; - -enum DependencyType { - Dependency = "dependency", - Optional = "optional", - Incompatible = "incompatible" -} - -export const tagNames: Record = { - [ExtensionTag.Accessibility]: "Accessibility", - [ExtensionTag.Appearance]: "Appearance", - [ExtensionTag.Chat]: "Chat", - [ExtensionTag.Commands]: "Commands", - [ExtensionTag.ContextMenu]: "Context Menu", - [ExtensionTag.DangerZone]: "Danger Zone", - [ExtensionTag.Development]: "Development", - [ExtensionTag.Fixes]: "Fixes", - [ExtensionTag.Fun]: "Fun", - [ExtensionTag.Markdown]: "Markdown", - [ExtensionTag.Voice]: "Voice", - [ExtensionTag.Privacy]: "Privacy", - [ExtensionTag.Profiles]: "Profiles", - [ExtensionTag.QualityOfLife]: "Quality of Life", - [ExtensionTag.Library]: "Library" -}; - -export default (require: typeof WebpackRequire) => { - const React = require("common_react"); - const spacepack = require("spacepack_spacepack").spacepack; - - const CommonComponents = require("common_components"); - const UserInfoClasses = spacepack.findByCode( - "infoScroller", - "userInfoSection", - "userInfoSectionHeader" - )[0].exports; - - const { MoonbaseSettingsStore } = - require("moonbase_stores") as typeof import("../../webpackModules/stores"); - - function InfoSection({ - title, - children - }: { - title: string; - children: React.ReactNode; - }) { - return ( -
- - {title} - - - - {children} - -
- ); - } - - function Badge({ - color, - children - }: { - color: string; - children: React.ReactNode; - }) { - return ( - - {children} - - ); - } - - function ExtensionInfo({ ext }: { ext: MoonbaseExtension }) { - const authors = ext.manifest?.meta?.authors; - const tags = ext.manifest?.meta?.tags; - const version = ext.manifest?.version; - - const dependencies: Dependency[] = []; - if (ext.manifest.dependencies != null) { - dependencies.push( - ...ext.manifest.dependencies.map((dep) => ({ - id: dep, - type: DependencyType.Dependency - })) - ); - } - - if (ext.manifest.suggested != null) { - dependencies.push( - ...ext.manifest.suggested.map((dep) => ({ - id: dep, - type: DependencyType.Optional - })) - ); - } - - if (ext.manifest.incompatible != null) { - dependencies.push( - ...ext.manifest.incompatible.map((dep) => ({ - id: dep, - type: DependencyType.Incompatible - })) - ); - } - - return ( - <> - {authors != null && ( - - {authors.map((author, i) => { - const comma = i !== authors.length - 1 ? ", " : ""; - if (typeof author === "string") { - return ( - - {author} - {comma} - - ); - } else { - // TODO: resolve IDs - return ( - - {author.name} - {comma} - - ); - } - })} - - )} - - {tags != null && ( - - {tags.map((tag, i) => { - const name = tagNames[tag]; - - return ( - - {name} - - ); - })} - - )} - - {dependencies.length > 0 && ( - - {dependencies.map((dep) => { - const colors = { - [DependencyType.Dependency]: "var(--brand-500)", - [DependencyType.Optional]: "var(--orange-400)", - [DependencyType.Incompatible]: "var(--red-400)" - }; - const color = colors[dep.type]; - const name = MoonbaseSettingsStore.getExtensionName(dep.id); - return ( - - {name} - - ); - })} - - )} - - {version != null && ( - - {version} - - )} - - ); - } - - return { - InfoSection, - ExtensionInfo - }; -}; diff --git a/packages/core-extensions/src/moonbase/ui/extensions/settings.tsx b/packages/core-extensions/src/moonbase/ui/extensions/settings.tsx deleted file mode 100644 index 615e33e..0000000 --- a/packages/core-extensions/src/moonbase/ui/extensions/settings.tsx +++ /dev/null @@ -1,396 +0,0 @@ -import { - ExtensionSettingType, - ExtensionSettingsManifest, - MultiSelectSettingType, - NumberSettingType, - SelectOption, - SelectSettingType -} from "@moonlight-mod/types/config"; -import WebpackRequire from "@moonlight-mod/types/discord/require"; -import { CircleXIconSVG, ExtensionState, MoonbaseExtension } from "../../types"; - -type SettingsProps = { - ext: MoonbaseExtension; - name: string; - setting: ExtensionSettingsManifest; - disabled: boolean; -}; - -type SettingsComponent = React.ComponentType; - -export default (require: typeof WebpackRequire) => { - const React = require("common_react"); - const CommonComponents = require("common_components"); - const Flux = require("common_flux"); - const spacepack = require("spacepack_spacepack").spacepack; - - const { MoonbaseSettingsStore } = - require("moonbase_stores") as typeof import("../../webpackModules/stores"); - - const Margins = spacepack.findByCode("marginCenterHorz:")[0].exports; - - function useConfigEntry(id: string, name: string) { - return Flux.useStateFromStores( - [MoonbaseSettingsStore], - () => { - return { - value: MoonbaseSettingsStore.getExtensionConfig(id, name), - displayName: MoonbaseSettingsStore.getExtensionConfigName(id, name), - description: MoonbaseSettingsStore.getExtensionConfigDescription( - id, - name - ) - }; - }, - [id, name] - ); - } - - function Boolean({ ext, name, setting, disabled }: SettingsProps) { - const { FormSwitch } = CommonComponents; - const { value, displayName, description } = useConfigEntry( - ext.id, - name - ); - - return ( - { - MoonbaseSettingsStore.setExtensionConfig(ext.id, name, value); - }} - note={description} - className={`${Margins.marginReset} ${Margins.marginTop20}`} - > - {displayName} - - ); - } - - function Number({ ext, name, setting, disabled }: SettingsProps) { - const { FormItem, FormText, Slider } = CommonComponents; - const { value, displayName, description } = useConfigEntry( - ext.id, - name - ); - - const castedSetting = setting as NumberSettingType; - const min = castedSetting.min ?? 0; - const max = castedSetting.max ?? 100; - - return ( - - {description && {description}} - { - const rounded = Math.max(min, Math.min(max, Math.round(value))); - MoonbaseSettingsStore.setExtensionConfig(ext.id, name, rounded); - }} - /> - - ); - } - - function String({ ext, name, setting, disabled }: SettingsProps) { - const { FormItem, FormText, TextInput } = CommonComponents; - const { value, displayName, description } = useConfigEntry( - ext.id, - name - ); - - return ( - - {description && ( - {description} - )} - { - if (disabled) return; - MoonbaseSettingsStore.setExtensionConfig(ext.id, name, value); - }} - /> - - ); - } - - function Select({ ext, name, setting, disabled }: SettingsProps) { - const { FormItem, FormText, SingleSelect } = CommonComponents; - const { value, displayName, description } = useConfigEntry( - ext.id, - name - ); - - const castedSetting = setting as SelectSettingType; - const options = castedSetting.options; - - return ( - - {description && ( - {description} - )} - - typeof o === "string" ? { value: o, label: o } : o - )} - onChange={(value: string) => { - if (disabled) return; - MoonbaseSettingsStore.setExtensionConfig(ext.id, name, value); - }} - /> - - ); - } - - function MultiSelect({ ext, name, setting, disabled }: SettingsProps) { - const { FormItem, FormText, Select, useVariableSelect, multiSelect } = - CommonComponents; - const { value, displayName, description } = useConfigEntry< - string | string[] - >(ext.id, name); - - const castedSetting = setting as MultiSelectSettingType; - const options = castedSetting.options; - - return ( - - {description && ( - {description} - )} - + typeof o === "string" ? { value: o, label: o } : o + )} + {...useVariableSelect({ + onSelectInteraction: multiSelect, + value: new Set(Array.isArray(value) ? value : [value]), + onChange: (value: string) => { + if (disabled) return; + MoonbaseSettingsStore.setExtensionConfig( + ext.id, + name, + Array.from(value) + ); + } + })} + /> + + ); +} + +const RemoveButtonClasses = spacepack.findByCode("removeButtonContainer")[0] + .exports; +const CircleXIcon = spacepack.findByCode(CircleXIconSVG)[0].exports.default; +function RemoveEntryButton({ + onClick, + disabled +}: { + onClick: () => void; + disabled: boolean; +}) { + const { Tooltip, Clickable } = CommonComponents; + return ( +
+ + {(props: any) => ( + + + + )} + +
+ ); +} + +function List({ ext, name, setting, disabled }: SettingsProps) { + const { FormItem, FormText, TextInput, Button, Flex } = CommonComponents; + const { value, displayName, description } = useConfigEntry( + ext.id, + name + ); + + const entries = value ?? []; + const updateConfig = () => + MoonbaseSettingsStore.setExtensionConfig(ext.id, name, entries); + + return ( + + {description && ( + {description} + )} + + {entries.map((val, i) => ( + // FIXME: stylesheets +
+ { + entries[i] = newVal; + updateConfig(); + }} + /> + { + entries.splice(i, 1); + updateConfig(); + }} + /> +
+ ))} + + +
+
+ ); +} + +function Dictionary({ ext, name, setting, disabled }: SettingsProps) { + const { FormItem, FormText, TextInput, Button, Flex } = CommonComponents; + const { value, displayName, description } = useConfigEntry< + Record + >(ext.id, name); + + const entries = Object.entries(value ?? {}); + const updateConfig = () => + MoonbaseSettingsStore.setExtensionConfig( + ext.id, + name, + Object.fromEntries(entries) + ); + + return ( + + {description && ( + {description} + )} + + {entries.map(([key, val], i) => ( + // FIXME: stylesheets +
+ { + entries[i][0] = newKey; + updateConfig(); + }} + /> + { + entries[i][1] = newValue; + updateConfig(); + }} + /> + { + entries.splice(i, 1); + updateConfig(); + }} + /> +
+ ))} + + +
+
+ ); +} + +function Setting({ ext, name, setting, disabled }: SettingsProps) { + const elements: Partial> = { + [ExtensionSettingType.Boolean]: Boolean, + [ExtensionSettingType.Number]: Number, + [ExtensionSettingType.String]: String, + [ExtensionSettingType.Select]: Select, + [ExtensionSettingType.MultiSelect]: MultiSelect, + [ExtensionSettingType.List]: List, + [ExtensionSettingType.Dictionary]: Dictionary + }; + const element = elements[setting.type]; + if (element == null) return <>; + return React.createElement(element, { ext, name, setting, disabled }); +} + +export default function Settings({ ext }: { ext: MoonbaseExtension }) { + const { Flex } = CommonComponents; + return ( + + {Object.entries(ext.manifest.settings!).map(([name, setting]) => ( + + ))} + + ); +} diff --git a/packages/core-extensions/src/moonbase/webpackModules/ui/index.tsx b/packages/core-extensions/src/moonbase/webpackModules/ui/index.tsx new file mode 100644 index 0000000..42c184c --- /dev/null +++ b/packages/core-extensions/src/moonbase/webpackModules/ui/index.tsx @@ -0,0 +1,64 @@ +import React from "@moonlight-mod/wp/common_react"; +import spacepack from "@moonlight-mod/wp/spacepack_spacepack"; +import { Text, TabBar } from "@moonlight-mod/wp/common_components"; + +import ExtensionsPage from "./extensions"; +import ConfigPage from "./config"; + +const Margins = spacepack.findByCode("marginCenterHorz:")[0].exports; + +const { Divider } = spacepack.findByCode(".default.HEADER_BAR")[0].exports + .default; +const TitleBarClasses = spacepack.findByCode("iconWrapper:", "children:")[0] + .exports; +const TabBarClasses = spacepack.findByCode("nowPlayingColumn:")[0].exports; + +export const pages: Record< + string, + { + name: string; + element: React.FunctionComponent; + } +> = { + extensions: { + name: "Extensions", + element: ExtensionsPage + }, + config: { + name: "Config", + element: ConfigPage + } +}; + +export function Moonbase() { + const [selectedTab, setSelectedTab] = React.useState(Object.keys(pages)[0]); + + return ( + <> +
+ + Moonbase + + + + {Object.entries(pages).map(([id, page]) => ( + + {page.name} + + ))} + +
+ + {React.createElement(pages[selectedTab].element)} + + ); +} diff --git a/packages/core-extensions/src/moonbase/wp.d.ts b/packages/core-extensions/src/moonbase/wp.d.ts new file mode 100644 index 0000000..8eeeb9a --- /dev/null +++ b/packages/core-extensions/src/moonbase/wp.d.ts @@ -0,0 +1,7 @@ +declare module "@moonlight-mod/wp/moonbase_ui" { + export * from "core-extensions/src/moonbase/webpackModules/ui"; +} + +declare module "@moonlight-mod/wp/moonbase_stores" { + export * from "core-extensions/src/moonbase/webpackModules/stores"; +} diff --git a/packages/types/package.json b/packages/types/package.json index 45dfb94..4a62feb 100644 --- a/packages/types/package.json +++ b/packages/types/package.json @@ -10,7 +10,6 @@ }, "dependencies": { "@types/flux": "^3.1.12", - "@types/node": "^20.6.2", "@types/react": "^18.2.22", "csstype": "^3.1.2", "standalone-electron-types": "^1.0.0" diff --git a/packages/types/src/import.d.ts b/packages/types/src/import.d.ts index b6057d2..972ddf7 100644 --- a/packages/types/src/import.d.ts +++ b/packages/types/src/import.d.ts @@ -6,15 +6,15 @@ declare module "@moonlight-mod/wp/spacepack_spacepack" { declare module "@moonlight-mod/wp/common_components" { import { CoreExtensions } from "@moonlight-mod/types"; - const components: CoreExtensions.CommonComponents; - export default components; - export = components; + const CommonComponent: CoreExtensions.CommonComponents; + export = CommonComponent; } declare module "@moonlight-mod/wp/common_flux" { import { CoreExtensions } from "@moonlight-mod/types"; const Flux: CoreExtensions.CommonFlux; - export default Flux; + // FIXME: This is wrong, the default export differs from the named exports. + export = Flux; } declare module "@moonlight-mod/wp/common_fluxDispatcher" { @@ -23,6 +23,8 @@ declare module "@moonlight-mod/wp/common_fluxDispatcher" { export default Dispatcher; } +declare module "@moonlight-mod/wp/common_stores"; + declare module "@moonlight-mod/wp/common_react" { import React from "react"; export = React; @@ -32,7 +34,6 @@ declare module "@moonlight-mod/wp/settings_settings" { import { CoreExtensions } from "@moonlight-mod/types"; export const Settings: CoreExtensions.Settings; export default Settings; - export = Settings; } declare module "@moonlight-mod/wp/markdown_markdown" { diff --git a/packages/types/src/index.ts b/packages/types/src/index.ts index a1ba8ec..2aef490 100644 --- a/packages/types/src/index.ts +++ b/packages/types/src/index.ts @@ -1,4 +1,3 @@ -/// /// /// /// diff --git a/packages/types/tsconfig.json b/packages/types/tsconfig.json index 40dd733..b2caaec 100644 --- a/packages/types/tsconfig.json +++ b/packages/types/tsconfig.json @@ -5,7 +5,6 @@ "esModuleInterop": true, "forceConsistentCasingInFileNames": true, "strict": true, - "skipLibCheck": true, "moduleResolution": "bundler", "jsx": "react", "declaration": true diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 23ab8ca..c853f61 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -80,9 +80,6 @@ importers: '@types/flux': specifier: ^3.1.12 version: 3.1.12 - '@types/node': - specifier: ^20.6.2 - version: 20.6.2 '@types/react': specifier: ^18.2.22 version: 18.2.22 @@ -423,10 +420,6 @@ packages: resolution: {integrity: sha512-cOxcXsQ2sxiwkykdJqvyFS+MLQPLvIdwh5l6gNg8qF6s+C7XSkEWOZjK+XhUZd+mYvHV/180g2cnCcIl4l06Pw==} dev: false - /@types/node@20.6.2: - resolution: {integrity: sha512-Y+/1vGBHV/cYk6OI1Na/LHzwnlNCAfU3ZNGrc1LdRe/LAIbdDPTTv/HU3M7yXN448aTVDq3eKRm2cg7iKLb8gw==} - dev: false - /@types/prop-types@15.7.6: resolution: {integrity: sha512-RK/kBbYOQQHLYj9Z95eh7S6t7gq4Ojt/NT8HTk8bWVhA5DaF+5SMnxHKkP4gPNN3wAZkKP+VjAf0ebtYzf+fxg==} dev: false diff --git a/tsconfig.json b/tsconfig.json index fe5194d..61f1245 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -5,7 +5,6 @@ "esModuleInterop": true, "forceConsistentCasingInFileNames": true, "strict": true, - "skipLibCheck": true, "moduleResolution": "bundler", "baseUrl": "./packages/", "jsx": "react",