Merge pull request #12 from t3dotgg/theo/more-gutting

Removed more bs
This commit is contained in:
Theo Browne 2025-02-26 01:10:18 -08:00 committed by GitHub
commit 8e6057db6a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
12 changed files with 48 additions and 3340 deletions

View file

@ -1,6 +1,8 @@
# Material Theme (But I won't sue you)
> **Note:** The original Material Theme has been removed from the marketplace due to [distributing malware through their extensions](https://news.ycombinator.com/item?id=43178831#43181919). This fork has been thoroughly audited and is completely safe to use. I have personally audited every line, and removed a TON of unnecessary stuff to be sure. The VS Code team is auditing it as well just to be extra safe.
So, uh, the guy who made the VS Code Material Theme is threatening everyone who uses it in their products. He [seems to have forgotten it was originally licensed under the Apache License, 2.0.](https://github.com/Dramaga11/vsc-material-theme/blob/main/LICENSE). He wiped the commit history to make it look like it was always his weird fake license.
What he has done is fraudulent and shameful. I have created this fork to maintain the original license and keep the project alive.

2975
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -2,7 +2,7 @@
"name": "vsc-material-theme-but-i-wont-sue-you",
"displayName": "Material Theme (But I Won't Sue You)",
"description": "A Apache-2 licensed fork of Material Theme with no threats of legal action",
"version": "35.0.0",
"version": "35.0.2",
"publisher": "t3dotgg",
"license": "Apache-2.0",
"author": "Theo",
@ -23,17 +23,15 @@
"vscode": "^1.51.0"
},
"scripts": {
"build": "run-s cleanup build:ts build:generate-themes build:ui",
"build": "run-s cleanup build:ts build:generate-themes",
"cleanup": "rimraf build && rimraf dist",
"lint": "eslint .",
"build:ui": "node dist/scripts/ui/index.js",
"build:generate-themes": "node dist/scripts/generator/index.js",
"build:ts": "tsc -p ./tsconfig.json && ncp dist/src/ build && ncp material-theme.config.json build",
"postinstall": "node ./node_modules/vscode/bin/install && tsc -p tsconfig.json"
},
"categories": [
"Themes",
"Other"
"Themes"
],
"keywords": [
"VSCode",
@ -175,7 +173,7 @@
"fs-extra": "9.0.1",
"ncp": "2.0.0",
"npm-run-all": "4.1.5",
"standard-version": "9.5.0",
"rimraf": "3.0.2",
"typescript": "4.1.3",
"vscode": "1.1.37"
},

View file

@ -1,17 +0,0 @@
import * as fs from 'fs-extra';
import * as path from 'path';
import {BUILD_FOLDER_PATH} from '../../src/env';
const UI_FOLDER_BUILD_PATH = path.join(BUILD_FOLDER_PATH, 'ui');
const run = async (): Promise<void> => {
try {
await fs.mkdirp(UI_FOLDER_BUILD_PATH);
} catch (error) {
console.error('ERROR build:ui:', error);
process.exit(1);
}
};
void run();

View file

@ -17,16 +17,10 @@ type PackageJSON = {
};
};
type InstallationType = {
firstInstall: boolean;
update: boolean;
};
export interface IExtensionManager {
init: (context: ExtensionContext) => Promise<void>;
getPackageJSON: () => PackageJSON;
getConfig: () => MaterialThemeConfig;
getInstallationType: () => Record<string, unknown>;
updateConfig: (config: Partial<MaterialThemeConfig>) => Promise<void>;
VERSION_KEY: string;
}
@ -36,7 +30,6 @@ class ExtensionManager implements IExtensionManager {
return 'vsc-material-theme.version';
}
installationType: InstallationType;
private readonly configFileUri: Uri;
private readonly userConfigFileUri: Uri;
private configJSON: MaterialThemeConfig;
@ -55,10 +48,6 @@ class ExtensionManager implements IExtensionManager {
return this.configJSON;
}
getInstallationType(): InstallationType {
return this.installationType;
}
async updateConfig(config: Partial<MaterialThemeConfig>): Promise<void> {
const newConfig = {...this.configJSON, ...config};
await workspace.fs.writeFile(this.configFileUri, Buffer.from(JSON.stringify(newConfig), 'utf-8'));
@ -67,62 +56,27 @@ class ExtensionManager implements IExtensionManager {
async init(context: ExtensionContext): Promise<void> {
try {
const packageJSON = this.getPackageJSON();
const userConfig = await this.getUserConfig();
const mementoStateVersion = context.globalState.get(this.VERSION_KEY);
const themeNeverUsed = mementoStateVersion === undefined || typeof mementoStateVersion !== 'string';
this.installationType = {
update: userConfig && this.isVersionUpdate(userConfig, packageJSON),
firstInstall: !userConfig && themeNeverUsed
};
// Theme not used before across devices
if (themeNeverUsed) {
await context.globalState.update(this.VERSION_KEY, packageJSON.version);
}
// Load configuration
const configBuffer = await workspace.fs.readFile(this.configFileUri);
const configContent = Buffer.from(configBuffer).toString('utf8');
this.configJSON = JSON.parse(configContent) as MaterialThemeConfig;
// Update version in user config
const userConfigUpdate = {...this.configJSON, changelog: {lastversion: packageJSON.version}};
await workspace.fs.writeFile(
this.userConfigFileUri,
Buffer.from(JSON.stringify(userConfigUpdate), 'utf-8')
);
// Store version in global state
await context.globalState.update(this.VERSION_KEY, packageJSON.version);
} catch (error) {
this.configJSON = {accentsProperties: {}, accents: {}};
await window
.showErrorMessage(`Material Theme: there was an error while loading the configuration. Please retry or open an issue: ${String(error)}`);
}
}
private isVersionUpdate(userConfig: MaterialThemeConfig, packageJSON: PackageJSON): boolean {
const splitVersion = (input: string): {major: number; minor: number; patch: number} => {
const [major, minor, patch] = input.split('.').map(i => parseInt(i, 10));
return {major, minor, patch};
};
const versionCurrent = splitVersion(packageJSON.version);
const versionOld = splitVersion(userConfig.changelog.lastversion);
const update = (
versionCurrent.major > versionOld.major ||
versionCurrent.minor > versionOld.minor ||
versionCurrent.patch > versionOld.patch
);
return update;
}
private async getUserConfig(): Promise<MaterialThemeConfig | undefined> {
try {
const configBuffer = await workspace.fs.readFile(this.userConfigFileUri);
const configContent = Buffer.from(configBuffer).toString('utf8');
return JSON.parse(configContent) as MaterialThemeConfig;
} catch {}
}
}
export const extensionManager = new ExtensionManager();

View file

@ -1,45 +0,0 @@
// WIP
// Import {WebviewController} from './Webview';
// import {
// workspace as Workspace
// } from 'vscode';
// import {ISettingsBootstrap} from './interfaces';
// import {getCustomSettings} from '../helpers/settings';
// import {getDefaultValues} from '../helpers/fs';
// export class SettingsWebview extends WebviewController<ISettingsBootstrap> {
// get filename(): string {
// return 'settings.html';
// }
// get id(): string {
// return 'materialTheme.settings';
// }
// get title(): string {
// return 'Material Theme Settings';
// }
// /**
// * This will be called by the WebviewController when init the view
// * passing as `window.bootstrap` to the view.
// */
// getBootstrap(): ISettingsBootstrap {
// return {
// config: getCustomSettings(),
// defaults: getDefaultValues(),
// scope: 'user',
// scopes: this.getAvailableScopes()
// };
// }
// private getAvailableScopes(): Array<['user' | 'workspace', string]> {
// const scopes: Array<['user' | 'workspace', string]> = [['user', 'User']];
// return scopes
// .concat(
// Workspace.workspaceFolders?.length ?
// ['workspace', 'Workspace'] :
// []
// );
// }
// }

View file

@ -1,161 +0,0 @@
import * as path from 'path';
import {
workspace as Workspace,
Disposable,
ExtensionContext,
WebviewPanel,
ViewColumn,
window,
WebviewPanelOnDidChangeViewStateEvent,
Uri
} from 'vscode';
import {Invalidates, Message} from './interfaces';
export abstract class WebviewController<TBootstrap> extends Disposable {
private panel: WebviewPanel | undefined;
private disposablePanel: Disposable | undefined;
private invalidateOnVisible: Invalidates;
private readonly context: ExtensionContext;
constructor(context: ExtensionContext) {
// Applying dispose callback for our disposable function
super(() => this.dispose());
this.context = context;
}
dispose(): void {
if (this.disposablePanel) {
this.disposablePanel.dispose();
}
}
async show(): Promise<void> {
const html = await this.getHtml();
// If panel already opened just reveal
if (this.panel !== undefined) {
// Replace placeholders in html content for assets and adding configurations as `window.bootstrap`
const fullHtml = this.replaceInPanel(html);
this.panel.webview.html = fullHtml;
return this.panel.reveal(ViewColumn.Active);
}
this.panel = window.createWebviewPanel(
this.id,
this.title,
ViewColumn.Active,
{
retainContextWhenHidden: true,
enableFindWidget: true,
enableCommandUris: true,
enableScripts: true
}
);
// Applying listeners
this.disposablePanel = Disposable.from(
this.panel,
this.panel.onDidDispose(this.onPanelDisposed, this),
this.panel.onDidChangeViewState(this.onViewStateChanged, this),
this.panel.webview.onDidReceiveMessage(this.onMessageReceived, this)
);
// Replace placeholders in html content for assets and adding configurations as `window.bootstrap`
const fullHtml = this.replaceInPanel(html);
this.panel.webview.html = fullHtml;
}
protected onMessageReceived(event: Message): void {
if (event === null) {
return;
}
console.log(`WebviewEditor.onMessageReceived: type=${event.type}, data=${JSON.stringify(event)}`);
switch (event.type) {
case 'saveSettings':
// TODO: update settings
break;
default:
break;
}
}
private replaceInPanel(html: string): string {
// Replace placeholders in html content for assets and adding configurations as `window.bootstrap`
const fullHtml = html
.replace(/{{root}}/g, this.panel.webview.asWebviewUri(Uri.file(this.context.asAbsolutePath('./build'))).toString())
.replace(/{{cspSource}}/g, this.panel.webview.cspSource)
.replace('\'{{bootstrap}}\'', JSON.stringify(this.getBootstrap()));
return fullHtml;
}
private async getHtml(): Promise<string> {
const doc = await Workspace
.openTextDocument(this.context.asAbsolutePath(path.join('build/ui', this.filename)));
return doc.getText();
}
// Private async postMessage(message: Message, invalidates: Invalidates = 'all'): Promise<boolean> {
// if (this.panel === undefined) {
// return false;
// }
// const result = await this.panel.webview.postMessage(message);
// // If post was ok, update invalidateOnVisible if different than default
// if (!result && this.invalidateOnVisible !== 'all') {
// this.invalidateOnVisible = invalidates;
// }
// return result;
// }
// Private async postUpdatedConfiguration(): Promise<boolean> {
// // Post full raw configuration
// return this.postMessage({
// type: 'settingsChanged',
// config: getCustomSettings()
// } as ISettingsChangedMessage, 'config');
// }
private onPanelDisposed(): void {
if (this.disposablePanel) {
this.disposablePanel.dispose();
}
this.panel = undefined;
}
private async onViewStateChanged(event: WebviewPanelOnDidChangeViewStateEvent): Promise<void> {
console.log('WebviewEditor.onViewStateChanged', event.webviewPanel.visible);
if (!this.invalidateOnVisible || !event.webviewPanel.visible) {
return;
}
// Update the view since it can be outdated
const invalidContext = this.invalidateOnVisible;
this.invalidateOnVisible = undefined;
switch (invalidContext) {
case 'config':
// Post the new configuration to the view
// return this.postUpdatedConfiguration();
return;
default:
return this.show();
}
}
abstract get filename(): string;
abstract get id(): string;
abstract get title(): string;
abstract getBootstrap(): TBootstrap;
}

View file

@ -1,33 +0,0 @@
export interface ISettingsChangedMessage {
type: 'settingsChanged';
config: Record<string, unknown>;
}
export interface ISaveSettingsMessage {
type: 'saveSettings';
changes: {
[key: string]: any;
};
removes: string[];
scope: 'user' | 'workspace';
uri: string;
}
export type Message = ISaveSettingsMessage | ISettingsChangedMessage;
export type Invalidates = 'all' | 'config' | undefined;
export interface IBootstrap {
config: Record<string, unknown>;
}
export interface ISettingsBootstrap extends IBootstrap {
scope: 'user' | 'workspace';
scopes: Array<['user' | 'workspace', string]>;
defaults: Record<string, unknown>;
}
declare global {
interface Window {
bootstrap: IBootstrap | ISettingsBootstrap | Record<string, unknown>;
}
}

View file

@ -1,20 +0,0 @@
import {ISettingsBootstrap} from '../../interfaces';
// Import accentsSelector from './lib/accents-selector';
const run = (): void => {
bind();
const {config, defaults} = window.bootstrap as ISettingsBootstrap;
// AccentsSelector('[data-setting="accentSelector"]', defaults.accents, config.accent);
console.log(defaults);
console.log(config);
};
const bind = (): void => {
document.querySelector('#fixIconsCTA').addEventListener('click', () => {
console.log('Test click');
});
};
run();

View file

@ -1,26 +0,0 @@
// Import {IAccents} from '../../../../interfaces/idefaults';
const templateSingleAccent = (accentName: string, accentColor: string): string => {
const dashAccentName = accentName.toLowerCase().replace(/ /gi, '-');
return `
<label for="${dashAccentName}" data-color="${accentColor}">${accentName}</label>
<input type="radio" name="accents" id="${dashAccentName}" value="${dashAccentName}" />
`;
};
export default (containerSelector: string, accentsObject: Record<string, string>, currentAccent: string): void => {
const container = document.querySelector(containerSelector);
for (const accentKey of Object.keys(accentsObject)) {
const el = document.createElement('div');
el.innerHTML = templateSingleAccent(accentKey, accentsObject[accentKey]);
if (accentKey === currentAccent) {
el.setAttribute('selected', 'true');
el.querySelector('input').setAttribute('checked', 'checked');
}
container.appendChild(el);
}
};

View file

@ -1,39 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Material Theme Settings Editor</title>
<link rel="stylesheet" href="{{root}}/ui/settings/style.css">
<link rel="stylesheet" href="https://unpkg.com/@native-elements/native-elements/dist/native-elements.css">
</head>
<body>
<header>
<h1>Material Theme Settings Editor (preview)</h1>
</header>
<main>
<button ne-button tabindex="2" autofocus>Button</button>
<div class="SettingsContainer">
<div>
<label>Fix file icons</label>
<input id="fixIconsCTA" type="submit" value="Fix">
</div>
<div class="AccentsRadioContainer" data-setting="accentSelector">
<!-- Populated by js -->
</div>
</div>
</main>
<input value="Example input">
<script type="text/javascript">
window.bootstrap = '{{bootstrap}}';
</script>
<script type="text/javascript" src="{{root}}/ui/settings.js"></script>
</body>
</html>

View file

@ -1,4 +0,0 @@
.AccentsRadioContainer > div {
display: inline-block;
margin-right: 10px;
}