this repo has no description

Merge pull request #11 from redstonekasi/mb-config

Moonbase config tab

Changed files
+318 -101
packages
core-extensions
src
moonbase
types
src
coreExtensions
+3 -3
packages/core-extensions/src/moonbase/ui/card.tsx packages/core-extensions/src/moonbase/ui/extensions/card.tsx
···
DownloadIconSVG,
ExtensionState,
TrashIconSVG
-
} from "../types";
-
import { ExtensionLoadSource } from "types/src";
+
} from "../../types";
+
import { ExtensionLoadSource } from "@moonlight-mod/types";
import info from "./info";
import settings from "./settings";
···
const { ExtensionInfo } = info(require);
const Settings = settings(require);
const { MoonbaseSettingsStore } =
-
require("moonbase_stores") as typeof import("../webpackModules/stores");
+
require("moonbase_stores") as typeof import("../../webpackModules/stores");
const UserProfileClasses = spacepack.findByCode(
"tabBarContainer",
+150
packages/core-extensions/src/moonbase/ui/config/index.tsx
···
+
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 (
+
<div className={RemoveButtonClasses.removeButtonContainer}>
+
<Tooltip text="Remove entry" position="top">
+
{(props: any) => (
+
<Clickable
+
{...props}
+
className={RemoveButtonClasses.removeButton}
+
onClick={onClick}
+
>
+
<CircleXIcon width={16} height={16} />
+
</Clickable>
+
)}
+
</Tooltip>
+
</div>
+
);
+
}
+
+
function ArrayFormItem({
+
config
+
}: {
+
config: "repositories" | "devSearchPaths";
+
}) {
+
const items = MoonbaseSettingsStore.getConfigOption(config) ?? [];
+
return (
+
<Flex direction={Flex.Direction.VERTICAL}>
+
{items.map((val, i) => (
+
<div
+
key={i}
+
style={{
+
display: "grid",
+
height: "32px",
+
gap: "8px",
+
gridTemplateColumns: "1fr 32px",
+
alignItems: "center"
+
}}
+
>
+
<TextInput
+
size={TextInput.Sizes.MINI}
+
value={val}
+
onChange={(newVal: string) => {
+
items[i] = newVal;
+
MoonbaseSettingsStore.setConfigOption(config, items);
+
}}
+
/>
+
<RemoveEntryButton
+
onClick={() => {
+
items.splice(i, 1);
+
MoonbaseSettingsStore.setConfigOption(config, items);
+
}}
+
/>
+
</div>
+
))}
+
+
<Button
+
look={Button.Looks.FILLED}
+
color={Button.Colors.GREEN}
+
size={Button.Sizes.SMALL}
+
className={Margins.marginTop8}
+
onClick={() => {
+
items.push("");
+
MoonbaseSettingsStore.setConfigOption(config, items);
+
}}
+
>
+
Add new entry
+
</Button>
+
</Flex>
+
);
+
}
+
+
return function ConfigPage() {
+
return (
+
<>
+
<FormItem title="Repositories">
+
<FormText className={Margins.marginBottom4}>
+
A list of remote repositories to display extensions from
+
</FormText>
+
<ArrayFormItem config="repositories" />
+
</FormItem>
+
<FormDivider className={FormClasses.dividerDefault} />
+
<FormItem
+
title="Extension search paths"
+
className={Margins.marginTop20}
+
>
+
<FormText className={Margins.marginBottom4}>
+
A list of local directories to search for built extensions
+
</FormText>
+
<ArrayFormItem config="devSearchPaths" />
+
</FormItem>
+
<FormDivider className={FormClasses.dividerDefault} />
+
<FormSwitch
+
className={Margins.marginTop20}
+
value={MoonbaseSettingsStore.getConfigOption("patchAll")}
+
onChange={(value: boolean) => {
+
MoonbaseSettingsStore.setConfigOption("patchAll", value);
+
}}
+
note="Wraps every webpack module in a function, separating them in DevTools"
+
>
+
Patch all
+
</FormSwitch>
+
<FormItem title="Log level">
+
<SingleSelect
+
autofocus={false}
+
clearable={false}
+
value={MoonbaseSettingsStore.getConfigOption("loggerLevel")}
+
options={logLevels.map((o) => ({
+
value: o.toLowerCase(),
+
label: o[0] + o.slice(1).toLowerCase()
+
}))}
+
onChange={(v) =>
+
MoonbaseSettingsStore.setConfigOption("loggerLevel", v)
+
}
+
/>
+
</FormItem>
+
</>
+
);
+
};
+
};
+99
packages/core-extensions/src/moonbase/ui/extensions/index.tsx
···
+
import {
+
ExtensionLoadSource,
+
ExtensionTag,
+
WebpackRequireType
+
} from "@moonlight-mod/types";
+
import { ExtensionState } from "../../types";
+
import filterBar, { 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 } = Flux.useStateFromStoresObject(
+
[MoonbaseSettingsStore],
+
() => {
+
return { extensions: MoonbaseSettingsStore.extensions };
+
}
+
);
+
+
const [query, setQuery] = React.useState("");
+
const [filter, setFilter] = React.useState({ ...defaultFilter });
+
const [selectedTags, setSelectedTags] = React.useState(new Set<string>());
+
+
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.core && ext.source.type === ExtensionLoadSource.Core) ||
+
(!filter.normal && ext.source.type === ExtensionLoadSource.Normal) ||
+
(!filter.developer &&
+
ext.source.type === ExtensionLoadSource.Developer) ||
+
(!filter.enabled &&
+
MoonbaseSettingsStore.getExtensionEnabled(ext.id)) ||
+
(!filter.disabled &&
+
!MoonbaseSettingsStore.getExtensionEnabled(ext.id)) ||
+
(!filter.installed && ext.state !== ExtensionState.NotDownloaded) ||
+
(!filter.repository && ext.state === ExtensionState.NotDownloaded)
+
)
+
);
+
+
return (
+
<>
+
<SearchBar
+
size={SearchBar.Sizes.MEDIUM}
+
query={query}
+
onChange={(v: string) => setQuery(v.toLowerCase())}
+
onClear={() => setQuery("")}
+
autoFocus={true}
+
autoComplete="off"
+
inputProps={{
+
autoCapitalize: "none",
+
autoCorrect: "off",
+
spellCheck: "false"
+
}}
+
/>
+
<React.Suspense
+
fallback={<div className={Margins.marginBottom20}></div>}
+
>
+
<FilterBar
+
filter={filter}
+
setFilter={setFilter}
+
selectedTags={selectedTags}
+
setSelectedTags={setSelectedTags}
+
/>
+
</React.Suspense>
+
{filtered.map((ext) => (
+
<ExtensionCard id={ext.id} key={ext.id} />
+
))}
+
</>
+
);
+
};
+
};
+1 -1
packages/core-extensions/src/moonbase/ui/filterBar.tsx packages/core-extensions/src/moonbase/ui/extensions/filterBar.tsx
···
ArrowsUpDownIconSVG,
ChevronSmallDownIconSVG,
ChevronSmallUpIconSVG
-
} from "../types";
+
} from "../../types";
export const defaultFilter = {
core: true,
+50 -93
packages/core-extensions/src/moonbase/ui/index.tsx
···
-
import {
-
ExtensionLoadSource,
-
ExtensionTag,
-
WebpackRequireType
-
} from "@moonlight-mod/types";
-
import card from "./card";
-
import filterBar, { defaultFilter } from "./filterBar";
-
import { ExtensionState } from "../types";
+
import { WebpackRequireType } from "@moonlight-mod/types";
+
import extensionsPage from "./extensions";
+
import configPage from "./config";
-
export enum ExtensionPage {
-
Info,
-
Description,
-
Settings
+
export enum MoonbasePage {
+
Extensions,
+
Config
}
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 Margins = spacepack.findByCode("marginCenterHorz:")[0].exports;
-
const ExtensionCard = card(require);
-
const FilterBar = React.lazy(() =>
-
filterBar(require).then((c) => ({ default: c }))
-
);
+
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;
-
const Margins = spacepack.findByCode("marginCenterHorz:")[0].exports;
-
const SearchBar = spacepack.findByCode("Messages.SEARCH", "hideSearchIcon")[0]
-
.exports.default;
+
const ExtensionsPage = extensionsPage(require);
+
const ConfigPage = configPage(require);
return function Moonbase() {
-
const { Text } = require("common_components");
+
const { Text, TabBar } = require("common_components");
-
const { extensions } = Flux.useStateFromStoresObject(
-
[MoonbaseSettingsStore],
-
() => {
-
return { extensions: MoonbaseSettingsStore.extensions };
-
}
-
);
-
-
const [query, setQuery] = React.useState("");
-
const [filter, setFilter] = React.useState({ ...defaultFilter });
-
const [selectedTags, setSelectedTags] = React.useState(new Set<string>());
-
-
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.core && ext.source.type === ExtensionLoadSource.Core) ||
-
(!filter.normal && ext.source.type === ExtensionLoadSource.Normal) ||
-
(!filter.developer &&
-
ext.source.type === ExtensionLoadSource.Developer) ||
-
(!filter.enabled &&
-
MoonbaseSettingsStore.getExtensionEnabled(ext.id)) ||
-
(!filter.disabled &&
-
!MoonbaseSettingsStore.getExtensionEnabled(ext.id)) ||
-
(!filter.installed && ext.state !== ExtensionState.NotDownloaded) ||
-
(!filter.repository && ext.state === ExtensionState.NotDownloaded)
-
)
+
const [selectedTab, setSelectedTab] = React.useState(
+
MoonbasePage.Extensions
);
return (
<>
-
<Text
-
className={Margins.marginBottom20}
-
variant="heading-lg/semibold"
-
tag="h2"
+
<div
+
className={`${TitleBarClasses.children} ${Margins.marginBottom20}`}
>
-
Moonbase
-
</Text>
-
<SearchBar
-
size={SearchBar.Sizes.MEDIUM}
-
query={query}
-
onChange={(v: string) => setQuery(v.toLowerCase())}
-
onClear={() => setQuery("")}
-
autoFocus={true}
-
autoComplete="off"
-
inputProps={{
-
autoCapitalize: "none",
-
autoCorrect: "off",
-
spellCheck: "false"
-
}}
-
/>
-
<React.Suspense
-
fallback={<div className={Margins.marginBottom20}></div>}
-
>
-
<FilterBar
-
filter={filter}
-
setFilter={setFilter}
-
selectedTags={selectedTags}
-
setSelectedTags={setSelectedTags}
-
/>
-
</React.Suspense>
-
{filtered.map((ext) => (
-
<ExtensionCard id={ext.id} key={ext.id} />
-
))}
+
<Text
+
className={TitleBarClasses.titleWrapper}
+
variant="heading-lg/semibold"
+
tag="h2"
+
>
+
Moonbase
+
</Text>
+
<Divider />
+
<TabBar
+
selectedItem={selectedTab}
+
onItemSelect={setSelectedTab}
+
type="top-pill"
+
className={TabBarClasses.tabBar}
+
>
+
<TabBar.Item
+
id={MoonbasePage.Extensions}
+
className={TabBarClasses.item}
+
>
+
Extensions
+
</TabBar.Item>
+
<TabBar.Item
+
id={MoonbasePage.Config}
+
className={TabBarClasses.item}
+
>
+
Config
+
</TabBar.Item>
+
</TabBar>
+
</div>
+
+
{selectedTab === MoonbasePage.Extensions && <ExtensionsPage />}
+
{selectedTab === MoonbasePage.Config && <ConfigPage />}
</>
);
};
+2 -2
packages/core-extensions/src/moonbase/ui/info.tsx packages/core-extensions/src/moonbase/ui/extensions/info.tsx
···
import WebpackRequire from "@moonlight-mod/types/discord/require";
import { ExtensionTag } from "@moonlight-mod/types";
-
import { MoonbaseExtension } from "../types";
+
import { MoonbaseExtension } from "../../types";
type Dependency = {
id: string;
···
)[0].exports;
const { MoonbaseSettingsStore } =
-
require("moonbase_stores") as typeof import("../webpackModules/stores");
+
require("moonbase_stores") as typeof import("../../webpackModules/stores");
function InfoSection({
title,
+2 -2
packages/core-extensions/src/moonbase/ui/settings.tsx packages/core-extensions/src/moonbase/ui/extensions/settings.tsx
···
SelectSettingType
} from "@moonlight-mod/types/config";
import WebpackRequire from "@moonlight-mod/types/discord/require";
-
import { CircleXIconSVG, MoonbaseExtension } from "../types";
+
import { CircleXIconSVG, MoonbaseExtension } from "../../types";
type SettingsProps = {
ext: MoonbaseExtension;
···
const spacepack = require("spacepack_spacepack").spacepack;
const { MoonbaseSettingsStore } =
-
require("moonbase_stores") as typeof import("../webpackModules/stores");
+
require("moonbase_stores") as typeof import("../../webpackModules/stores");
const Margins = spacepack.findByCode("marginCenterHorz:")[0].exports;
+10
packages/core-extensions/src/moonbase/webpackModules/stores.ts
···
this.emitChange();
}
+
getConfigOption<K extends keyof Config>(key: K): Config[K] {
+
return this.config[key];
+
}
+
+
setConfigOption<K extends keyof Config>(key: K, value: Config[K]) {
+
this.config[key] = value;
+
this.modified = this.isModified();
+
this.emitChange();
+
}
+
writeConfig() {
this.submitting = true;
+1
packages/types/src/coreExtensions/components.ts
···
}>
>;
TextInput: TextInput;
+
FormDivider: ComponentClass<any>;
FormSection: ComponentClass<
PropsWithChildren<{
className?: string;