mirror of
https://gitlab.com/aviortheking/code-stats-vscode.git
synced 2025-04-22 02:42:11 +00:00
feat: Allow the plugin to work under the browser context
Signed-off-by: Avior <f.bouillon@aptatio.com>
This commit is contained in:
parent
e42e5271db
commit
cef14d1038
2
.gitignore
vendored
2
.gitignore
vendored
@ -1,5 +1,5 @@
|
|||||||
.DS_Store
|
.DS_Store
|
||||||
out
|
out
|
||||||
node_modules
|
node_modules
|
||||||
.vscode-test/
|
.vscode-test*/
|
||||||
*.vsix
|
*.vsix
|
5
.vscode/settings.json
vendored
5
.vscode/settings.json
vendored
@ -6,5 +6,8 @@
|
|||||||
"search.exclude": {
|
"search.exclude": {
|
||||||
"out": true // set this to false to include "out" folder in search results
|
"out": true // set this to false to include "out" folder in search results
|
||||||
},
|
},
|
||||||
"typescript.tsdk": "node_modules/typescript/lib"
|
"typescript.tsdk": "node_modules/typescript/lib",
|
||||||
|
|
||||||
|
// username for internal browser tests
|
||||||
|
"codestats.username": "Aviortheking"
|
||||||
}
|
}
|
14
esbuild.js
Normal file
14
esbuild.js
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
const { NodeModulesPolyfillPlugin } = require('@esbuild-plugins/node-modules-polyfill')
|
||||||
|
const { build } = require('esbuild')
|
||||||
|
build({
|
||||||
|
entryPoints: ['./src/code-stats.ts'],
|
||||||
|
plugins: [NodeModulesPolyfillPlugin()],
|
||||||
|
bundle: true,
|
||||||
|
minify: true,
|
||||||
|
sourcemap: true,
|
||||||
|
target: ['es2016', 'chrome90', 'firefox78', 'safari14', 'edge90'],
|
||||||
|
external: ['vscode', 'node-fetch'],
|
||||||
|
outfile: 'out/browser.js',
|
||||||
|
format: 'cjs',
|
||||||
|
platform: 'node'
|
||||||
|
})
|
3268
package-lock.json
generated
3268
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
17
package.json
17
package.json
@ -31,6 +31,7 @@
|
|||||||
"*"
|
"*"
|
||||||
],
|
],
|
||||||
"main": "./out/src/code-stats",
|
"main": "./out/src/code-stats",
|
||||||
|
"browser": "./out/browser",
|
||||||
"extensionKind": [
|
"extensionKind": [
|
||||||
"ui",
|
"ui",
|
||||||
"workspace"
|
"workspace"
|
||||||
@ -66,19 +67,25 @@
|
|||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"vscode:prepublish": "tsc -p ./",
|
"vscode:prepublish": "tsc -p ./",
|
||||||
"compile": "tsc -watch -p ./"
|
"compile": "tsc -watch -p ./",
|
||||||
|
"test:browser": "vscode-test-web --extensionDevelopmentPath=. .",
|
||||||
|
"compile:browser": "node esbuild.js"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@esbuild-plugins/node-modules-polyfill": "^0.1.4",
|
||||||
"@types/mocha": "^7.0.2",
|
"@types/mocha": "^7.0.2",
|
||||||
"@types/node": "^13.9.8",
|
"@types/node": "^13.9.8",
|
||||||
|
"@types/node-fetch": "^2.6.2",
|
||||||
"@types/vscode": "^1.43.2",
|
"@types/vscode": "^1.43.2",
|
||||||
|
"@vscode/test-web": "^0.0.34",
|
||||||
|
"esbuild": "^0.17.3",
|
||||||
"growl": "^1.10.5",
|
"growl": "^1.10.5",
|
||||||
"mocha": "^7.1.1",
|
"mocha": "^7.1.1",
|
||||||
"typescript": "^3.8.3",
|
"typescript": "^3.8.3",
|
||||||
"vscode-test": "^1.5.2"
|
"vscode-test": "^1.5.2"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"axios": "^0.19.2",
|
"lodash.template": "^4.5.0",
|
||||||
"lodash.template": "^4.5.0"
|
"node-fetch": "^2.6.8"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,102 +1,105 @@
|
|||||||
import { Pulse } from "./pulse";
|
import type fetch from 'node-fetch'
|
||||||
import { getISOTimestamp, getLanguageName } from "./utils";
|
import { Pulse } from "./pulse"
|
||||||
import * as axios from "axios";
|
import { getISOTimestamp, getLanguageName } from "./utils"
|
||||||
|
|
||||||
|
// Please do not look at this
|
||||||
|
let realFetch: typeof fetch
|
||||||
|
// @ts-expect-error VSCode is not including fetch everytime
|
||||||
|
if (typeof fetch === 'undefined') {
|
||||||
|
realFetch = require('node-fetch')
|
||||||
|
} else {
|
||||||
|
// @ts-expect-error VSCode is not including fetch everytime
|
||||||
|
realFetch = fetch
|
||||||
|
}
|
||||||
|
|
||||||
export class CodeStatsAPI {
|
export class CodeStatsAPI {
|
||||||
private API_KEY = null;
|
private API_KEY = null;
|
||||||
private USER_NAME = null;
|
private USER_NAME = null;
|
||||||
private UPDATE_URL = "https://codestats.net/api/";
|
private UPDATE_URL = "https://codestats.net/api";
|
||||||
|
private headers: Record<string, string> = {
|
||||||
private axios = null;
|
"Content-Type": "application/json"
|
||||||
|
}
|
||||||
|
|
||||||
constructor(apiKey: string, apiURL: string, userName: string) {
|
constructor(apiKey: string, apiURL: string, userName: string) {
|
||||||
this.updateSettings(apiKey, apiURL, userName);
|
this.updateSettings(apiKey, apiURL, userName);
|
||||||
}
|
}
|
||||||
|
|
||||||
public updateSettings( apiKey: string, apiURL: string, userName: string) {
|
public updateSettings(apiKey: string, apiURL: string, userName: string) {
|
||||||
|
|
||||||
this.API_KEY = apiKey;
|
this.API_KEY = apiKey;
|
||||||
this.UPDATE_URL = apiURL;
|
this.UPDATE_URL = apiURL;
|
||||||
this.USER_NAME = userName;
|
this.USER_NAME = userName;
|
||||||
|
|
||||||
if (
|
if (
|
||||||
this.API_KEY === null ||
|
this.API_KEY === null ||
|
||||||
this.API_KEY === undefined ||
|
this.API_KEY === undefined ||
|
||||||
this.API_KEY === ""
|
this.API_KEY === ""
|
||||||
) {
|
) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.axios = axios.default.create({
|
this.headers["X-API-Token"] = this.API_KEY
|
||||||
baseURL: this.UPDATE_URL,
|
}
|
||||||
timeout: 10000,
|
|
||||||
headers: {
|
|
||||||
"X-API-Token": this.API_KEY,
|
|
||||||
"Content-Type": "application/json"
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public sendUpdate(pulse: Pulse): axios.AxiosPromise {
|
public async sendUpdate(pulse: Pulse): Promise<any> {
|
||||||
// If we did not have API key, don't try to update
|
// If we did not have API key, don't try to update
|
||||||
if (this.axios === null) {
|
if (this.API_KEY === null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// tslint:disable-next-line:typedef
|
// tslint:disable-next-line:typedef
|
||||||
const data = new ApiJSON(new Date());
|
const data = new ApiJSON(new Date());
|
||||||
|
|
||||||
for (let lang of pulse.xps.keys()) {
|
for (let lang of pulse.xps.keys()) {
|
||||||
let languageName: string = getLanguageName(lang);
|
let languageName: string = getLanguageName(lang);
|
||||||
let xp: number = pulse.getXP(lang);
|
let xp: number = pulse.getXP(lang);
|
||||||
data.xps.push(new ApiXP(languageName, xp));
|
data.xps.push(new ApiXP(languageName, xp));
|
||||||
}
|
}
|
||||||
|
|
||||||
let json: string = JSON.stringify(data);
|
let json: string = JSON.stringify(data);
|
||||||
console.log(`JSON: ${json}`);
|
console.log(`JSON: ${json}`);
|
||||||
|
|
||||||
return this.axios
|
try {
|
||||||
.post("my/pulses", json)
|
const response = await realFetch(`${this.UPDATE_URL}/my/pulses`, {
|
||||||
.then(response => {
|
method: 'POST',
|
||||||
console.log(response);
|
body: json,
|
||||||
})
|
headers: this.headers
|
||||||
.then(() => {
|
})
|
||||||
pulse.reset();
|
console.log(response)
|
||||||
})
|
pulse.reset()
|
||||||
.catch(error => {
|
} catch (error) {
|
||||||
console.log(error);
|
console.log(error)
|
||||||
});
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public getProfile(): axios.AxiosPromise {
|
public async getProfile(): Promise<any> {
|
||||||
return this.axios
|
try {
|
||||||
.get(`users/${this.USER_NAME}`)
|
const resp = await realFetch(`${this.UPDATE_URL}/users/${this.USER_NAME}`)
|
||||||
.then(response => {
|
const response = await resp.json()
|
||||||
return response.data;
|
return response
|
||||||
})
|
} catch (error) {
|
||||||
.catch(error => {
|
console.log(error)
|
||||||
console.log(error);
|
return null
|
||||||
return null;
|
}
|
||||||
});
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class ApiJSON {
|
class ApiJSON {
|
||||||
constructor(date: Date) {
|
constructor(date: Date) {
|
||||||
this.coded_at = getISOTimestamp(new Date());
|
this.coded_at = getISOTimestamp(new Date());
|
||||||
this.xps = new Array<ApiXP>();
|
this.xps = new Array<ApiXP>();
|
||||||
}
|
}
|
||||||
|
|
||||||
coded_at: string;
|
coded_at: string;
|
||||||
xps: Array<ApiXP>;
|
xps: Array<ApiXP>;
|
||||||
}
|
}
|
||||||
|
|
||||||
class ApiXP {
|
class ApiXP {
|
||||||
constructor(language: string, xp: number) {
|
constructor(language: string, xp: number) {
|
||||||
this.language = language;
|
this.language = language;
|
||||||
this.xp = xp;
|
this.xp = xp;
|
||||||
}
|
}
|
||||||
|
|
||||||
language: string;
|
language: string;
|
||||||
xp: number;
|
xp: number;
|
||||||
}
|
}
|
||||||
|
@ -1,10 +1,9 @@
|
|||||||
"use strict";
|
import { ExtensionContext } from "vscode"
|
||||||
import { ExtensionContext } from "vscode";
|
import { XpCounter } from "./xp-counter"
|
||||||
import { XpCounter } from "./xp-counter";
|
|
||||||
|
|
||||||
export function activate(context: ExtensionContext): void {
|
export function activate(context: ExtensionContext): void {
|
||||||
let controller: XpCounter = new XpCounter(context);
|
let controller: XpCounter = new XpCounter(context);
|
||||||
context.subscriptions.push(controller);
|
context.subscriptions.push(controller);
|
||||||
}
|
}
|
||||||
|
|
||||||
// this method is called when your extension is deactivated
|
// this method is called when your extension is deactivated
|
||||||
|
@ -1,99 +1,94 @@
|
|||||||
|
|
||||||
import * as fs from 'fs';
|
import { CancellationToken, Event, ExtensionContext, TextDocumentContentProvider, Uri, workspace } from "vscode"
|
||||||
import { CancellationToken, Event, ExtensionContext, TextDocumentContentProvider, Uri } from "vscode";
|
import { CodeStatsAPI } from "./code-stats-api"
|
||||||
import { CodeStatsAPI } from "./code-stats-api";
|
import profileHtmlEex from './profile.html.eex'
|
||||||
import * as path from 'path';
|
|
||||||
|
|
||||||
import template = require('lodash.template');
|
import template = require('lodash.template');
|
||||||
|
|
||||||
export class ProfileProvider implements TextDocumentContentProvider {
|
export class ProfileProvider implements TextDocumentContentProvider {
|
||||||
onDidChange?: Event<Uri>;
|
onDidChange?: Event<Uri>;
|
||||||
|
|
||||||
api: CodeStatsAPI;
|
api: CodeStatsAPI;
|
||||||
context: ExtensionContext;
|
context: ExtensionContext;
|
||||||
|
|
||||||
constructor(context: ExtensionContext, api: CodeStatsAPI) {
|
constructor(context: ExtensionContext, api: CodeStatsAPI) {
|
||||||
this.context = context;
|
this.context = context;
|
||||||
this.api = api;
|
this.api = api;
|
||||||
}
|
}
|
||||||
|
|
||||||
provideTextDocumentContent(uri: Uri, token: CancellationToken): string | Thenable<string> {
|
provideTextDocumentContent(uri: Uri, token: CancellationToken): string | Thenable<string> {
|
||||||
|
|
||||||
if( token.isCancellationRequested )
|
if (token.isCancellationRequested) return;
|
||||||
return;
|
|
||||||
|
|
||||||
const LEVEL_FACTOR = 0.025;
|
const LEVEL_FACTOR = 0.025;
|
||||||
|
|
||||||
function getLevel(xp: number): number {
|
function getLevel(xp: number): number {
|
||||||
return Math.floor(Math.sqrt(xp) * LEVEL_FACTOR);
|
return Math.floor(Math.sqrt(xp) * LEVEL_FACTOR);
|
||||||
}
|
}
|
||||||
|
|
||||||
function getNextLevelXp(level: number): number {
|
function getNextLevelXp(level: number): number {
|
||||||
return Math.pow(Math.ceil((level + 1) / LEVEL_FACTOR), 2);
|
return Math.pow(Math.ceil((level + 1) / LEVEL_FACTOR), 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
function getLevelProgress(xp: number, new_xp: number): number[] {
|
function getLevelProgress(xp: number, new_xp: number): number[] {
|
||||||
let level = getLevel(xp);
|
let level = getLevel(xp);
|
||||||
let curLevelXp = getNextLevelXp(level - 1);
|
let curLevelXp = getNextLevelXp(level - 1);
|
||||||
let nextLevelXp = getNextLevelXp(level);
|
let nextLevelXp = getNextLevelXp(level);
|
||||||
|
|
||||||
let haveXp = xp - curLevelXp;
|
let haveXp = xp - curLevelXp;
|
||||||
|
|
||||||
let needXp = nextLevelXp - curLevelXp;
|
let needXp = nextLevelXp - curLevelXp;
|
||||||
|
|
||||||
let xpP = Math.round(haveXp * 100.0 / needXp);
|
let xpP = Math.round(haveXp * 100.0 / needXp);
|
||||||
let nxpP = Math.round(new_xp * 100.0 / needXp);
|
let nxpP = Math.round(new_xp * 100.0 / needXp);
|
||||||
return [ xpP, nxpP ];
|
return [ xpP, nxpP ];
|
||||||
}
|
}
|
||||||
|
|
||||||
function getSortedArray(obj: any): any[] {
|
function getSortedArray(obj: any): any[] {
|
||||||
|
let items = [];
|
||||||
|
|
||||||
let items = [];
|
for (const prop in obj) {
|
||||||
|
let item = obj[prop];
|
||||||
|
let percents = getLevelProgress(item.xps, item.new_xps);
|
||||||
|
items.push(
|
||||||
|
{
|
||||||
|
name: prop,
|
||||||
|
level: getLevel(item.xps),
|
||||||
|
xp: item.xps,
|
||||||
|
new_xp: item.new_xps,
|
||||||
|
progress: percents[0],
|
||||||
|
new_progress: percents[1]
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return items.sort( (a,b) => {return b.xp - a.xp;});
|
||||||
|
}
|
||||||
|
|
||||||
for( let prop in obj) {
|
return this.api.getProfile().then(async (profile) => {
|
||||||
let item = obj[prop];
|
|
||||||
let percents = getLevelProgress(item.xps, item.new_xps);
|
|
||||||
items.push(
|
|
||||||
{
|
|
||||||
name: prop,
|
|
||||||
level: getLevel(item.xps),
|
|
||||||
xp: item.xps,
|
|
||||||
new_xp: item.new_xps,
|
|
||||||
progress: percents[0],
|
|
||||||
new_progress: percents[1]
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return items.sort( (a,b) => {return b.xp - a.xp;});
|
|
||||||
}
|
|
||||||
|
|
||||||
return this.api.getProfile().then(profile => {
|
if( profile === null )
|
||||||
|
{
|
||||||
|
return `<h1>Can't fetch profile. Please try again later</h1> Make sure <strong>codestats.username</strong> setting is set to correct user name.`;
|
||||||
|
}
|
||||||
|
|
||||||
if( profile === null )
|
// don't look at this file please
|
||||||
{
|
let htmlTemplate = profileHtmlEex
|
||||||
return `<h1>Can't fetch profile. Please try again later</h1> Make sure <strong>codestats.username</strong> setting is set to correct user name.`;
|
|
||||||
}
|
profile["level"] = getLevel(profile["total_xp"]);
|
||||||
|
|
||||||
let htmlTemplate = fs.readFileSync(this.context.asAbsolutePath("assets/profile.html.eex"));
|
let percents = getLevelProgress(profile["total_xp"], profile["new_xp"]);
|
||||||
|
|
||||||
profile["level"] = getLevel(profile["total_xp"]);
|
|
||||||
|
|
||||||
let percents = getLevelProgress(profile["total_xp"], profile["new_xp"]);
|
profile["progress"] = percents[0];
|
||||||
|
profile["new_progress"] = percents[1];
|
||||||
|
|
||||||
profile["progress"] = percents[0];
|
let languages = getSortedArray(profile["languages"]);
|
||||||
profile["new_progress"] = percents[1];
|
let machines = getSortedArray(profile["machines"]);
|
||||||
|
|
||||||
|
let html = template(htmlTemplate);
|
||||||
|
|
||||||
let languages = getSortedArray(profile["languages"]);
|
return html({profile: profile, languages: languages, machines: machines});
|
||||||
let machines = getSortedArray(profile["machines"]);
|
|
||||||
|
|
||||||
let html = template(htmlTemplate);
|
|
||||||
|
|
||||||
const stylePath = Uri.file(path.join(this.context.extensionPath, 'assets', 'profile.css'));
|
|
||||||
const styleSrc = stylePath.with({scheme: 'vscode-resource'});
|
|
||||||
|
|
||||||
return html({profile: profile, languages: languages, machines: machines, style: styleSrc});
|
});
|
||||||
|
}
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
191
src/profile.html.eex.ts
Normal file
191
src/profile.html.eex.ts
Normal file
@ -0,0 +1,191 @@
|
|||||||
|
export default `
|
||||||
|
<style>
|
||||||
|
/* Dark theme color palette */
|
||||||
|
.vscode-dark {
|
||||||
|
--color-language: #dbd5b9;
|
||||||
|
--color-primary: #E3C23D;
|
||||||
|
--color-secondary: #5D5535;
|
||||||
|
--color-bar-background: #303030;
|
||||||
|
--color-bar-border: #424242;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Light theme color palette */
|
||||||
|
.vscode-light {
|
||||||
|
--color-language: #1B2334;
|
||||||
|
--color-primary: #5D78B3;
|
||||||
|
--color-secondary: #354567;
|
||||||
|
--color-bar-background: #939DB3;
|
||||||
|
--color-bar-border: #697080;
|
||||||
|
}
|
||||||
|
|
||||||
|
.profile {
|
||||||
|
color: var(--color-language);
|
||||||
|
}
|
||||||
|
|
||||||
|
h3 {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
sup {
|
||||||
|
top: -.5em;
|
||||||
|
font-size: 75%;
|
||||||
|
color: var(--color-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.language-progress {
|
||||||
|
float: left;
|
||||||
|
position: relative;
|
||||||
|
width: 6rem;
|
||||||
|
height: 6rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.language-progress .tooltiptext {
|
||||||
|
visibility: hidden;
|
||||||
|
width: 120px;
|
||||||
|
background-color: var(--color-bar-border);
|
||||||
|
color: var(--color-language);
|
||||||
|
text-align: center;
|
||||||
|
border-radius: 6px;
|
||||||
|
padding: 5px 0;
|
||||||
|
position: absolute;
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.language-progress:hover .tooltiptext {
|
||||||
|
visibility: visible;
|
||||||
|
}
|
||||||
|
|
||||||
|
.language-progress svg {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
transform: rotate(-90deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
circle {
|
||||||
|
stroke-width: 5;
|
||||||
|
fill:transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
circle.backg {
|
||||||
|
stroke: var(--color-bar-background);
|
||||||
|
}
|
||||||
|
|
||||||
|
circle.newxp {
|
||||||
|
stroke: var(--color-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
circle.oldxp {
|
||||||
|
stroke: var(--color-secondary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.language {
|
||||||
|
padding-top: 2rem;
|
||||||
|
margin: auto;
|
||||||
|
text-align: center;
|
||||||
|
color: var(--color-language);
|
||||||
|
}
|
||||||
|
|
||||||
|
.language span {
|
||||||
|
display: block;
|
||||||
|
font-weight: bold;
|
||||||
|
font-size: 1.1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.machines {
|
||||||
|
clear: both;
|
||||||
|
margin-top: 1rem;
|
||||||
|
padding-top: 1rem;
|
||||||
|
}
|
||||||
|
.machine {
|
||||||
|
float: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.progress {
|
||||||
|
height: 20px;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
background-color: var(--color-bar-background);
|
||||||
|
border-radius: 4px;
|
||||||
|
border: solid 1px;
|
||||||
|
border-color: var(--color-bar-border);
|
||||||
|
}
|
||||||
|
|
||||||
|
.progress-bar {
|
||||||
|
float: left;
|
||||||
|
width: 0%;
|
||||||
|
height: 100%;
|
||||||
|
font-size: 12px;
|
||||||
|
line-height: 20px;
|
||||||
|
text-align: center;
|
||||||
|
background-color: var(--color-secondary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.progress-bar-new {
|
||||||
|
background-color: var(--color-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
</style>
|
||||||
|
<div class="profile">
|
||||||
|
<h3> \${profile.user} <sup>\${profile.level}</sup> \${profile.total_xp} xp
|
||||||
|
<% if( profile.new_xp > 0 ) { %>
|
||||||
|
<sup>+<%= profile.new_xp %></sup>
|
||||||
|
<% } %>
|
||||||
|
</h3>
|
||||||
|
|
||||||
|
<div class="progress">
|
||||||
|
<div class="progress-bar" style='width:\${profile.progress}%;'>
|
||||||
|
</div>
|
||||||
|
<div class="progress-bar progress-bar-new" style='width:\${profile.new_progress}%;'>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="languages">
|
||||||
|
<% for( let l in languages) { %>
|
||||||
|
<div class="language-progress">
|
||||||
|
<span class="tooltiptext">
|
||||||
|
<strong><%=languages[l].xp %> xp</strong>
|
||||||
|
<% if( languages[l].new_xp > 0 ) { %>
|
||||||
|
<sup>+<%= languages[l].new_xp %></sup>
|
||||||
|
<% } %>
|
||||||
|
</span>
|
||||||
|
<svg viewBox="0 0 100 100">
|
||||||
|
<circle class="backg" cx="50" cy="50" r="45" ></circle>
|
||||||
|
<circle class="newxp" cx="50" cy="50" r="45" stroke-dasharray="282.6" stroke-dashoffset='\${ ((100-languages[l].progress) * 282.6 / 100) }'></circle>
|
||||||
|
<circle class="oldxp" cx="50" cy="50" r="45" stroke-dasharray="282.6" stroke-dashoffset='\${ ((100-languages[l].progress + languages[l].new_progress) * 282.6 / 100) }'></circle>
|
||||||
|
</svg>
|
||||||
|
<div class="language">
|
||||||
|
<%= languages[l].name %>
|
||||||
|
<span>
|
||||||
|
<%= languages[l].level %>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<% } %>
|
||||||
|
</div>
|
||||||
|
<p/>
|
||||||
|
|
||||||
|
<div class="machines">
|
||||||
|
<% for( let m in machines) { %>
|
||||||
|
<div class="machine">
|
||||||
|
<strong>
|
||||||
|
<%= machines[m].name %>
|
||||||
|
</strong>
|
||||||
|
<sup> <%= machines[m].level %> </sup>
|
||||||
|
<%= machines[m].xp %> xp
|
||||||
|
<% if( machines[m].new_xp > 0 ) { %>
|
||||||
|
<sup>+<%= machines[m].new_xp %></sup>
|
||||||
|
<% } %>
|
||||||
|
<div class="progress">
|
||||||
|
<div class="progress-bar" style='width:\${machines[m].progress}%;'>
|
||||||
|
</div>
|
||||||
|
<div class="progress-bar progress-bar-new" style='width:\${machines[m].new_progress}%;'>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<% } %>
|
||||||
|
</div>
|
||||||
|
<div>`
|
@ -1,22 +1,12 @@
|
|||||||
|
import * as path from 'path'
|
||||||
import {
|
import {
|
||||||
Disposable,
|
commands, Disposable, ExtensionContext, StatusBarAlignment, StatusBarItem,
|
||||||
workspace,
|
TextDocument, TextDocumentChangeEvent, Uri,
|
||||||
window,
|
ViewColumn, window, workspace, WorkspaceConfiguration
|
||||||
Uri,
|
} from "vscode"
|
||||||
ViewColumn,
|
import { CodeStatsAPI } from "./code-stats-api"
|
||||||
commands,
|
import { ProfileProvider } from "./profile-provider"
|
||||||
StatusBarItem,
|
import { Pulse } from "./pulse"
|
||||||
TextDocument,
|
|
||||||
StatusBarAlignment,
|
|
||||||
TextDocumentChangeEvent,
|
|
||||||
WorkspaceConfiguration,
|
|
||||||
ExtensionContext
|
|
||||||
} from "vscode";
|
|
||||||
import { Pulse } from "./pulse";
|
|
||||||
import { CodeStatsAPI } from "./code-stats-api";
|
|
||||||
import { ProfileProvider } from "./profile-provider";
|
|
||||||
import * as path from 'path';
|
|
||||||
|
|
||||||
export class XpCounter {
|
export class XpCounter {
|
||||||
private combinedDisposable: Disposable;
|
private combinedDisposable: Disposable;
|
||||||
private statusBarItem: StatusBarItem;
|
private statusBarItem: StatusBarItem;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user