@ -1,2 +1,3 @@ | |||
bdstorage.json | |||
emote_data.json | |||
logs.log |
@ -1 +0,0 @@ | |||
/home/yigit/.dotfiles/misc/BetterDiscord |
@ -0,0 +1,350 @@ | |||
/** | |||
* @name PlatformIndicators | |||
* @displayName PlatformIndicators | |||
* @authorId 415849376598982656 | |||
* @invite gvA2ree | |||
*/ | |||
/*@cc_on | |||
@if (@_jscript) | |||
// Offer to self-install for clueless users that try to run this directly. | |||
var shell = WScript.CreateObject("WScript.Shell"); | |||
var fs = new ActiveXObject("Scripting.FileSystemObject"); | |||
var pathPlugins = shell.ExpandEnvironmentStrings("%APPDATA%\BetterDiscord\plugins"); | |||
var pathSelf = WScript.ScriptFullName; | |||
// Put the user at ease by addressing them in the first person | |||
shell.Popup("It looks like you've mistakenly tried to run me directly. \n(Don't do that!)", 0, "I'm a plugin for BetterDiscord", 0x30); | |||
if (fs.GetParentFolderName(pathSelf) === fs.GetAbsolutePathName(pathPlugins)) { | |||
shell.Popup("I'm in the correct folder already.", 0, "I'm already installed", 0x40); | |||
} else if (!fs.FolderExists(pathPlugins)) { | |||
shell.Popup("I can't find the BetterDiscord plugins folder.\nAre you sure it's even installed?", 0, "Can't install myself", 0x10); | |||
} else if (shell.Popup("Should I copy myself to BetterDiscord's plugins folder for you?", 0, "Do you need some help?", 0x34) === 6) { | |||
fs.CopyFile(pathSelf, fs.BuildPath(pathPlugins, fs.GetFileName(pathSelf)), true); | |||
// Show the user where to put plugins in the future | |||
shell.Exec("explorer " + pathPlugins); | |||
shell.Popup("I'm installed!", 0, "Successfully installed", 0x40); | |||
} | |||
WScript.Quit(); | |||
@else@*/ | |||
module.exports = (() => { | |||
const config = { | |||
info: { | |||
name: "PlatformIndicators", | |||
authors: [ | |||
{ | |||
name: "Strencher", | |||
discord_id: "415849376598982656", | |||
github_username: "Strencher", | |||
twitter_username: "Strencher3" | |||
} | |||
], | |||
version: "0.0.5", | |||
description: "Adds indicators for every platform that the user is using. Source code availble on the repo in the 'src' folder.", | |||
github: "https://github.com/Strencher/BetterDiscordStuff/blob/master/PlatformIndicators/APlatformIndicators.plugin.js", | |||
github_raw: "https://raw.githubusercontent.com/Strencher/BetterDiscordStuff/master/PlatformIndicators/APlatformIndicators.plugin.js" | |||
}, | |||
changelog: [ | |||
{ | |||
title: "v0.0.5", | |||
type: "fixed", | |||
items: [ | |||
"Thanks to @qwert#1441 for fixing the padding issue in chat messages!", | |||
"I still need ideas where to show all of them at one position that is not next to the username... join my Support server => https://discord.gg/gvA2ree to send me ideas!" | |||
] | |||
}, | |||
{ | |||
title: "v0.0.4", | |||
type: "added", | |||
items: [ | |||
"2 Attempt to fix conflicts with BetterRoleColors.", | |||
"It'll probably require you to update 2 times because the filename has changed.", | |||
"Bug fixes... styling fixes..." | |||
] | |||
} | |||
], | |||
defaultConfig: [ | |||
{ | |||
type: "switch", | |||
name: "Show in MemberList", | |||
note: "Shows the platform indicators in the memberlist", | |||
id: "showInMemberList", | |||
value: true | |||
}, | |||
{ | |||
type: "switch", | |||
name: "Show next to username", | |||
note: "Shows the platform indicators next the username in messages.", | |||
id: "showOnMessages", | |||
value: true | |||
}, | |||
{ | |||
type: "switch", | |||
name: "Show in Dmd List", | |||
note: "Shows the platform indicators in the dm list.", | |||
id: "showInDmsList", | |||
value: true | |||
}, | |||
{ | |||
type: "switch", | |||
name: "Show next to discord tags", | |||
note: "Shows the platform indicators right next to the discord tag.", | |||
id: "showOnTags", | |||
value: true | |||
}, | |||
{ | |||
type: "switch", | |||
name: "Ignore Bots", | |||
note: "Ignores the status of bots which is always web anyways.", | |||
id: "ignoreBots", | |||
value: true | |||
}, | |||
{ | |||
type: "category", | |||
name: "icons", | |||
id: "icons", | |||
settings: [ | |||
{ | |||
type: "switch", | |||
name: "Web Icon", | |||
note: "Show the Web icon.", | |||
id: "web", | |||
value: true | |||
}, | |||
{ | |||
type: "switch", | |||
name: "Desktop Icon", | |||
note: "Show the Desktop icon.", | |||
id: "desktop", | |||
value: true | |||
}, | |||
{ | |||
type: "switch", | |||
name: "Mobile Icon", | |||
note: "Show the Mobile icon.", | |||
id: "mobile", | |||
value: true | |||
} | |||
] | |||
} | |||
] | |||
}; | |||
//@ts-ignore | |||
const BdApi = window.BdApi; | |||
// @ts-ignore | |||
return !global.ZeresPluginLibrary ? class { | |||
constructor() { | |||
this._config = config; | |||
} | |||
getName() { return config.info.name; } | |||
getAuthor() { return config.info.authors.map(a => a.name).join(", "); } | |||
getDescription() { return config.info.description; } | |||
getVersion() { return config.info.version; } | |||
load() { | |||
BdApi.showConfirmationModal("Library plugin is needed", [`The library plugin needed for ${config.info.name} is missing. Please click Download Now to install it.`], { | |||
confirmText: "Download", | |||
cancelText: "Cancel", | |||
onConfirm: () => { | |||
require("request").get("https://rauenzi.github.io/BDPluginLibrary/release/0PluginLibrary.plugin.js", async (error, response, body) => { | |||
if (error) | |||
return require("electron").shell.openExternal("https://betterdiscord.net/ghdl?url=https://raw.githubusercontent.com/rauenzi/BDPluginLibrary/master/release/0PluginLibrary.plugin.js"); | |||
await new Promise(r => require("fs").writeFile(require("path").join(BdApi.Plugins.folder, "0PluginLibrary.plugin.js"), body, r)); | |||
}); | |||
} | |||
}); | |||
} | |||
start() { } | |||
stop() { } | |||
} : (([Plugin, Api]) => { | |||
const plugin = (Plugin, Api) => { | |||
const { Utilities, WebpackModules, PluginUtilities, ReactTools, Patcher, Logger, DiscordModules: { React, UserStatusStore, Dispatcher, DiscordConstants: { ActionTypes } } } = Api; | |||
const Utils = Object.assign(Utilities, { | |||
joinClassNames: (...classNames) => classNames.filter(Boolean).join(" "), | |||
capFirst(text) { | |||
return text[0].toUpperCase() + text.slice(1); | |||
} | |||
}); | |||
const DesktopIcon = React.memo(props => (React.createElement("svg", Object.assign({ className: "PI-icon_desktop", width: "24", height: "24" }, props, { viewBox: "0 0 24 24" }), | |||
React.createElement("path", { fill: "currentColor", d: "M4 2.5C2.897 2.5 2 3.397 2 4.5V15.5C2 16.604 2.897 17.5 4 17.5H11V19.5H7V21.5H17V19.5H13V17.5H20C21.103 17.5 22 16.604 22 15.5V4.5C22 3.397 21.103 2.5 20 2.5H4ZM20 4.5V13.5H4V4.5H20Z" })))); | |||
const WebIcon = React.memo(props => (React.createElement("svg", Object.assign({ className: "PI-icon_web", width: "24", height: "24" }, props, { viewBox: "0 0 24 24" }), | |||
React.createElement("path", { fill: "currentColor", d: "M12 2C6.48 2 2 6.48 2 12C2 17.52 6.48 22 12 22C17.52 22 22 17.52 22 12C22 6.48 17.52 2 12 2ZM11 19.93C7.05 19.44 4 16.08 4 12C4 11.38 4.08 10.79 4.21 10.21L9 15V16C9 17.1 9.9 18 11 18V19.93ZM17.9 17.39C17.64 16.58 16.9 16 16 16H15V13C15 12.45 14.55 12 14 12H8V10H10C10.55 10 11 9.55 11 9V7H13C14.1 7 15 6.1 15 5V4.59C17.93 5.78 20 8.65 20 12C20 14.08 19.2 15.97 17.9 17.39Z" })))); | |||
const MobileIcon = React.memo(props => (React.createElement("svg", Object.assign({ className: "PI-icon_mobile", width: "24", height: "24" }, props, { viewBox: "0 0 24 24" }), | |||
React.createElement("g", { fill: "none" }, | |||
React.createElement("path", { fill: "currentColor", d: "M15.5 1h-8C6.12 1 5 2.12 5 3.5v17C5 21.88 6.12 23 7.5 23h8c1.38 0 2.5-1.12 2.5-2.5v-17C18 2.12 16.88 1 15.5 1zm-4 21c-.83 0-1.5-.67-1.5-1.5s.67-1.5 1.5-1.5 1.5.67 1.5 1.5-.67 1.5-1.5 1.5zm4.5-4H7V4h9v14z" }))))); | |||
const Icons = { | |||
mobile: MobileIcon, | |||
web: WebIcon, | |||
desktop: DesktopIcon | |||
}; | |||
const getClass = (props = [], items = props, exclude = [], selector = false) => { | |||
const module = WebpackModules.getModule(m => m && props.every(prop => m[prop] !== undefined) && exclude.every(e => m[e] == undefined)); | |||
if (!module) | |||
return ''; | |||
return (selector ? '.' : '') + items.map(item => module[item]).join(selector ? '.' : ' '); | |||
}; | |||
const { TooltipContainer: Tooltip } = WebpackModules.getByProps("TooltipContainer"); | |||
const StatusModule = WebpackModules.getByProps("Status", "getStatusMask"); | |||
const Flux = WebpackModules.getByProps("connectStores"); | |||
const MessageTimestamp = WebpackModules.getByProps("MessageTimestamp"); | |||
const { Messages } = WebpackModules.getByProps("Messages", "setLocale"); | |||
const AuthStore = WebpackModules.getByProps("getId", "getEmail"); | |||
let plugin, currentClientStatus; | |||
const StatusIndicators = function StatusIndicators(props) { | |||
if (!props) | |||
return null; | |||
return (React.createElement("div", { className: Utils.joinClassNames("PI-indicatorContainer", "PI-type_" + props.type) }, Object.keys(props).filter(e => plugin.settings.icons[e]).map(e => { | |||
const color = StatusModule.getStatusColor(props[e]); | |||
const Icon = Icons[e]; | |||
return React.createElement(Tooltip, { text: Utils.capFirst(e) + ": " + Messages[`STATUS_${(props[e] == "mobile" ? "mobile_online" : props[e]).toUpperCase()}`], position: "top" }, | |||
React.createElement(Icon, { style: { color }, width: "18", height: "18" })); | |||
}))); | |||
}; | |||
return class PlatformIndicators extends Plugin { | |||
constructor() { | |||
super(...arguments); | |||
this.css = ` | |||
.PI-indicatorContainer { | |||
display: inline-flex; | |||
} | |||
.PI-indicatorContainer svg { | |||
margin-left: 2px; | |||
} | |||
.header-23xsNx { | |||
display: flex !important; | |||
flex-direction: row !important; | |||
} | |||
.PI-container { | |||
display: flex; | |||
} | |||
`; | |||
this.getSettingsPanel = () => { | |||
return this.buildSettingsPanel().getElement(); | |||
}; | |||
this.ON_PRESENCE_UPDATE = ({ user, clientStatus }) => { | |||
if (user.id != AuthStore.getId()) | |||
return; | |||
currentClientStatus = clientStatus; | |||
UserStatusStore.emitChange(); | |||
}; | |||
} | |||
getClients(userId) { | |||
const isSelf = userId == AuthStore.getId(); | |||
const status = isSelf ? currentClientStatus : UserStatusStore.getState().clientStatuses[userId]; | |||
return status !== null && status !== void 0 ? status : {}; | |||
} | |||
onStart() { | |||
plugin = this; | |||
PluginUtilities.addStyle(config.info.name, this.css); | |||
Utils.suppressErrors(this.patchMessageHeader.bind(this))(); | |||
Utils.suppressErrors(this.patchMemberListItem.bind(this))(); | |||
Utils.suppressErrors(this.patchDmList.bind(this))(); | |||
Utils.suppressErrors(this.patchDiscordTag.bind(this))(); | |||
Dispatcher.subscribe(ActionTypes.PRESENCE_UPDATE, this.ON_PRESENCE_UPDATE); | |||
} | |||
async patchMemberListItem() { | |||
const MemberListItem = WebpackModules.getByDisplayName("MemberListItem"); | |||
Patcher.after(MemberListItem.prototype, "renderDecorators", ({ props }, _, returnValue) => { | |||
var _a; | |||
if (!this.settings.showInMemberList) | |||
return; | |||
try { | |||
const tree = (_a = returnValue === null || returnValue === void 0 ? void 0 : returnValue.props) === null || _a === void 0 ? void 0 : _a.children; | |||
if (!Array.isArray(tree) || (this.settings.ignoreBots && props.user.bot)) | |||
return; | |||
const FluxWrapper = Flux.connectStores([UserStatusStore], () => this.getClients(props.user.id))(clients => React.createElement(StatusIndicators, Object.assign({}, clients, { type: "memberList" }))); | |||
tree.unshift(React.createElement(FluxWrapper, null)); | |||
} | |||
catch (error) { | |||
Logger.error("Error while patching MemberListItem:", error); | |||
} | |||
}); | |||
this.forceUpdate(getClass(["member"], ["member"], [], true)); | |||
} | |||
patchMessageHeader() { | |||
Patcher.after(MessageTimestamp, "default", (_, [props], returnValue) => { | |||
if (!this.settings.showOnMessages) | |||
return; | |||
try { | |||
const tree = Utils.getNestedProp(returnValue, "props.children.1.props.children"); | |||
if (!Array.isArray(tree) || (this.settings.ignoreBots && props.message.author.bot)) | |||
return; | |||
const FluxWrapper = Flux.connectStores([UserStatusStore], () => this.getClients(props.message.author.id))(clients => React.createElement(StatusIndicators, Object.assign({}, clients, { type: "chat" }))); | |||
tree.splice(2, 0, React.createElement(FluxWrapper, null)); | |||
} | |||
catch (error) { | |||
Logger.error("Error while patching MessageTimestammp:", error); | |||
} | |||
}); | |||
} | |||
patchDmList() { | |||
var _a; | |||
const { default: PrivateChannel } = (_a = WebpackModules.getModule(m => { var _a; return ((_a = m === null || m === void 0 ? void 0 : m.default) === null || _a === void 0 ? void 0 : _a.displayName) === "PrivateChannel"; })) !== null && _a !== void 0 ? _a : {}; | |||
Patcher.after(PrivateChannel.prototype, "render", (_this, _, ret) => { | |||
const unpatch = Patcher.after(ret.type, "render", (_, __, ret) => { | |||
var _a, _b; | |||
unpatch(); | |||
if (!this.settings.showInDmsList) | |||
return; | |||
const tree = Utils.findInReactTree(ret, m => { var _a; return ((_a = m === null || m === void 0 ? void 0 : m.className) === null || _a === void 0 ? void 0 : _a.indexOf("nameAndDecorators")) > -1; }); | |||
if (!tree) | |||
return; | |||
if (!Array.isArray(tree === null || tree === void 0 ? void 0 : tree.children) || (this.settings.ignoreBots && ((_b = (_a = _this.props) === null || _a === void 0 ? void 0 : _a.user) === null || _b === void 0 ? void 0 : _b.bot))) | |||
return; | |||
const FluxWrapper = Flux.connectStores([UserStatusStore], () => { var _a, _b; return this.getClients((_b = (_a = _this.props) === null || _a === void 0 ? void 0 : _a.user) === null || _b === void 0 ? void 0 : _b.id); })(clients => React.createElement(StatusIndicators, Object.assign({}, clients, { type: "dmList" }))); | |||
tree.children = [ | |||
tree.children, | |||
React.createElement(FluxWrapper, null) | |||
]; | |||
}); | |||
}); | |||
this.forceUpdate(getClass(["privateChannels"], ["privateChannels"], [], true)); | |||
} | |||
forceUpdate(selector) { | |||
const nodes = document.querySelectorAll(selector); | |||
if (!nodes.length) | |||
return; | |||
for (const node of nodes) { | |||
const instance = ReactTools.getOwnerInstance(node); | |||
if (!instance) | |||
return; | |||
instance.forceUpdate(); | |||
} | |||
} | |||
patchDiscordTag() { | |||
const DiscordTag = WebpackModules.getModule(m => { var _a; return ((_a = m === null || m === void 0 ? void 0 : m.default) === null || _a === void 0 ? void 0 : _a.displayName) === "DiscordTag"; }); | |||
const NameTag = WebpackModules.getModule(m => { var _a; return ((_a = m === null || m === void 0 ? void 0 : m.default) === null || _a === void 0 ? void 0 : _a.displayName) === "NameTag"; }); | |||
Patcher.after(DiscordTag, "default", (_, [{ user }], ret) => { | |||
ret.props.user = user; | |||
}); | |||
Patcher.after(NameTag, "default", (_, [args], ret) => { | |||
if (!this.settings.showOnTags) | |||
return; | |||
const tree = ret === null || ret === void 0 ? void 0 : ret.props; | |||
var { user } = args; | |||
if (!Array.isArray(tree === null || tree === void 0 ? void 0 : tree.children) || (this.settings.ignoreBots && (user === null || user === void 0 ? void 0 : user.bot))) | |||
return; | |||
const FluxWrapper = Flux.connectStores([UserStatusStore], () => this.getClients(user === null || user === void 0 ? void 0 : user.id))(clients => React.createElement(StatusIndicators, Object.assign({}, clients, { type: "discordTag" }))); | |||
try { | |||
tree.children.push(React.createElement(FluxWrapper, null)); | |||
} | |||
catch (error) { | |||
Logger.error("Failed to inject into NameTag:\n", error); | |||
} | |||
return ret; | |||
}); | |||
} | |||
onStop() { | |||
Patcher.unpatchAll(); | |||
PluginUtilities.removeStyle(config.info.name); | |||
Dispatcher.unsubscribe(ActionTypes.PRESENCE_UPDATE, this.ON_PRESENCE_UPDATE); | |||
} | |||
}; | |||
}; | |||
return plugin(Plugin, Api); | |||
//@ts-ignore | |||
})(global.ZeresPluginLibrary.buildPlugin(config)); | |||
})(); | |||
/*@end@*/ |
@ -0,0 +1,6 @@ | |||
{ | |||
"currentVersionInfo": { | |||
"version": "1.0.0", | |||
"hasShownChangelog": true | |||
} | |||
} |
@ -0,0 +1,674 @@ | |||
/** | |||
* @name BetterCodeblocks | |||
* @invite undefined | |||
* @authorLink undefined | |||
* @donate undefined | |||
* @patreon undefined | |||
* @website https://github.com/vBread/BetterCodeblocks | |||
* @source https://github.com/vBread/BetterCodeblocks/blob/master/BetterCodeblocks.plugin.js | |||
*/ | |||
/*@cc_on | |||
@if (@_jscript) | |||
// Offer to self-install for clueless users that try to run this directly. | |||
var shell = WScript.CreateObject("WScript.Shell"); | |||
var fs = new ActiveXObject("Scripting.FileSystemObject"); | |||
var pathPlugins = shell.ExpandEnvironmentStrings("%APPDATA%\BetterDiscord\plugins"); | |||
var pathSelf = WScript.ScriptFullName; | |||
// Put the user at ease by addressing them in the first person | |||
shell.Popup("It looks like you've mistakenly tried to run me directly. \n(Don't do that!)", 0, "I'm a plugin for BetterDiscord", 0x30); | |||
if (fs.GetParentFolderName(pathSelf) === fs.GetAbsolutePathName(pathPlugins)) { | |||
shell.Popup("I'm in the correct folder already.", 0, "I'm already installed", 0x40); | |||
} else if (!fs.FolderExists(pathPlugins)) { | |||
shell.Popup("I can't find the BetterDiscord plugins folder.\nAre you sure it's even installed?", 0, "Can't install myself", 0x10); | |||
} else if (shell.Popup("Should I copy myself to BetterDiscord's plugins folder for you?", 0, "Do you need some help?", 0x34) === 6) { | |||
fs.CopyFile(pathSelf, fs.BuildPath(pathPlugins, fs.GetFileName(pathSelf)), true); | |||
// Show the user where to put plugins in the future | |||
shell.Exec("explorer " + pathPlugins); | |||
shell.Popup("I'm installed!", 0, "Successfully installed", 0x40); | |||
} | |||
WScript.Quit(); | |||
@else@*/ | |||
module.exports = (() => { | |||
const config = { "info": { "name": "BetterCodeblocks", "authors": [{ "name": "Bread", "discord_id": "304260051915374603" }], "version": "1.0.0", "description": "Enhances the look and feel of Discord's codeblocks with customizable colors", "github": "https://github.com/vBread/BetterCodeblocks", "github_raw": "https://github.com/vBread/BetterCodeblocks/blob/master/BetterCodeblocks.plugin.js" }, "changelog": [], "main": "index.js" }; | |||
return !global.ZeresPluginLibrary ? class { | |||
constructor() { this._config = config; } | |||
getName() { return config.info.name; } | |||
getAuthor() { return config.info.authors.map(a => a.name).join(", "); } | |||
getDescription() { return config.info.description; } | |||
getVersion() { return config.info.version; } | |||
load() { | |||
BdApi.showConfirmationModal("Library Missing", `The library plugin needed for ${config.info.name} is missing. Please click Download Now to install it.`, { | |||
confirmText: "Download Now", | |||
cancelText: "Cancel", | |||
onConfirm: () => { | |||
require("request").get("https://rauenzi.github.io/BDPluginLibrary/release/0PluginLibrary.plugin.js", async (error, response, body) => { | |||
if (error) return require("electron").shell.openExternal("https://betterdiscord.net/ghdl?url=https://raw.githubusercontent.com/rauenzi/BDPluginLibrary/master/release/0PluginLibrary.plugin.js"); | |||
await new Promise(r => require("fs").writeFile(require("path").join(BdApi.Plugins.folder, "0PluginLibrary.plugin.js"), body, r)); | |||
}); | |||
} | |||
}); | |||
} | |||
start() {} | |||
stop() {} | |||
} : (([Plugin, Api]) => { | |||
const plugin = (Plugin, Library) => { | |||
const { Patcher, WebpackModules, DiscordModules, PluginUtilities, Settings } = Library; | |||
const { SettingPanel, SettingGroup, Textbox } = Settings | |||
const { React, hljs } = DiscordModules | |||
return class BetterCodeblocks extends Plugin { | |||
constructor() { | |||
super() | |||
this.defaults = { | |||
addition: '#98c379', | |||
attr_1: '#d19a66', | |||
attr_2: '#d19a66', | |||
attribute: '#98c379', | |||
background: '#282c34', | |||
built_in: '#e6c07b', | |||
bullet: '#61aeee', | |||
code: '#abb2bf', | |||
comment: '#5c6370', | |||
deletion: '#e06c75', | |||
doctag: '#c678dd', | |||
keyword: '#c678dd', | |||
literal: '#56b6c2', | |||
meta_string: '#98c379', | |||
meta: '#61aeee', | |||
name: '#e06c75', | |||
nomarkup: '#98c379', | |||
number: '#d19a66', | |||
params: '#abb2bf', | |||
quote: '#5c6370', | |||
regexp: '#98c379', | |||
section: '#e06c75', | |||
selector_attr: '#d19a66', | |||
selector_class: '#d19a66', | |||
selector_id: '#61aeee', | |||
selector_pseudo: '#d19a66', | |||
selector_tag: '#e06c75', | |||
string: '#98c379', | |||
subst: '#e06c75', | |||
symbol: '#61aeee', | |||
tag: '#e06c75', | |||
template_variable: '#d19a66', | |||
text: '#abb2bf', | |||
title: '#61aeee', | |||
type: '#d19a66', | |||
variable: '#d19a66' | |||
} | |||
this.hljs = PluginUtilities.loadSettings('BetterCodeblocks', this.defaults) | |||
} | |||
onStart() { | |||
const parser = WebpackModules.getByProps('parse', 'parseTopic') | |||
Patcher.after(parser.defaultRules.codeBlock, 'react', (_, args, res) => { | |||
this.inject(args, res) | |||
return res | |||
}); | |||
PluginUtilities.addStyle('BetterCodeblocks', this.css) | |||
} | |||
onStop() { | |||
PluginUtilities.removeStyle('BetterCodeblocks') | |||
Patcher.unpatchAll(); | |||
} | |||
getSettingsPanel() { | |||
return SettingPanel.build(PluginUtilities.saveSettings('BetterCodeblocks', this.hljs), | |||
new SettingGroup('Customization').append( | |||
new Textbox('Additions', 'Changes the color of additions for Diff', this.hljs.addition, (color) => this.updateColor('addition', color)), | |||
new Textbox('Annotation Tags', 'Changes the color of documentation/annotation tags', this.hljs.doctag, (color) => this.updateColor('doctag', color)), | |||
new Textbox('Attributes', 'Changes the color of HTML tag attributes', this.hljs.attribute, (color) => this.updateColor('attribute', color)), | |||
new Textbox('Background', 'Changes the color of the codeblock background', this.hljs.background, (color) => this.updateColor('background', color)), | |||
new Textbox('Built-In', 'Changes the color of built-in keywords', this.hljs.built_in, (color) => this.updateColor('built_in', color)), | |||
new Textbox('Bullets', 'Changes the color of bullet points for Markdown', this.hljs.bullet, (color) => this.updateColor('bullet', color)), | |||
new Textbox('Comments', 'Changes the color of comments', this.hljs.comment, (color) => this.updateColor('comment', color)), | |||
new Textbox('Deletions', 'Changes the color of deletions for Diff', this.hljs.deletion, (color) => this.updateColor('deletion', color)), | |||
new Textbox('Keywords', 'Changes the color of keywords', this.hljs.keyword, (color) => this.updateColor('keyword', color)), | |||
new Textbox('Literals', 'Changes the color of literal keywords', this.hljs.literal, (color) => this.updateColor('literal', color)), | |||
new Textbox('Names', 'Changes the color of function names', this.hljs.title, (color) => this.updateColor('title', color)), | |||
new Textbox('Number', 'Changes the color of numbers', this.hljs.number, (color) => this.updateColor('number', color)), | |||
new Textbox('Parameters', 'Changes the color of function parameters', this.hljs.params, (color) => this.updateColor('params', color)), | |||
new Textbox('Regular Expressions', 'Changes the color of regular expressions', this.hljs.regexp, (color) => this.updateColor('regexp', color)), | |||
new Textbox('Selector Attributes', 'Changes the color of CSS selector attributes', this.hljs.selector_attr, (color) => this.updateColor('selector_attr', color)), | |||
new Textbox('Selector Classes', 'Changes the color of CSS selector classes', this.hljs.selector_class, (color) => this.updateColor('selector_class', color)), | |||
new Textbox('Selector IDs', 'Changes the color of CSS selector IDs', this.hljs.selector_id, (color) => this.updateColor('selector_id', color)), | |||
new Textbox('Selector Pseudos', 'Changes the color of CSS selector pseudos', this.hljs.selector_pseudo, (color) => this.updateColor('selector_pseudo', color)), | |||
new Textbox('Selector Tags', 'Changes the color of CSS selector tags', this.hljs.selector_tag, (color) => this.updateColor('selector_tag', color)), | |||
new Textbox('Strings', 'Changes the color of strings', this.hljs.string, (color) => this.updateColor('string', color)), | |||
new Textbox('Template Literals', 'Changes the color of template literals', this.hljs.template_variable, (color) => this.updateColor('template_variable', color)), | |||
new Textbox('Types', 'Changes the color of types', this.hljs.type, (color) => this.updateColor('type', color)), | |||
new Textbox('Variables', 'Changes the color of variables', this.hljs.variable, (color) => this.updateColor('variable', color)), | |||
) | |||
) | |||
} | |||
updateColor(property, color) { | |||
let reset = false | |||
if (!/#?\w{6}/.test(color) || color === '') { | |||
color = this.defaults[property] | |||
reset = true | |||
} | |||
if (!color.startsWith('#')) { | |||
color = `#${color}` | |||
} | |||
this.hljs[property] = color | |||
if (reset) PluginUtilities.saveSettings('BetterCodeblocks', this.hljs) | |||
PluginUtilities.removeStyle('BetterCodeblocks') | |||
PluginUtilities.addStyle('BetterCodeblocks', this.css) | |||
} | |||
inject(args, res) { | |||
const { render } = res.props; | |||
res.props.render = (props) => { | |||
const codeblock = render(props); | |||
const codeElement = codeblock.props.children; | |||
const classes = codeElement.props.className.split(' '); | |||
const lang = args ? args[0].lang : classes[classes.indexOf('hljs') + 1]; | |||
const lines = codeElement.props.dangerouslySetInnerHTML | |||
? codeElement.props.dangerouslySetInnerHTML.__html | |||
.replace( | |||
/<span class="(hljs-[a-z]+)">([^<]*)<\/span>/g, | |||
(_, className, code) => code.split('\n').map(l => `<span class="${className}">${l}</span>`).join('\n') | |||
) | |||
.split('\n') | |||
: codeElement.props.children.split('\n'); | |||
delete codeElement.props.dangerouslySetInnerHTML; | |||
codeElement.props.children = this.render(lang, lines); | |||
return codeblock; | |||
}; | |||
} | |||
render(lang, lines) { | |||
const { Messages } = WebpackModules.getByProps('Messages') | |||
if (hljs && typeof hljs.getLanguage === 'function') { | |||
lang = hljs.getLanguage(lang); | |||
} | |||
return React.createElement(React.Fragment, null, | |||
lang && React.createElement('div', { className: 'bd-codeblock-lang' }, lang.name), | |||
React.createElement('table', { className: 'bd-codeblock-table' }, | |||
...lines.map((line, i) => React.createElement('tr', null, | |||
React.createElement('td', null, i + 1), | |||
React.createElement('td', | |||
lang ? { | |||
dangerouslySetInnerHTML: { | |||
__html: line | |||
} | |||
} : { | |||
children: line | |||
} | |||
) | |||
)) | |||
), | |||
React.createElement('button', { | |||
className: 'bd-codeblock-copy-btn', | |||
onClick: this.clickHandler | |||
}, Messages.COPY) | |||
); | |||
} | |||
clickHandler({ target }) { | |||
const { Messages } = WebpackModules.getByProps('Messages') | |||
const { clipboard } = require('electron') | |||
if (target.classList.contains('copied')) return; | |||
target.innerText = Messages.ACCOUNT_USERNAME_COPY_SUCCESS_1; | |||
target.classList.add('copied'); | |||
setTimeout(() => { | |||
target.innerText = Messages.COPY; | |||
target.classList.remove('copied'); | |||
}, 1e3); | |||
const code = [...target.parentElement.querySelectorAll('td:last-child')].map(t => t.textContent).join('\n'); | |||
clipboard.writeText(code); | |||
} | |||
get css() { | |||
return ` | |||
.hljs { | |||
background-color: ${this.hljs.background} !important; | |||
color: ${this.hljs.text}; | |||
position: relative; | |||
} | |||
.hljs:not([class$='hljs']) { | |||
padding-top: 2px; | |||
} | |||
.bd-codeblock-lang { | |||
color: var(--text-normal); | |||
border-bottom: 1px solid var(--background-modifier-accent); | |||
padding: 0 5px; | |||
margin-bottom: 6px; | |||
font-size: .8em; | |||
font-family: 'Raleway', sans-serif; | |||
font-weight: bold; | |||
} | |||
.bd-codeblock-table { | |||
border-collapse: collapse; | |||
} | |||
.bd-codeblock-table tr { | |||
height: 19px; | |||
width: 100%; | |||
} | |||
.bd-codeblock-table td:first-child { | |||
border-right: 1px solid var(--background-modifier-accent); | |||
padding-left: 5px; | |||
padding-right: 8px; | |||
user-select: none; | |||
} | |||
.bd-codeblock-table td:last-child { | |||
padding-left: 8px; | |||
word-break: break-all; | |||
} | |||
.bd-codeblock-copy-btn { | |||
color: #fff; | |||
border-radius: 4px; | |||
line-height: 20px; | |||
padding: 0 10px; | |||
font-family: 'Raleway', sans-serif; | |||
font-size: .8em; | |||
text-transform: uppercase; | |||
font-weight: bold; | |||
margin: 3px; | |||
background: var(--background-floating); | |||
position: absolute; | |||
right: 0 !important; | |||
bottom: 0 !important; | |||
opacity: 0; | |||
transition: .3s; | |||
} | |||
.bd-codeblock-copy-btn.copied { | |||
background-color: #43b581; | |||
opacity: 1; | |||
} | |||
.hljs:hover .bd-codeblock-copy-btn { | |||
opacity: 1; | |||
} | |||
// HLJS Styling | |||
.hljs > .bd-codeblock-table > tr > td > span > .hljs-tag { | |||
color: ${this.hljs.tag}; | |||
} | |||
.hljs > .bd-codeblock-table > tr > td > span > .hljs-tag > .hljs-name { | |||
color: ${this.hljs.name}; | |||
} | |||
.hljs > .bd-codeblock-table > tr > td > span > .hljs-tag > .hljs-attr { | |||
color: ${this.hljs.attr_2}; | |||
} | |||
.hljs > .bd-codeblock-table > tr > td > .bash > .hljs-built_in { | |||
color: ${this.hljs.built_in}; | |||
} | |||
.hljs > .bd-codeblock-table > tr > td > .bash > .hljs-variable { | |||
color: ${this.hljs.variable}; | |||
} | |||
.hljs > .bd-codeblock-table > tr > td > .hljs-tag { | |||
color: ${this.hljs.tag} !important; | |||
} | |||
.hljs > .bd-codeblock-table > tr > td > .hljs-tag > .hljs-name { | |||
color: ${this.hljs.tag}; | |||
} | |||
.hljs > .bd-codeblock-table > tr > td > .hljs-tag > .hljs-attr { | |||
color: ${this.hljs.tag}; | |||
} | |||
.hljs > .bd-codeblock-table > tr > td > .hljs-function > .hljs-params { | |||
color: ${this.hljs.params}; | |||
} | |||
.hljs > .bd-codeblock-table > tr > td > .hljs-function > .hljs-params > .hljs-type { | |||
color: ${this.hljs.type}; | |||
} | |||
.hljs > .bd-codeblock-table > tr > td > .hljs-params { | |||
color: ${this.hljs.params}; | |||
} | |||
.hljs > .bd-codeblock-table > tr > td > .hljs-params > .hljs-built_in { | |||
color: ${this.hljs.built_in}; | |||
} | |||
.hljs > .bd-codeblock-table > tr > td > .hljs-selector-attr { | |||
color: ${this.hljs.selector_attr}; | |||
} | |||
.hljs > .bd-codeblock-table > tr > td > .hljs-type { | |||
color: ${this.hljs.type}; | |||
} | |||
.hljs > .bd-codeblock-table > tr > td > .hljs-selector-id { | |||
color: ${this.hljs.selector_id}; | |||
} | |||
.hljs > .bd-codeblock-table > tr > td > .hljs-selector-pseudo { | |||
color: ${this.hljs.selector_pseudo}; | |||
} | |||
.hljs > .bd-codeblock-table > tr > td > .hljs-bullet { | |||
color: ${this.hljs.bullet}; | |||
} | |||
.hljs > .bd-codeblock-table > tr > td > .hljs-addition { | |||
color: ${this.hljs.addition}; | |||
} | |||
.hljs > .bd-codeblock-table > tr > td > .hljs-deletion { | |||
color: ${this.hljs.deletion}; | |||
} | |||
.hljs > .bd-codeblock-table > tr > td > .hljs-regexp { | |||
color: ${this.hljs.regexp}; | |||
} | |||
.hljs > .bd-codeblock-table > tr > td > .hljs-doctag { | |||
color: ${this.hljs.doctag}; | |||
} | |||
.hljs > .bd-codeblock-table > tr > td > .hljs-built_in { | |||
color: ${this.hljs.built_in}; | |||
} | |||
.hljs > .bd-codeblock-table > tr > td > .hljs-attr { | |||
color: ${this.hljs.attr_1}; | |||
} | |||
.hljs > .bd-codeblock-table > tr > td .hljs-nomarkup > span { | |||
color: ${this.hljs.nomarkup}; | |||
} | |||
.hljs > .bd-codeblock-table > tr > td .hljs-section { | |||
color: ${this.hljs.section}; | |||
} | |||
.hljs > .bd-codeblock-table > tr > td .hljs-meta { | |||
color: ${this.hljs.meta}; | |||
} | |||
.hljs > .bd-codeblock-table > tr > td .hljs-literal { | |||
color: ${this.hljs.literal}; | |||
} | |||
.hljs > .bd-codeblock-table > tr > td .hljs-title { | |||
color: ${this.hljs.title}; | |||
} | |||
.hljs > .bd-codeblock-table > tr > td .hljs-keyword { | |||
color: ${this.hljs.keyword}; | |||
} | |||
.hljs > .bd-codeblock-table > tr > td .hljs-selector-tag { | |||
color: ${this.hljs.selector_tag}; | |||
} | |||
.hljs > .bd-codeblock-table > tr > td .hljs-selector-class { | |||
color: ${this.hljs.selector_class}; | |||
} | |||
.hljs > .bd-codeblock-table > tr > td .hljs-attribute { | |||
color: ${this.hljs.attribute}; | |||
} | |||
.hljs > .bd-codeblock-table > tr > td .hljs-symbol { | |||
color: ${this.hljs.symbol}; | |||
} | |||
.hljs > .bd-codeblock-table > tr > td .hljs-number { | |||
color: ${this.hljs.number}; | |||
} | |||
.hljs > .bd-codeblock-table > tr > td .hljs-string { | |||
color: ${this.hljs.string}; | |||
} | |||
.hljs > .bd-codeblock-table > tr > td .hljs-subst { | |||
color: ${this.hljs.subst}; | |||
} | |||
.hljs > .bd-codeblock-table > tr > td .hljs-code { | |||
color: ${this.hljs.code}; | |||
} | |||
.hljs > .bd-codeblock-table > tr > td .hljs-comment { | |||
color: ${this.hljs.comment}; | |||
} | |||
.hljs > .bd-codeblock-table > tr > td .hljs-quote { | |||
color: ${this.hljs.quote}; | |||
} | |||
.hljs > .bd-codeblock-table > tr > td .hljs-variable { | |||
color: ${this.hljs.variable}; | |||
} | |||
.hljs > .bd-codeblock-table > tr > td .hljs-template-variable { | |||
color: ${this.hljs.template_variable}; | |||
} | |||
.hljs > .bd-codeblock-table > tr > td .hljs-meta-string { | |||
color: ${this.hljs.meta_string}; | |||
} | |||
// Chat CB | |||
.codeLine-14BKbG > span > span { | |||
color: ${this.hljs.text}; | |||
} | |||
.codeLine-14BKbG > span > span > span > .hljs-tag { | |||
color: ${this.hljs.tag}; | |||
} | |||
.codeLine-14BKbG > span > span > span > .hljs-tag > .hljs-name { | |||
color: ${this.hljs.name}; | |||
} | |||
.codeLine-14BKbG > span > span > span > .hljs-tag > .hljs-attr { | |||
color: ${this.hljs.attr_2}; | |||
} | |||
.codeLine-14BKbG > span > span > .bash > .hljs-built_in { | |||
color: ${this.hljs.built_in}; | |||
} | |||
.codeLine-14BKbG > span > span > .bash > .hljs-variable { | |||
color: ${this.hljs.variable}; | |||
} | |||
.codeLine-14BKbG > span > span > .hljs-tag { | |||
color: ${this.hljs.tag} !important; | |||
} | |||
.codeLine-14BKbG > span > span > .hljs-tag > .hljs-name { | |||
color: ${this.hljs.tag}; | |||
} | |||
.codeLine-14BKbG > span > span > .hljs-tag > .hljs-attr { | |||
color: ${this.hljs.tag}; | |||
} | |||
.codeLine-14BKbG > span > span > .hljs-function > .hljs-params { | |||
color: ${this.hljs.params}; | |||
} | |||
.codeLine-14BKbG > span > span > .hljs-function > .hljs-params > .hljs-type { | |||
color: ${this.hljs.type}; | |||
} | |||
.codeLine-14BKbG > span > span > .hljs-params { | |||
color: ${this.hljs.params}; | |||
} | |||
.codeLine-14BKbG > span > span > .hljs-params > .hljs-built_in { | |||
color: ${this.hljs.built_in}; | |||
} | |||
.codeLine-14BKbG > span > span > .hljs-selector-attr { | |||
color: ${this.hljs.selector_attr}; | |||
} | |||
.codeLine-14BKbG > span > span > .hljs-type { | |||
color: ${this.hljs.type}; | |||
} | |||
.codeLine-14BKbG > span > span > .hljs-selector-id { | |||
color: ${this.hljs.selector_id}; | |||
} | |||
.codeLine-14BKbG > span > span > .hljs-selector-pseudo { | |||
color: ${this.hljs.selector_pseudo}; | |||
} | |||
.codeLine-14BKbG > span > span > .hljs-bullet { | |||
color: ${this.hljs.bullet}; | |||
} | |||
.codeLine-14BKbG > span > span > .hljs-addition { | |||
color: ${this.hljs.addition}; | |||
} | |||
.codeLine-14BKbG > span > span > .hljs-deletion { | |||
color: ${this.hljs.deletion}; | |||
} | |||
.codeLine-14BKbG > span > span > .hljs-regexp { | |||
color: ${this.hljs.regexp}; | |||
} | |||
.codeLine-14BKbG > span > span > .hljs-doctag { | |||
color: ${this.hljs.doctag}; | |||
} | |||
.codeLine-14BKbG > span > span > .hljs-built_in { | |||
color: ${this.hljs.built_in}; | |||
} | |||
.codeLine-14BKbG > span > span > .hljs-attr { | |||
color: ${this.hljs.attr_1}; | |||
} | |||
.codeLine-14BKbG > span > span .hljs-nomarkup > span { | |||
color: ${this.hljs.nomarkup}; | |||
} | |||
.codeLine-14BKbG > span > span .hljs-section { | |||
color: ${this.hljs.section}; | |||
} | |||
.codeLine-14BKbG > span > span .hljs-meta { | |||
color: ${this.hljs.meta}; | |||
} | |||
.codeLine-14BKbG > span > span .hljs-literal { | |||
color: ${this.hljs.literal}; | |||
} | |||
.codeLine-14BKbG > span > span .hljs-title { | |||
color: ${this.hljs.title}; | |||
} | |||
.codeLine-14BKbG > span > span .hljs-keyword { | |||
color: ${this.hljs.keyword}; | |||
} | |||
.codeLine-14BKbG > span > span .hljs-selector-tag { | |||
color: ${this.hljs.selector_tag}; | |||
} | |||
.codeLine-14BKbG > span > span .hljs-selector-class { | |||
color: ${this.hljs.selector_class}; | |||
} | |||
.codeLine-14BKbG > span > span .hljs-attribute { | |||
color: ${this.hljs.attribute}; | |||
} | |||
.codeLine-14BKbG > span > span .hljs-symbol { | |||
color: ${this.hljs.symbol}; | |||
} | |||
.codeLine-14BKbG > span > span .hljs-number { | |||
color: ${this.hljs.number}; | |||
} | |||
.codeLine-14BKbG > span > span .hljs-string { | |||
color: ${this.hljs.string}; | |||
} | |||
.codeLine-14BKbG > span > span .hljs-subst { | |||
color: ${this.hljs.subst}; | |||
} | |||
.codeLine-14BKbG > span > span .hljs-code { | |||
color: ${this.hljs.code}; | |||
} | |||
.codeLine-14BKbG > span > span .hljs-comment { | |||
color: ${this.hljs.comment}; | |||
} | |||
.codeLine-14BKbG > span > span .hljs-quote { | |||
color: ${this.hljs.quote}; | |||
} | |||
.codeLine-14BKbG > span > span .hljs-variable { | |||
color: ${this.hljs.variable}; | |||
} | |||
.codeLine-14BKbG > span > span .hljs-template-variable { | |||
color: ${this.hljs.template_variable}; | |||
} | |||
.codeLine-14BKbG > span > span .hljs-meta-string { | |||
color: ${this.hljs.meta_string}; | |||
} | |||
` | |||
} | |||
}; | |||
}; | |||
return plugin(Plugin, Api); | |||
})(global.ZeresPluginLibrary.buildPlugin(config)); | |||
})(); | |||
/*@end@*/ |
@ -0,0 +1,333 @@ | |||
/** | |||
* @name GameActivityToggle | |||
* @version 1.2.6 | |||
* @description Simple plugin that adds the \"display game activity\" setting | |||
* on the home toolbar so you can toggle it easier when you don't want your friends knowing how much you play video games. | |||
* | |||
* @authorLink https://github.com/Egrodo | |||
* @source https://github.com/Egrodo/DiscordPlugins/blob/master/GameActivityToggle.plugin.js | |||
*/ | |||
/*@cc_on | |||
@if (@_jscript) | |||
// Offer to self-install for clueless users that try to run this directly. | |||
var shell = WScript.CreateObject("WScript.Shell"); | |||
var fs = new ActiveXObject("Scripting.FileSystemObject"); | |||
var pathPlugins = shell.ExpandEnvironmentStrings("%APPDATA%\BetterDiscord\plugins"); | |||
var pathSelf = WScript.ScriptFullName; | |||
// Put the user at ease by addressing them in the first person | |||
shell.Popup("It looks like you've mistakenly tried to run me directly. \n(Don't do that!)", 0, "I'm a plugin for BetterDiscord", 0x30); | |||
if (fs.GetParentFolderName(pathSelf) === fs.GetAbsolutePathName(pathPlugins)) { | |||
shell.Popup("I'm in the correct folder already.", 0, "I'm already installed", 0x40); | |||
} else if (!fs.FolderExists(pathPlugins)) { | |||
shell.Popup("I can't find the BetterDiscord plugins folder.\nAre you sure it's even installed?", 0, "Can't install myself", 0x10); | |||
} else if (shell.Popup("Should I copy myself to BetterDiscord's plugins folder for you?", 0, "Do you need some help?", 0x34) === 6) { | |||
fs.CopyFile(pathSelf, fs.BuildPath(pathPlugins, fs.GetFileName(pathSelf)), true); | |||
// Show the user where to put plugins in the future | |||
shell.Exec("explorer " + pathPlugins); | |||
shell.Popup("I'm installed!", 0, "Successfully installed", 0x40); | |||
} | |||
WScript.Quit(); | |||
@else@*/ | |||
const enabledIcon = | |||
'<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 24 24" width="20" height="20" xml:space="preserve"><style type="text/css">.st0{fill:#B9BBBE;}</style><g><path class="st0" d="M20.8,7.7c-0.6-1.2-1.8-1.9-3.1-1.9H6.3C5,5.7,3.8,6.5,3.2,7.6l-2.8,5.8c0,0,0,0,0,0C-0.3,15.1,0.4,17,2,17.8L2.3,18C4,18.7,5.9,18,6.7,16.4l0.1-0.3c0.3-0.6,0.9-1,1.6-1h7.1c0.7,0,1.3,0.4,1.6,1l0.1,0.3c0.8,1.6,2.7,2.4,4.4,1.6l0.3-0.1c1.6-0.8,2.3-2.7,1.6-4.4L20.8,7.7z M8.6,10.5c0,0.2-0.2,0.4-0.4,0.4H7.3c-0.2,0-0.4,0.2-0.4,0.4v0.9c0,0.2-0.2,0.4-0.4,0.4H5.7c-0.2,0-0.4-0.2-0.4-0.4v-0.9c0-0.2-0.2-0.4-0.4-0.4c0,0,0,0,0,0H4.1c-0.2,0-0.4-0.2-0.4-0.4V9.7c0-0.2,0.2-0.4,0.4-0.4h0.9c0.2,0,0.4-0.2,0.4-0.4c0,0,0,0,0,0V8.1c0-0.2,0.2-0.4,0.4-0.4h0.8C6.8,7.7,7,7.9,7,8.1V9c0,0.2,0.2,0.4,0.4,0.4h0.9c0.2,0,0.3,0.2,0.3,0.4V10.5z M15.6,10.9c-0.4,0-0.8-0.3-0.8-0.8c0-0.4,0.3-0.8,0.8-0.8c0,0,0,0,0,0c0.4,0,0.8,0.3,0.8,0.8C16.4,10.5,16.1,10.9,15.6,10.9z M17.2,7.7C17.2,7.7,17.2,7.7,17.2,7.7c0.4,0,0.8,0.3,0.8,0.8c0,0,0,0,0,0c0,0.4-0.4,0.8-0.8,0.8c-0.4,0-0.8-0.4-0.8-0.8S16.8,7.7,17.2,7.7z M18,11.7L18,11.7C18,11.7,18,11.7,18,11.7c0,0.4-0.3,0.8-0.8,0.8c-0.4,0-0.8-0.3-0.8-0.8c0-0.4,0.3-0.8,0.8-0.8c0,0,0,0,0,0C17.7,10.9,18,11.3,18,11.7C18,11.7,18,11.7,18,11.7L18,11.7C18,11.7,18,11.7,18,11.7C18,11.7,18,11.7,18,11.7z M18.9,10.9c-0.4,0-0.8-0.3-0.8-0.8c0-0.4,0.3-0.8,0.8-0.8c0,0,0,0,0,0c0.4,0,0.8,0.3,0.8,0.8C19.6,10.5,19.3,10.9,18.9,10.9z"/><polygon points="19.3,11.2 19.3,11.2 19.3,11.2 "/><polygon points="19.3,11.2 19.3,11.2 19.3,11.2 "/></g></svg>'; | |||
const enabledIconHover = | |||
'<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 24 24" width="20" height="20" xml:space="preserve"><style type="text/css">.st0{fill:#dcddde;}</style><g><path class="st0" d="M20.8,7.7c-0.6-1.2-1.8-1.9-3.1-1.9H6.3C5,5.7,3.8,6.5,3.2,7.6l-2.8,5.8c0,0,0,0,0,0C-0.3,15.1,0.4,17,2,17.8L2.3,18C4,18.7,5.9,18,6.7,16.4l0.1-0.3c0.3-0.6,0.9-1,1.6-1h7.1c0.7,0,1.3,0.4,1.6,1l0.1,0.3c0.8,1.6,2.7,2.4,4.4,1.6l0.3-0.1c1.6-0.8,2.3-2.7,1.6-4.4L20.8,7.7z M8.6,10.5c0,0.2-0.2,0.4-0.4,0.4H7.3c-0.2,0-0.4,0.2-0.4,0.4v0.9c0,0.2-0.2,0.4-0.4,0.4H5.7c-0.2,0-0.4-0.2-0.4-0.4v-0.9c0-0.2-0.2-0.4-0.4-0.4c0,0,0,0,0,0H4.1c-0.2,0-0.4-0.2-0.4-0.4V9.7c0-0.2,0.2-0.4,0.4-0.4h0.9c0.2,0,0.4-0.2,0.4-0.4c0,0,0,0,0,0V8.1c0-0.2,0.2-0.4,0.4-0.4h0.8C6.8,7.7,7,7.9,7,8.1V9c0,0.2,0.2,0.4,0.4,0.4h0.9c0.2,0,0.3,0.2,0.3,0.4V10.5z M15.6,10.9c-0.4,0-0.8-0.3-0.8-0.8c0-0.4,0.3-0.8,0.8-0.8c0,0,0,0,0,0c0.4,0,0.8,0.3,0.8,0.8C16.4,10.5,16.1,10.9,15.6,10.9z M17.2,7.7C17.2,7.7,17.2,7.7,17.2,7.7c0.4,0,0.8,0.3,0.8,0.8c0,0,0,0,0,0c0,0.4-0.4,0.8-0.8,0.8c-0.4,0-0.8-0.4-0.8-0.8S16.8,7.7,17.2,7.7z M18,11.7L18,11.7C18,11.7,18,11.7,18,11.7c0,0.4-0.3,0.8-0.8,0.8c-0.4,0-0.8-0.3-0.8-0.8c0-0.4,0.3-0.8,0.8-0.8c0,0,0,0,0,0C17.7,10.9,18,11.3,18,11.7C18,11.7,18,11.7,18,11.7L18,11.7C18,11.7,18,11.7,18,11.7C18,11.7,18,11.7,18,11.7z M18.9,10.9c-0.4,0-0.8-0.3-0.8-0.8c0-0.4,0.3-0.8,0.8-0.8c0,0,0,0,0,0c0.4,0,0.8,0.3,0.8,0.8C19.6,10.5,19.3,10.9,18.9,10.9z"/><polygon points="19.3,11.2 19.3,11.2 19.3,11.2 "/><polygon points="19.3,11.2 19.3,11.2 19.3,11.2 "/></g></svg>'; | |||
const disabledIcon = | |||
'<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 24 24" width="20" height="20" xml:space="preserve"><style type="text/css">.st0{fill:#B9BBBE;}.st1{fill:#F04747;}</style><g><path class="st0" d="M17.7,5.7h-0.8L4.4,18.1c1-0.2,1.9-0.8,2.3-1.8l0.1-0.3c0.3-0.6,0.9-1,1.6-1h1.9l4.7-4.6v0c-0.1-0.1-0.1-0.2-0.1-0.4c0-0.4,0.3-0.8,0.8-0.8c0,0,0,0,0,0c0.1,0,0.2,0,0.3,0.1l0.5-0.5c-0.1-0.1-0.1-0.2-0.1-0.4c0-0.4,0.3-0.8,0.8-0.8c0.1,0,0.3,0,0.4,0.1l1.7-1.7C18.8,5.8,18.3,5.7,17.7,5.7z M23.5,13.4l-2.8-5.8c0,0,0-0.1-0.1-0.1l-1.8,1.8c0.4,0,0.7,0.4,0.7,0.8c0,0.4-0.3,0.8-0.8,0.8c-0.4,0-0.8-0.3-0.8-0.7l-0.8,0.8c0.4,0,0.7,0.4,0.7,0.8c0,0.4-0.4,0.8-0.8,0.8c-0.4,0-0.8-0.3-0.8-0.7L13.1,15h2.4c0.7,0,1.3,0.4,1.6,1l0.1,0.3c0.8,1.6,2.7,2.3,4.4,1.6l0.3-0.1C23.6,17,24.3,15,23.5,13.4z M6.3,5.7C5,5.7,3.8,6.4,3.3,7.6l-2.8,5.8c0,0,0,0,0,0C-0.3,15,0.4,16.9,2,17.7L14,5.7H6.3z M8.2,10.8H7.3c-0.2,0-0.4,0.2-0.4,0.3v0.9c0,0.2-0.2,0.3-0.3,0.3H5.7c-0.2,0-0.3-0.2-0.3-0.3v-0.9c0-0.2-0.2-0.3-0.4-0.3H4.1c-0.2,0-0.4-0.2-0.4-0.4V9.6c0-0.2,0.2-0.4,0.4-0.4H5c0.2,0,0.4-0.2,0.4-0.4V8c0-0.2,0.2-0.4,0.4-0.4h0.8C6.8,7.7,7,7.8,7,8v0.9c0,0.2,0.2,0.4,0.4,0.4h0.9c0.2,0,0.3,0.2,0.3,0.4v0.8C8.6,10.7,8.4,10.8,8.2,10.8z"/><polygon points="19.3,11.1 19.3,11.1 19.3,11.1 "/><polygon points="19.3,11.2 19.3,11.1 19.3,11.1 "/></g><polygon class="st1" points="22.6,2.7 22.6,2.8 19.3,6.1 16,9.3 16,9.4 15,10.4 15,10.4 10.3,15 2.8,22.5 1.4,21.1 21.2,1.3 "/></svg>'; | |||
const disabledIconHover = | |||
'<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 24 24" width="20" height="20" xml:space="preserve"><style type="text/css">.st0{fill:#dcddde;}.st1{fill:#F04747;}</style><g><path class="st0" d="M17.7,5.7h-0.8L4.4,18.1c1-0.2,1.9-0.8,2.3-1.8l0.1-0.3c0.3-0.6,0.9-1,1.6-1h1.9l4.7-4.6v0c-0.1-0.1-0.1-0.2-0.1-0.4c0-0.4,0.3-0.8,0.8-0.8c0,0,0,0,0,0c0.1,0,0.2,0,0.3,0.1l0.5-0.5c-0.1-0.1-0.1-0.2-0.1-0.4c0-0.4,0.3-0.8,0.8-0.8c0.1,0,0.3,0,0.4,0.1l1.7-1.7C18.8,5.8,18.3,5.7,17.7,5.7z M23.5,13.4l-2.8-5.8c0,0,0-0.1-0.1-0.1l-1.8,1.8c0.4,0,0.7,0.4,0.7,0.8c0,0.4-0.3,0.8-0.8,0.8c-0.4,0-0.8-0.3-0.8-0.7l-0.8,0.8c0.4,0,0.7,0.4,0.7,0.8c0,0.4-0.4,0.8-0.8,0.8c-0.4,0-0.8-0.3-0.8-0.7L13.1,15h2.4c0.7,0,1.3,0.4,1.6,1l0.1,0.3c0.8,1.6,2.7,2.3,4.4,1.6l0.3-0.1C23.6,17,24.3,15,23.5,13.4z M6.3,5.7C5,5.7,3.8,6.4,3.3,7.6l-2.8,5.8c0,0,0,0,0,0C-0.3,15,0.4,16.9,2,17.7L14,5.7H6.3z M8.2,10.8H7.3c-0.2,0-0.4,0.2-0.4,0.3v0.9c0,0.2-0.2,0.3-0.3,0.3H5.7c-0.2,0-0.3-0.2-0.3-0.3v-0.9c0-0.2-0.2-0.3-0.4-0.3H4.1c-0.2,0-0.4-0.2-0.4-0.4V9.6c0-0.2,0.2-0.4,0.4-0.4H5c0.2,0,0.4-0.2,0.4-0.4V8c0-0.2,0.2-0.4,0.4-0.4h0.8C6.8,7.7,7,7.8,7,8v0.9c0,0.2,0.2,0.4,0.4,0.4h0.9c0.2,0,0.3,0.2,0.3,0.4v0.8C8.6,10.7,8.4,10.8,8.2,10.8z"/><polygon points="19.3,11.1 19.3,11.1 19.3,11.1 "/><polygon points="19.3,11.2 19.3,11.1 19.3,11.1 "/></g><polygon class="st1" points="22.6,2.7 22.6,2.8 19.3,6.1 16,9.3 16,9.4 15,10.4 15,10.4 10.3,15 2.8,22.5 1.4,21.1 21.2,1.3 "/></svg>'; | |||
const micIconPath = `M6.7 11H5C5 12.19 5.34 13.3 5.9 14.28L7.13 13.05C6.86 12.43 6.7 11.74 6.7 11Z`; | |||
const muteIconPath = `M14.99 11C14.99 12.66 13.66 14 12 14C10.34 14 9 12.66 9 11V5C9 3.34 10.34 2 12 2C13.66 2 15 3.34 15 5L14.99 11ZM12 16.1C14.76 16.1 17.3 14 17.3 11H19C19 14.42 16.28 17.24 13 17.72V21H11V17.72C7.72 17.23 5 14.41 5 11H6.7C6.7 14 9.24 16.1 12 16.1ZM12 4C11.2 4 11 4.66667 11 5V11C11 11.3333 11.2 12 12 12C12.8 12 13 11.3333 13 11V5C13 4.66667 12.8 4 12 4Z`; | |||
class GameActivityToggle { | |||
btnReference = null; | |||
tooltipReference = null; | |||
soundReference = null; | |||
observer = null; | |||
soundToggled = true; | |||
gameActivity = true; | |||
constructor() { | |||
this.onToggle = this.onToggle.bind(this); | |||
this.onButtonMouseOut = this.onButtonMouseOut.bind(this); | |||
this.onButtonMouseOver = this.onButtonMouseOver.bind(this); | |||
this.checkForRemoval = this.checkForRemoval.bind(this); | |||
this.checkForChange = this.checkForChange.bind(this); | |||
} | |||
getName() { | |||
return "Game Activity Toggle"; | |||
} | |||
getDescription() { | |||
return 'Simple plugin that adds a "Display Game Activity" button on the main toolbar so you can toggle it easier.'; | |||
} | |||
getVersion() { | |||
return "1.2.6"; | |||
} | |||
getAuthor() { | |||
return "egrodo"; | |||
} | |||
load() { | |||
// Not required, but if the user has ZLibrary installed then support auto update. | |||
if (window.ZLibrary) { | |||
ZLibrary.PluginUpdater.checkForUpdate( | |||
this.getName(), | |||
this.getVersion(), | |||
"https://raw.githubusercontent.com/Egrodo/DiscordPlugins/master/GameActivityToggle.plugin.js" | |||
); | |||
} | |||
} | |||
start() { | |||
// On start check what game activity is currently set to. | |||
this.gameActivity = BdApi.findModuleByProps("guildPositions").showCurrentGame; | |||
this.soundReference = BdApi.findModuleByProps("playSound"); | |||
// Check if there's a sound setting saved | |||
// For some reason BdApi interprets boolean false's as undefined, so we're storing the toggle as a string and converting it. | |||
const savedSoundSetting = BdApi.loadData(this.getName(), "soundToggled"); | |||
if (!savedSoundSetting) { | |||
this.soundToggled = true; | |||
} else if (savedSoundSetting === "true") { | |||
this.savedSoundSetting = true; | |||
} else if (savedSoundSetting === "false") { | |||
this.savedSoundSetting = false; | |||
} else { | |||
console.error( | |||
`Game Activity Toggle Error: soundToggle data somehow corrupted, not true/false: ${savedSoundSetting}` | |||
); | |||
this.savedSoundSetting = true; | |||
BdApi.saveData(this.getName(), "soundToggled", "true"); | |||
} | |||
// Create our DOM elements | |||
this.createButton(); | |||
this.createTooltip(); | |||
// Watch for change | |||
BdApi.findModuleByProps("guildPositions").addChangeListener(this.checkForChange); | |||
} | |||
createButton() { | |||
// Use flexMarginReset prop to find the selector for the taskbar row. | |||
const selector = (BdApi.findModuleByProps("flexMarginReset", "flex").flex || "").split(" ")[0]; | |||
if (!selector) { | |||
console.error("GameActivityToggle failed to start up: Selector not found?"); | |||
return; | |||
} | |||
// If there are multiple elements found with this selector then the user is most likely in a call. Use the appropriate one | |||
const rows = document.querySelectorAll(`.${selector}`); | |||
let row; | |||
if (rows.length) { | |||
// Find the correct row by looking for one who's DOM structure matches what we expect | |||
for (let i = 0; i < rows.length; ++i) { | |||
try { | |||
if ( | |||
rows[i].firstElementChild.firstElementChild.firstElementChild.firstElementChild.getAttribute("d") === | |||
muteIconPath || | |||
rows[i].firstElementChild.firstElementChild.firstElementChild.firstElementChild.getAttribute("d") === | |||
micIconPath | |||
) { | |||
row = rows[i]; | |||
break; | |||
} | |||
} catch (err) { | |||
// If the above firstELementChild accessing fails assume it's not the correct row and continue | |||
continue; | |||
} | |||
} | |||
} else { | |||
row = rows[0]; | |||
} | |||
if (!row) { | |||
console.log(rows); | |||
throw new Error("Could not find correct row?"); | |||
} | |||
this.btnReference = row.firstElementChild.cloneNode(true); | |||
this.btnReference.firstElementChild.innerHTML = this.gameActivity ? enabledIcon : disabledIcon; | |||
this.btnReference.firstElementChild.style.pointerEvents = "none"; // Ignore pointer events to fix bug that was causing repeated clicks to be ignored. | |||
this.btnReference.id = "GameActivityToggleBtn"; | |||
this.btnReference.setAttribute("aria-label", "Toggle Game Activity"); | |||
this.btnReference.setAttribute("aria-checked", `${this.gameActivity ? "true" : "false"}`); | |||
this.btnReference.addEventListener("click", this.onToggle); | |||
this.btnReference.addEventListener("mouseenter", this.onButtonMouseOver); | |||
this.btnReference.addEventListener("mouseleave", this.onButtonMouseOut); | |||
row.prepend(this.btnReference); | |||
// Observe changes on the row to watch for our element being overwritten. | |||
if (!this.observer) { | |||
this.observer = new MutationObserver(this.checkForRemoval); | |||
this.observer.observe(row, { | |||
attributes: false, | |||
childList: true, | |||
subtree: false, | |||
}); | |||
} | |||
} | |||
createTooltip() { | |||
// Also setup my recreated tooltip that uses Discord's classes. | |||
const tooltipClasses = BdApi.findModuleByProps("tooltipBottom"); | |||
const wrapperDiv = document.createElement("div"); | |||
this.tooltipReference = wrapperDiv; | |||
wrapperDiv.style.visibility = "hidden"; | |||
wrapperDiv.style.position = "absolute"; | |||
// wrapperDiv.style.zIndex = "1003"; | |||
wrapperDiv.className = [ | |||
tooltipClasses.tooltip, | |||
tooltipClasses.tooltipTop, | |||
tooltipClasses.tooltipBlack, | |||
tooltipClasses.tooltipDisablePointerEvents, | |||
].join(" "); | |||
const textWrapper = document.createElement("div"); | |||
textWrapper.className = tooltipClasses.tooltipContent; | |||
textWrapper.innerText = `Turn ${this.gameActivity ? "off" : "on"} game activity`; | |||
const bottomArrow = document.createElement("div"); | |||
bottomArrow.className = tooltipClasses.tooltipPointer; | |||
wrapperDiv.appendChild(textWrapper); | |||
wrapperDiv.appendChild(bottomArrow); | |||
document.body.appendChild(wrapperDiv); | |||
} | |||
onToggle() { | |||
this.gameActivity = !this.gameActivity; | |||
BdApi.findModuleByProps("updateRemoteSettings").updateLocalSettings({ | |||
showCurrentGame: this.gameActivity, | |||
}); | |||
BdApi.findModuleByProps("updateRemoteSettings").updateRemoteSettings({ | |||
showCurrentGame: this.gameActivity, | |||
}); | |||
this.btnReference.firstElementChild.innerHTML = this.gameActivity ? enabledIcon : disabledIcon; | |||
// In order to preserve the tooltipPointer but also change the message we have to do this | |||
const innerTooltipHTML = this.tooltipReference.firstElementChild.innerHTML.split("Turn"); | |||
this.tooltipReference.firstElementChild.innerHTML = `${innerTooltipHTML[0]} Turn ${ | |||
this.gameActivity ? "off" : "on" | |||
} game activity`; | |||
this.btnReference.setAttribute("aria-checked", `${this.gameActivity ? "true" : "false"}`); | |||
// If enabled, play the mute / unmute sound on toggle. | |||
if (this.soundToggled) { | |||
if (this.gameActivity) { | |||
this.soundReference.playSound("unmute", 0.4); | |||
} else this.soundReference.playSound("mute", 0.4); | |||
} | |||
} | |||
// On mouse over swap icons to highlight and display tooltip in correct position. | |||
onButtonMouseOver({ target }) { | |||
this.btnReference.firstElementChild.innerHTML = this.gameActivity ? enabledIconHover : disabledIconHover; | |||
const { x, y } = target.getBoundingClientRect(); | |||
const tooltipXPos = x + target.clientWidth / 2 - this.tooltipReference.offsetWidth / 2; | |||
const tooltipYPos = y - target.clientHeight - 8; // 8 being a constant amount of space to hover above the btn. | |||
this.tooltipReference.style.left = `${tooltipXPos}px`; | |||
this.tooltipReference.style.visibility = "visible"; | |||
this.tooltipReference.style.top = `${tooltipYPos}px`; | |||
this.tooltipReference.visibility = "visible"; | |||
} | |||
onButtonMouseOut() { | |||
this.btnReference.firstElementChild.innerHTML = this.gameActivity ? enabledIcon : disabledIcon; | |||
this.tooltipReference.style.visibility = "hidden"; | |||
} | |||
// Certain UI actions can result in the row being re-rendered and the button removed. Watch the row and re-add the button when necessary. | |||
checkForRemoval() { | |||
if (!document.getElementById("GameActivityToggleBtn")) { | |||
this.createButton(); | |||
} | |||
} | |||
// We need to check for the user toggling game activity in the actual settings menu as well, because we have no event for that. | |||
checkForChange() { | |||
if (this.gameActivity !== BdApi.findModuleByProps("guildPositions").showCurrentGame) { | |||
this.gameActivity = BdApi.findModuleByProps("guildPositions").showCurrentGame; | |||
this.btnReference.firstElementChild.innerHTML = this.gameActivity ? enabledIcon : disabledIcon; | |||
const innerTooltipHTML = this.tooltipReference.firstElementChild.innerHTML.split("Turn"); | |||
this.tooltipReference.firstElementChild.innerHTML = `${innerTooltipHTML[0]} Turn ${ | |||
this.gameActivity ? "off" : "on" | |||
} game activity`; | |||
this.btnReference.setAttribute("aria-checked", `${this.gameActivity ? "true" : "false"}`); | |||
this.btnReference.addEventListener("click", this.onToggle); | |||
} | |||
} | |||
// Create settings panel that allows users to disable the alert sound | |||
getSettingsPanel() { | |||
// Create wrapper row | |||
const wrapper = document.createElement("div"); | |||
wrapper.className = "ui-flex flex-vertical flex-justify-start flex-align-stretch flex-nowrap ui-switch-item"; | |||
wrapper.style.marginTop = "1.5rem"; | |||
const titleContainer = document.createElement("div"); | |||
titleContainer.className = 'class="ui-flex flex-horizontal flex-justify-start flex-align-stretch flex-nowrap"'; | |||
titleContainer.style.display = "flex"; | |||
const title = document.createElement("h3"); | |||
title.innerText = "Alert Sound"; | |||
title.className = "ui-form-title h3 margin-reset margin-reset ui-flex-child"; | |||
titleContainer.appendChild(title); | |||
// Create switch | |||
const button = document.createElement("div"); | |||
button.classList.add("bd-switch", ...(this.soundToggled ? ["bd-switch-checked"] : [])); | |||
const input = document.createElement("input"); | |||
input.type = "checkbox"; | |||
input.className = "bd-checkbox"; | |||
button.appendChild(input); | |||
button.style.float = "right"; | |||
input.onclick = () => { | |||
this.soundToggled = !this.soundToggled; | |||
BdApi.saveData(this.getName(), "soundToggled", this.soundToggled.toString()); | |||
button.classList.remove(...(this.soundToggled ? [] : ["bd-switch-checked"])); | |||
button.classList.add(...(this.soundToggled ? ["bd-switch-checked"] : [])); | |||
}; | |||
// Create description box and append button to it | |||
const description = document.createElement("div"); | |||
description.className = "ui-form-text style-description margin-top-4"; | |||
description.innerText = "Toggle the alert sound that plays on button click"; | |||
description.style.borderBottom = "none"; | |||
description.appendChild(button); | |||
wrapper.appendChild(titleContainer); | |||
wrapper.appendChild(description); | |||
return wrapper; | |||
} | |||
stop() { | |||
this.observer.disconnect(); | |||
this.btnReference.removeEventListener("mouseenter", this.onButtonMouseOver); | |||
this.btnReference.removeEventListener("mouseleave", this.onButtonMouseOut); | |||
this.btnReference.parentNode.removeChild(this.btnReference); | |||
this.tooltipReference.parentNode.removeChild(this.tooltipReference); | |||
} | |||
} |
@ -0,0 +1,6 @@ | |||
{ | |||
"currentVersionInfo": { | |||
"version": "1.2.29", | |||
"hasShownChangelog": true | |||
} | |||
} |
@ -0,0 +1,114 @@ | |||
/** | |||
* @name ClearVision | |||
* @author ClearVision Team | |||
* @version 6 | |||
* @description Highly customizable theme for BetterDiscord. | |||
* @source https://github.com/ClearVision/ClearVision-v6 | |||
* @website https://clearvision.gitlab.io | |||
* @invite 2fKpjAR | |||
*/ | |||
/* Credits to Zerthox for making the original theme. */ | |||
/* IMPORT CSS */ | |||
@import url(https://clearvision.gitlab.io/v6/main.css); | |||
/* SETTINGS */ | |||
:root { | |||
/* ACCENT COLORS */ | |||
--main-color: #2780e6; /* main accent color (hex, rgb or hsl) [default: #2780e6] */ | |||
--hover-color: #1e63b3; /* hover accent color (hex, rgb or hsl) [default: #1e63b3] */ | |||
--success-color: #43b581; /* success accent color (hex, rgb or hsl) [default: #43b581] */ | |||
--danger-color: #982929; /* danger accent color (hex, rgb or hsl) [default: #982929] */ | |||
/* Customization Patch */ | |||
--channel-unread: var(--main-color); /* Unread Server channel color. [default: var(--main-color)] THIS OVERRIDES YOUR MAIN COLOR*/ | |||
--channel-color: rgba(255,255,255,0.3); /*Read Server channel color [default: rgba(255,255,255,0.3);]*/ | |||
--muted-color: rgba(255,255,255,0.1); /*Muted channel color [default: rgba(255,255,255,0.1);]*/ | |||
--url-color: var(--main-color); /*The color of url links [default: var(--main-color)]*/ | |||
/* STATUS COLORS */ | |||
--online-color: #43b581; /* online status color (hex, rgb or hsl) [default: #43b581] */ | |||
--idle-color: #faa61a; /* idle status color (hex, rgb or hsl) [default: #faa61a] */ | |||
--dnd-color: #982929; /* dnd status color (hex, rgb or hsl) [default: #982929] */ | |||
--streaming-color: #593695; /* streaming status color (hex, rgb or hsl) [default: #593695] */ | |||
--offline-color: #808080; /* offline/invisible status color (hex, rgb or hsl) [default: #808080] */ | |||
/* GENERAL */ | |||
--main-font: Whitney, Helvetica Neue, Helvetica, Arial, sans-serif; /* main font for app (font must be installed) [default: Whitney, Helvetica Neue, Helvetica, Arial, sans-serif] */ | |||
--code-font: Consolas, Liberation Mono, Menlo, Courier, monospace; /* font for codeblocks (font must be installed) [default: Consolas, Liberation Mono, Menlo, Courier, monospace] */ | |||
--server-columns: 1; /* amount of server list columns [default: 1] */ | |||
--channels-width: 220px; /* channel list width (240px for Discord default) [default: 220px] */ | |||
--members-width: 240px; /* member list width [default: 240px] */ | |||
--avatar-roundness: 50%; /* avatar roundness (0% for squares, 50% for circles) [default: 50%] */ | |||
/* APP BACKGROUND */ | |||
--background-shading: 100%; /* app background shading (0 for complete smoothness) [default: 100%] */ | |||
--background-overlay: rgba(0, 0, 0, 0.6); /* app background overlay color/gradient [default: rgba(0, 0, 0, 0.6)] */ | |||
--background-image: url(https://clearvision.gitlab.io/images/sapphire.jpg); /* app background image (link must be HTTPS) [default: url(https://clearvision.gitlab.io/images/sapphire.jpg)]*/ | |||
--background-position: center; /* app background position [default: center] */ | |||
--background-size: cover; /* app background size [default: cover] */ | |||
--background-repeat: no-repeat; /* app background repeat [default: no-repeat] */ | |||
--background-attachment: fixed; /* app background attachment [default: fixed] */ | |||
--background-brightness: 100%; /* app background brightness (< 100% for darken, > 100% for lighten) [default: 100%] */ | |||
--background-contrast: 100%; /* app background contrast [default: 100%] */ | |||
--background-saturation: 100%; /* app background saturation [default: 100%] */ | |||
--background-invert: 0%; /* app background invert (0 - 100%) [default: 0%] */ | |||
--background-grayscale: 0%; /* app background grayscale ( 0 - 100%) [default: 0%] */ | |||
--background-sepia: 0%; /* app background sepia (0 - 100%) [default: 0%] */ | |||
--background-blur: 0px; /* app background blur [default: 0px] */ | |||
/* HOME BUTTON ICON */ | |||
--home-icon: url(https://clearvision.gitlab.io/icons/discord.svg); /* home button icon (link must be HTTPS) [default: url(https://clearvision.gitlab.io/icons/discord.svg)]*/ | |||
--home-position: center; /* home button icon position [default: center] */ | |||
--home-size: 40px; /* home button icon size [default: 40px] */ | |||
/* MODAL BACKDROP */ | |||
--backdrop-overlay: rgba(0, 0, 0, 0.8); /* modal backdrop overlay color/gradient [default: rgba(0, 0, 0, 0.8)] */ | |||
--backdrop-image: var(--background-image); /* modal backdrop image (link must be HTTPS) [default: var(---background-image)] */ | |||
--backdrop-position: var(--background-position); /* modal backdrop position [default: var(---background-position)] */ | |||
--backdrop-size: var(--background-size); /* modal backdrop size [default: var(---background-size)] */ | |||
--backdrop-repeat: var(--background-repeat); /* modal backdrop repeat [default: var(---background-repeat)] */ | |||
--backdrop-attachment: var(--background-attachment); /* modal backdrop attachment [default: var(--background-attachment)] */ | |||
--backdrop-brightness: var(--background-brightness); /* modal backdrop brightness (< 100% for darken, > 100% for lighten) [default: var(---background-brightness)] */ | |||
--backdrop-contrast: var(--background-contrast); /* modal backdrop contrast [default: var(---background-contrast)] */ | |||
--backdrop-saturation: var(--background-saturation); /* modal backdrop saturation [default: var(---background-saturation)] */ | |||
--backdrop-invert: var(--background-invert); /* modal backdrop invert (0 - 100%) [default: var(---background-invert)] */ | |||
--backdrop-grayscale: var(--background-grayscale); /* modal backdrop grayscale ( 0 - 100%) [default: var(---background-grayscale)] */ | |||
--backdrop-sepia: var(--background-sepia); /* modal backdrop sepia (0 - 100%) [default: var(---background-sepia)] */ | |||
--backdrop-blur: var(--background-blur); /* modal backdrop blur [default: var(--background-blur)] */ | |||
/* USER POPOUT BACKGROUND */ | |||
--user-popout-image: var(--background-image); /* app background image (link must be HTTPS) [default: var(---background-image)] */ | |||
--user-popout-position: var(--background-position); /* user popout background position [default: var(---background-position)] */ | |||
--user-popout-size: var(--background-size); /* user popout background size [default: var(---background-size)] */ | |||
--user-popout-repeat: var(--background-repeat); /* user popout background repeat [default: var(---background-repeat)] */ | |||
--user-popout-attachment: var(--background-attachment); /* user popout background attachment [default: var(--background-attachment)] */ | |||
--user-popout-brightness: var(--background-brightness); /* user popout background brightness (< 100% for darken, > 100% for lighten) [default: var(---background-brightness)] */ | |||
--user-popout-contrast: var(--background-contrast); /* user popout background contrast [default: var(---background-contrast)] */ | |||
--user-popout-saturation: var(--background-saturation); /* user popout background saturation [default: var(---background-saturation)] */ | |||
--user-popout-invert: var(--background-invert); /* user popout background invert (0 - 100%) [default: var(---background-invert)] */ | |||
--user-popout-grayscale: var(--background-grayscale); /* user popout background grayscale (0 - 100%) [default: var(---background-grayscale)] */ | |||
--user-popout-sepia: var(--background-sepia); /* user popout background sepia (0 - 100%) [default: var(---background-sepia)] */ | |||
--user-popout-blur: calc(var(--background-blur) + 3px); /* user popout background blur [default: calc(var(--background-blur) + 3px)] */ | |||
/* USER MODAL BACKGROUND */ | |||
--user-modal-image: var(--background-image); /* app background image (link must be HTTPS) [default: var(---background-image)] */ | |||
--user-modal-position: var(--background-position); /* user modal background position [default: var(---background-position)] */ | |||
--user-modal-size: var(--background-size); /* user modal background size [default: var(---background-size)] */ | |||
--user-modal-repeat: var(--background-repeat); /* user modal background repeat [default: var(---background-repeat)] */ | |||
--user-modal-attachment: var(--background-attachment); /* user modal background attachment [default: var(--background-attachment)] */ | |||
--user-modal-brightness: var(--background-brightness); /* user modal background brightness (< 100% for darken, > 100% for lighten) [default: var(---background-brightness)] */ | |||
--user-modal-contrast: var(--background-contrast); /* user modal background contrast [default: var(---background-contrast)] */ | |||
--user-modal-saturation: var(--background-saturation); /* user modal background saturation [default: var(---background-saturation)] */ | |||
--user-modal-invert: var(--background-invert); /* user modal background invert (0 - 100%) [default: var(---background-invert)] */ | |||
--user-modal-grayscale: var(--background-grayscale); /* user modal background grayscale (0 - 100%) [default: var(---background-grayscale)] */ | |||
--user-modal-sepia: var(--background-sepia); /* user modal background sepia (0 - 100%) [default: var(---background-sepia)] */ | |||
--user-modal-blur: calc(var(--background-blur) + 3px); /* user modal background blur [default: calc(var(--background-blur) + 3px)] */ | |||
/* THEME BD COLORS */ | |||
--bd-blue: var(--main-color); /* betterdiscord main color [default: var(--main-color)] */ | |||
--bd-blue-hover: var(--hover-color); /* betterdiscord hover color [default: var(--hover-color)] */ | |||
--bd-blue-active: var(--hover-color); /* betterdiscord active color [default: var(--hover-color)] */ | |||
} |
@ -0,0 +1,14 @@ | |||
/** | |||
* @name Dark Discord | |||
* @version 1.0 | |||
* @description An actual dark mode for discord. | |||
* @source https://github.com/zzzmario/dark-discord | |||
* @authorId 583062692596547585 | |||
* @author mario | |||
* @donate https://paypal.me/zzzmario | |||
* @invite 7JceW7S | |||
**/ | |||
@import url(https://zzzmario.github.io/dark-discord/src/source.css); |
@ -0,0 +1,18 @@ | |||
import os | |||
import sys | |||
from ranger.core.loader import CommandLoader | |||
from ranger.api.commands import Command | |||
class chrome_choose(Command): | |||
def execute(self): | |||
""" Extract copied files to current directory """ | |||
filename = self.arg(1) | |||
cwd = self.fm.thisdir | |||
if filename == '.': | |||
with open('/tmp/ranger-chrome-choosed', 'w') as f: | |||
f.write(cwd.path + '/') | |||
sys.exit(0) | |||
with open('/tmp/ranger-chrome-choosed', 'w') as f: | |||
f.write(os.path.join(cwd.path, filename)) | |||
sys.exit(0) |
@ -0,0 +1,36 @@ | |||
#!/bin/sh | |||
for ((i=1;i<=$#;i++)); | |||
do | |||
if [ ${!i} = "--getsavefilename" ] | |||
then ((i++)) | |||
filename=${!i}; | |||
fi | |||
if [ ${!i} = "--version" ] | |||
then | |||
echo "someversion" | |||
exit | |||
fi | |||
done; | |||
path=$( echo ${filename%/*} ) | |||
file=$( echo ${filename##/*/} ) | |||
rm /tmp/ranger-chrome-choosed | |||
st -c ranger -n ranger -e "ranger" -t file-chooser-ranger | |||
selected=$(cat /tmp/ranger-chrome-choosed 2> /dev/null) | |||
if [ ! $? = 0 ] | |||
then | |||
exit 1 | |||
fi | |||
if [ -d $selected ] | |||
then | |||
echo "$selected$file" | |||
else | |||
echo $selected | |||
fi | |||
@ -0,0 +1,3 @@ | |||
#!/bin/sh | |||
export XDG_CURRENT_DESKTOP=KDE | |||
brave $@ |
@ -0,0 +1,279 @@ | |||
#include <X11/keysym.h> | |||
#include <X11/XKBlib.h> | |||
#include "normalMode.h" | |||
#include "utils.h" | |||
extern Glyph const styleSearch, style[]; | |||
extern char const wDelS[], wDelL[], *nmKeys[]; | |||
extern unsigned int bg[], fg, currentBg, highlightBg, highlightFg, amountNmKeys; | |||
typedef struct { int p[3]; } Pos; | |||
struct NormalModeState { | |||
struct OperationState { | |||
enum Op {noop=0, visual='v', visualLine='V', yank = 'y'} op; | |||
enum Infix {infix_none=0, infix_i='i', infix_a='a'} infix; | |||
} cmd; | |||
struct MotionState { | |||
uint32_t c; int active; Pos searchPos; | |||
enum Search {none=0, fw='/', bw='?'} search; | |||
} m; | |||
} defaultNormalMode, state; | |||
DynamicArray searchStr=UTF8_ARRAY, cCmd=UTF8_ARRAY, lCmd=UTF8_ARRAY; | |||
Glyph styleCmd; | |||
char posBuffer[10], brack[6][2] = { {"()"}, {"<>"}, {"{}"}, {"[]"}, {"\"\""}, {"''"}}; | |||
int exited=1, overlay=1; | |||
static inline uint32_t cchar() { return term.line[term.c.y][term.c.x].u; } | |||
static inline int pos(int p, int h) {return IS_SET(MODE_ALTSCREEN)?p:rangeY(p+h*histOff-insertOff);} | |||
static inline int contains(char c, char const * values, uint32_t memSize) { | |||
for (uint32_t i = 0; i < memSize; ++i) if (c == values[i]) return 1; | |||
return 0; | |||
} | |||
static inline void decodeTo(char const *cs, int len, DynamicArray *darr) { | |||
char *var = expand(darr); | |||
if (!var) empty(darr); else utf8decode(cs, (Rune*)(var), len); | |||
} | |||
static inline void applyPos(Pos p) { | |||
term.c.x = p.p[0], term.c.y = p.p[1]; | |||
if (!IS_SET(MODE_ALTSCREEN) && histOp) term.line = &buf[histOff = p.p[2]]; | |||
} | |||
/// Find string in history buffer, and provide string-match-lookup for highlighting matches | |||
static int highlighted(int x, int y) { | |||
int const s=term.row*term.col, i=y*term.col+x, sz=size(&searchStr); | |||
return sz && i<s && mark[i]!=sz && i+mark[i]<s && !mark[i+mark[i]]; | |||
} | |||
static void markSearchMatches(int all) { | |||
int sz = size(&searchStr), ox = 0, oy = 0, oi=0; | |||
for (int y=0; sz && all && y<term.row; ++y) | |||
for (int x=0; x<term.col; ++x) term.dirty[y] |= highlighted(x, y); | |||
for (int y = 0, wi=0, owi=0, i=0; sz && y < term.row; ++y) | |||
for (int x=0; x<term.col; ++x, wi%=sz, ++i, owi=wi) | |||
if (all || term.dirty[y]) { | |||
mark[i]=sz-(wi=(getU32(&searchStr,wi,1)==term.line[y][x].u?wi+1:0)); | |||
if (wi==1) ox=x, oy=y, oi=i; else if (!wi && owi) x=ox, y=oy, i=oi; | |||
} | |||
for (int y=0; sz &&all &&y<term.row; ++y) | |||
for (int x=0; x<term.col; ++x) term.dirty[y] |= highlighted(x, y); | |||
} | |||
static int findString(int8_t s, int all) { | |||
Pos p = (Pos) {.p={term.c.x, term.c.y, IS_SET(MODE_ALTSCREEN) ? 0 : histOff}}; | |||
historyMove(s, 0, 0); | |||
uint32_t strSz=size(&searchStr), maxIter=rows()*term.col+strSz, widx=0; | |||
for (uint32_t i=0, wi = 0; widx<strSz && ++i<=maxIter; historyMove(s, 0, 0), wi=widx) { | |||
widx = (getU32(&searchStr, widx, s>0)==cchar())?widx+1:0; | |||
if (wi && !widx) historyMove(-s*wi, 0, 0); | |||
} | |||
if (widx == strSz && widx) historyMove(-s * strSz, 0, 0); | |||
else applyPos(p); | |||
markSearchMatches(all); | |||
return widx == strSz; | |||
} | |||
/// Execute series of normal-mode commands from char array / decoded from dynamic array | |||
static ExitState pressKeys(char const* s, size_t e) { | |||
ExitState x=succ; | |||
for (size_t i=0; i<e && (x=(!s[i] ? x : kpressHist(&s[i], 1, 0, NULL))); ++i); | |||
return x; | |||
} | |||
static ExitState executeCommand(uint32_t *c, size_t z) { | |||
ExitState x=succ; | |||
char dc [32]; | |||
for (size_t i=0; i<z && (x=kpressHist(dc,utf8encode(c[i],dc),0,NULL));++i); | |||
return x; | |||
} | |||
/// Get character for overlay, if the overlay (st) has something to show, else normal char. | |||
static void getChar(DynamicArray *st, Glyph *glyphChange, int y, int xEnd, int width, int x) { | |||
if (x < xEnd - min(width=min(width,xEnd), size(st))) *glyphChange = term.line[y][x]; | |||
else if (x<xEnd) glyphChange->u = *((Rune*)(st->content + (size(st)+x-xEnd)*st->elSize)); | |||
} | |||
/// Expand "infix" expression: for instance (w =>) l b | | v e | | y | |||
static ExitState expandExpression(char c) { // ({ =>) l ? { \n | l | v / } \n | h | y | |||
int a=state.cmd.infix==infix_a, yank=state.cmd.op=='y', lc=tolower(c), found=1; | |||
state.cmd.infix = infix_none; | |||
if(!yank && state.cmd.op!=visual && state.cmd.op!=visualLine) return failed; | |||
char mot[11] = {'l', 0, 'b', 0, 0, 'v', 0, 'e', 0, 0, yank ? 'y' : 0}; | |||
if (lc == 'w') mot[2] = 'b' - lc + c, mot[7] = (a ? 'w' : 'e') - lc + c, mot[9]=a?'h':0; | |||
else { | |||
mot[1]='?', mot[3]=mot[8]='\n', mot[6]='/', mot[4]=a?0:'l', mot[9]=a?0:'h'; | |||
for (int i=found=0; !found && i < 6; ++i) | |||
if ((found=contains(c,brack[i],2))) mot[2]=brack[i][0], mot[7]=brack[i][1]; | |||
} | |||
if (!found) return failed; | |||
assign(&lCmd, &cCmd); | |||
empty(&cCmd); | |||
state.cmd = defaultNormalMode.cmd; | |||
return pressKeys(mot, 11); | |||
} | |||
int executeMotion(char const cs, int len, KeySym const *const ks) { | |||
state.m.c = max(state.m.c, 1); | |||
if (ks && *ks == XK_d) historyMove(0, 0, term.row / 2); | |||
else if (ks && *ks == XK_u) historyMove(0, 0, -term.row / 2); | |||
else if (ks && *ks == XK_f) historyMove(0, 0, term.row-1+(term.c.y=0)); | |||
else if (ks && *ks == XK_b) historyMove(0, 0, -(term.c.y=term.row-1)); | |||
else if (ks && *ks == XK_h) overlay = !overlay; | |||
else if (!len) return failed; | |||
else if (cs == 'K') historyMove(0, 0, -state.m.c); | |||
else if (cs == 'J') historyMove(0, 0, state.m.c); | |||
else if (cs == 'k') historyMove(0, -state.m.c, 0); | |||
else if (cs == 'j') historyMove(0, state.m.c, 0); | |||
else if (cs == 'h') historyMove(-state.m.c, 0, 0); | |||
else if (cs == 'l') historyMove( state.m.c, 0, 0); | |||
else if (cs == 'H') term.c.y = 0; | |||
else if (cs == 'M') term.c.y = term.bot / 2; | |||
else if (cs == 'L') term.c.y = term.bot; | |||
else if (cs == 's' || cs == 'S') altToggle = cs == 's' ? !altToggle : 1; | |||
else if (cs == 'G' || cs == 'g') { | |||
if (cs == 'G') term.c = c[0] = c[IS_SET(MODE_ALTSCREEN)+1]; | |||
if (!IS_SET(MODE_ALTSCREEN)) term.line = &buf[histOff=insertOff]; | |||
} else if (cs == '0') term.c.x = 0; | |||
else if (cs == '$') term.c.x = term.col-1; | |||
else if (cs == 't') sel.type = sel.type==SEL_REGULAR ? SEL_RECTANGULAR : SEL_REGULAR; | |||
else if (cs == 'n' || cs == 'N') { | |||
int const d = ((cs=='N')!=(state.m.search==bw))?-1:1; | |||
for (int i = state.m.c; i && findString(d, 0); --i); | |||
} else if (contains(cs, "wWeEbB", 6)) { | |||
int const low=cs<=90, off=tolower(cs)!='w', sgn=(tolower(cs)=='b')?-1:1, | |||
l=strlen(wDelL), s=strlen(wDelS), mit=rows()*term.col; | |||
for (int it=0, on=0; state.m.c > 0; ++it) { | |||
if (off || it) if (!historyMove(sgn, 0, 0)) it = mit; //< offset move | |||
int n = 1<<(contains(cchar(),wDelS,s) ?(2-low) :!contains(cchar(),wDelL,l)), | |||
found = (on|=n)^n && ((off ?on^n :n)!=1); //< state change &letter state | |||
if (found && off) historyMove(-sgn, 0, 0); //< offset move if required | |||
if (found || it>mit) it=-1, on=0, --state.m.c; //< terminate iteration | |||
} | |||
} else return failed; | |||
state.m.c = 0; | |||
return state.cmd.op == yank ? exitMotion : succ; | |||
} | |||
ExitState kpressHist(char const *cs, int len, int ctrl, KeySym const *ksym) { | |||
historyOpToggle(1, 1); | |||
int const prevYOff=IS_SET(MODE_ALTSCREEN)?0:histOff, search=state.m.search&&state.m.active, | |||
prevAltToggle=altToggle, prevOverlay=overlay; | |||
int const noOp=!state.cmd.op&&!state.cmd.infix, num=len==1&&BETWEEN(cs[0],48,57), | |||
esc=ksym&&*ksym==XK_Escape, ret=(ksym&&*ksym==XK_Return)||(len==1&&cs[0]=='\n'), | |||
quantifier=num&&(cs[0]!='0'||state.m.c), ins=!search &&noOp &&len &&cs[0]=='i'; | |||
exited = 0; | |||
ExitState result = succ; | |||
if (esc || ret || ins) { result = exitMotion, len = 0; | |||
} else if (ksym && *ksym == XK_BackSpace) { | |||
if ((search || state.m.c) && size(&cCmd)) pop(&cCmd); | |||
if (search) { | |||
if (size(&searchStr)) pop(&searchStr); | |||
else result = exitMotion; | |||
if (!size(&searchStr)) tfulldirt(); | |||
applyPos(state.m.searchPos); | |||
findString(state.m.search==fw ? 1 : -1, 1); | |||
} else if (state.m.c) state.m.c /= 10; | |||
len = 0; | |||
} else if (search) { | |||
if (len >= 1) decodeTo(cs, len, &searchStr); | |||
applyPos(state.m.searchPos); | |||
findString(state.m.search==fw ? 1 : -1, 1); | |||
} else if (len == 0) { result = failed; | |||
} else if (quantifier) { state.m.c = min(SHRT_MAX, state.m.c*10+cs[0]-48); | |||
} else if (state.cmd.infix && state.cmd.op && (result = expandExpression(cs[0]), len=0)) { | |||
} else if (cs[0] == '.') { | |||
if (size(&cCmd)) assign(&lCmd, &cCmd); | |||
empty(&cCmd); | |||
executeCommand((uint32_t*) lCmd.content, size(&lCmd)); | |||
empty(&cCmd); | |||
len = 0; | |||
} else if (cs[0] == 'r') { tfulldirt(); | |||
} else if (cs[0] == 'c') { | |||
empty(&lCmd); | |||
empty(&cCmd); | |||
empty(&searchStr); | |||
tfulldirt(); | |||
len = 0; | |||
} else if (cs[0] == fw || cs[0] == bw) { | |||
empty(&searchStr); | |||
state.m.search = cs[0]; | |||
state.m.searchPos = (Pos){.p={term.c.x, term.c.y, prevYOff}}; | |||
state.m.active = 1; | |||
} else if (cs[0]==infix_i || cs[0]==infix_a) { state.cmd.infix=cs[0]; | |||
} else if (cs[0] == 'y') { | |||
if (state.cmd.op) { | |||
result = (state.cmd.op == yank) ? exitOp : exitMotion; | |||
if (state.cmd.op == yank) selstart(0, term.c.y, 0); | |||
} else selstart(term.c.x, term.c.y, 0); | |||
state.cmd.op = yank; | |||
} else if (cs[0] == visual || cs[0] == visualLine) { | |||
if (state.cmd.op != (unsigned char) cs[0]) { | |||
state.cmd = defaultNormalMode.cmd; | |||
state.cmd.op = cs[0]; | |||
selstart(cs[0] == visualLine ?0 :term.c.x, term.c.y, 0); | |||
} else result = exitOp; | |||
} else if (!(result =executeMotion(len?cs[0]:0, len, ctrl?ksym:NULL))) { | |||
result=failed; | |||
for (size_t i = 0; !ctrl && i < amountNmKeys; ++i) | |||
if (cs[0]==nmKeys[i][0] && | |||
failed!=(result=pressKeys(&nmKeys[i][1], strlen(nmKeys[i])-1))) goto end; | |||
} // Operation/Motion finished if valid: update cmd string, extend selection, update search | |||
if (result != failed) { | |||
if (len == 1 && !ctrl) decodeTo(cs, len, &cCmd); | |||
if ((state.cmd.op == visualLine) || ((state.cmd.op == yank) && (result == exitOp))) { | |||
int const off = pos(term.c.y, 1) < pos(sel.ob.y, 0); | |||
sel.ob.x = off ? term.col - 1 : 0; | |||
selextend(off ? 0 : term.col-1, term.c.y, sel.type, 0); | |||
} else if (sel.oe.x != -1) selextend(term.c.x, term.c.y, sel.type, 0); | |||
} // Set repaint for motion or status bar | |||
if (!IS_SET(MODE_ALTSCREEN) && prevYOff != histOff) tfulldirt(); | |||
// Terminate Motion / operation if thus indicated | |||
if (result == exitMotion) { | |||
if (!state.m.active) result = (exited=noOp) ? finished : exitOp; | |||
state.m.active = state.m.c = 0; | |||
} | |||
if (result == exitOp || result == finished) { | |||
if (state.cmd.op == yank) { | |||
xsetsel(getsel()); | |||
xclipcopy(); | |||
} | |||
state = defaultNormalMode; | |||
selclear(); | |||
if (!esc) assign(&lCmd, &cCmd); | |||
empty(&cCmd); | |||
} // Update the content displayed in the history overlay | |||
styleCmd = style[state.cmd.op==yank ? 1 : (state.cmd.op==visual ? 2 : | |||
(state.cmd.op==visualLine ? 3 :0))]; | |||
int const posLin = !IS_SET(MODE_ALTSCREEN) ? rangeY(insertOff-histOff):0, h=rows()-term.row; | |||
if (!posLin || posLin==h || !h) strcpy(posBuffer, posLin ? " [BOT] " : " [TOP] "); | |||
else sprintf(posBuffer, " % 3d%c ", min(100, max(0, .5 + posLin * 100. / h)),'%'); | |||
if ((overlay || overlay!=prevOverlay) && term.col>9 && term.row>4) { | |||
if (!term.dirty[term.row-1]) xdrawline(term.line[term.row-1], term.col*2/3, term.row-1, term.col-1); | |||
if (!term.dirty[term.row-2]) xdrawline(term.line[term.row-2], term.col*2/3, term.row-2, term.col-1); | |||
} | |||
if (result==finished) altToggle = 0; | |||
if (altToggle != prevAltToggle) tswapscreen(); | |||
end: | |||
historyOpToggle(-1, 1); | |||
return result; | |||
} | |||
void historyOverlay(int x, int y, Glyph* g) { | |||
if (!histMode) return; | |||
TCursor const *cHist = histOp ? &term.c : &c[0]; | |||
if(overlay && term.col > 9 && term.row > 4 && (x > (2*term.col/3)) && (y >= (term.row-2))) { | |||
*g = (y == term.row - 2) ? styleSearch : styleCmd; | |||
if (y == term.row-2) getChar(&searchStr, g, term.row-2, term.col-2, term.col/3, x); | |||
else if (x > term.col - 7) g->u = posBuffer[x - term.col + 7]; | |||
else getChar(size(&cCmd) ?&cCmd :&lCmd, g, term.row-1, term.col-7, term.col/3-6, x); | |||
} else if (highlighted(x, y)) g->bg = highlightBg, g->fg = highlightFg; | |||
else if ((x==cHist->x) ^ (y==cHist->y)) g->bg = currentBg; | |||
else if (x==cHist->x) g->mode^=ATTR_REVERSE; | |||
} | |||
void historyPreDraw() { | |||
static Pos op = {.p={0, 0, 0}}; | |||
historyOpToggle(1, 0); | |||
// Draw the cursor cross if changed | |||
if (term.c.y >= term.row || op.p[1] >= term.row) tfulldirt(); | |||
else if (exited || (op.p[1] != term.c.y)) term.dirty[term.c.y] = term.dirty[op.p[1]] = 1; | |||
for (int i=0; (exited || term.c.x != op.p[0]) && i<term.row; ++i) if (!term.dirty[i]) { | |||
xdrawline(term.line[i], term.c.x, i, term.c.x + 1); | |||
xdrawline(term.line[i], op.p[0], i, op.p[0] + 1); | |||
} | |||
// Update search results either only for lines with new content or all results if exiting | |||
markSearchMatches(exited); | |||
op = (Pos){.p = {term.c.x, term.c.y, 0}}; | |||
historyOpToggle(-1, 0); | |||
} |
@ -0,0 +1,7 @@ | |||
void normalMode(); | |||
void historyPreDraw(); | |||
void historyOverlay(int x, int y, Glyph* g); | |||
void historyModeToggle(int start); | |||
/// Handles keys in normal mode. | |||
typedef enum {failed=0, succ=1, exitMotion=2, exitOp=3, finished=4} ExitState; | |||
ExitState kpressHist(char const *txt, int len, int ctrl, KeySym const *kSym); |
@ -0,0 +1,23 @@ | |||
/// Dynamic memory-chunk, with (1) datatype size, (2/3) initialized / allocated chunk, (4) content | |||
typedef struct { uint8_t const elSize; uint32_t init, alloc; char* content; } DynamicArray; | |||
#define UTF8_ARRAY {4, 0, 0, NULL} | |||
static inline int p_alloc(DynamicArray *s, uint32_t amount) { | |||
uint32_t const diff=s->init+s->elSize*amount-s->alloc, nas=s->alloc+max(diff,15)*s->elSize; | |||
if (s->alloc < s->init + s->elSize * amount) { | |||
char* tmp = realloc(s->content, nas); | |||
if (!tmp) return 0; | |||
s->alloc = nas, s->content = tmp; | |||
} | |||
return 1; | |||
} | |||
static inline char *view(DynamicArray * s, uint32_t i) { return s->content + i*s->elSize; } | |||
static inline char *end(DynamicArray *s, uint32_t i) { return s->content +s->init-(i+1)*s->elSize; } | |||
static inline uint32_t getU32(DynamicArray* s, uint32_t i, int b) { return *((uint32_t*) (b ?view(s,i) :end(s,i))); } | |||
static char *expand(DynamicArray *s) { if (!p_alloc(s, 1)) return NULL; s->init += s->elSize; return end(s, 0); } | |||
static inline void pop(DynamicArray* s) { s->init -= s->elSize; } | |||
static inline void empty(DynamicArray* s) { s->init = 0; } | |||
static inline int size(DynamicArray const * s) { return s->init / s->elSize; } | |||
static inline void assign(DynamicArray* s, DynamicArray const *o) { | |||
if (p_alloc(s, size(o))) memcpy(s->content, o->content, (s->init=o->init)); | |||
} |