Compare commits
10
Commits
02a7eb8090
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ca32117a8a | ||
|
|
ecfa56bf5a | ||
|
|
77909c5684 | ||
|
|
663c085f34 | ||
|
|
272812a815 | ||
|
|
fd01ccf8af | ||
|
|
7877651ab0 | ||
|
|
7f0aa63843 | ||
|
|
9d72732cfd | ||
|
|
2ef3cae6eb |
Vendored
+2
-1
@@ -7,7 +7,8 @@
|
|||||||
"request": "launch",
|
"request": "launch",
|
||||||
"runtimeExecutable": "${execPath}",
|
"runtimeExecutable": "${execPath}",
|
||||||
"args": ["--extensionDevelopmentPath=${workspaceFolder}"],
|
"args": ["--extensionDevelopmentPath=${workspaceFolder}"],
|
||||||
"outFiles": ["${workspaceFolder}/out/**/*.js"]
|
"outFiles": ["${workspaceFolder}/out/**/*.js"],
|
||||||
|
"preLaunchTask": "npm: compile"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
Vendored
+12
@@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"version": "2.0.0",
|
||||||
|
"tasks": [
|
||||||
|
{
|
||||||
|
"label": "npm: compile",
|
||||||
|
"type": "shell",
|
||||||
|
"command": "npm run compile",
|
||||||
|
"group": "build",
|
||||||
|
"problemMatcher": []
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -1,2 +1,5 @@
|
|||||||
# extension
|
# extension
|
||||||
|
|
||||||
The VSCode extension for SpeedCode
|
The VSCode extension for SpeedCode
|
||||||
|
|
||||||
|
All the problems and their test cases [here](https://github.com/SpeedCodeHQ/problems)
|
||||||
|
|||||||
Generated
+18
@@ -8,6 +8,7 @@
|
|||||||
"name": "speed-code",
|
"name": "speed-code",
|
||||||
"version": "0.0.1",
|
"version": "0.0.1",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@types/node": "^24.2.0",
|
||||||
"@types/vscode": "^1.70.0",
|
"@types/vscode": "^1.70.0",
|
||||||
"tslint": "^6.0.0",
|
"tslint": "^6.0.0",
|
||||||
"typescript": "^5.0.0"
|
"typescript": "^5.0.0"
|
||||||
@@ -41,6 +42,16 @@
|
|||||||
"node": ">=6.9.0"
|
"node": ">=6.9.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@types/node": {
|
||||||
|
"version": "24.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/node/-/node-24.2.0.tgz",
|
||||||
|
"integrity": "sha512-3xyG3pMCq3oYCNg7/ZP+E1ooTaGB4cG8JWRsqqOYQdbWNY4zbaV0Ennrd7stjiJEFZCaybcIgpTjJWHRfBSIDw==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"undici-types": "~7.10.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@types/vscode": {
|
"node_modules/@types/vscode": {
|
||||||
"version": "1.102.0",
|
"version": "1.102.0",
|
||||||
"resolved": "https://registry.npmjs.org/@types/vscode/-/vscode-1.102.0.tgz",
|
"resolved": "https://registry.npmjs.org/@types/vscode/-/vscode-1.102.0.tgz",
|
||||||
@@ -497,6 +508,13 @@
|
|||||||
"node": ">=14.17"
|
"node": ">=14.17"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/undici-types": {
|
||||||
|
"version": "7.10.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.10.0.tgz",
|
||||||
|
"integrity": "sha512-t5Fy/nfn+14LuOc2KNYg75vZqClpAiqscVvMygNnlsHBFpSXdJaYtXMcdNLpl/Qvc3P2cB3s6lOV51nqsFq4ag==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/wrappy": {
|
"node_modules/wrappy": {
|
||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
|
||||||
|
|||||||
+37
-4
@@ -6,24 +6,57 @@
|
|||||||
"engines": {
|
"engines": {
|
||||||
"vscode": "^1.70.0"
|
"vscode": "^1.70.0"
|
||||||
},
|
},
|
||||||
"activationEvents": [],
|
"activationEvents": [
|
||||||
|
"onStartupFinished"
|
||||||
|
],
|
||||||
"main": "./out/extension.js",
|
"main": "./out/extension.js",
|
||||||
"contributes": {
|
"contributes": {
|
||||||
"commands": [
|
"commands": [
|
||||||
{
|
{
|
||||||
"command": "speed-code.startSpeedRun",
|
"command": "speed-code.startSpeedRun",
|
||||||
"title": "Hello World"
|
"title": "Start Speed Run"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"command": "speed-code.cancelSpeedRun",
|
||||||
|
"title": "Cancel Speed Run"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"keybindings": [
|
||||||
|
{
|
||||||
|
"command": "speed-code.submitSpeedRun",
|
||||||
|
"key": "shift+enter",
|
||||||
|
"when": "editorTextFocus"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"viewsContainers": {
|
||||||
|
"panel": [
|
||||||
|
{
|
||||||
|
"id": "speedCodeResultPanel",
|
||||||
|
"title": "Speed Code",
|
||||||
|
"icon": "./timer.svg"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
"views": {
|
||||||
|
"speedCodeResultPanel": [
|
||||||
|
{
|
||||||
|
"type": "tree",
|
||||||
|
"id": "speedCodeResultTab",
|
||||||
|
"name": "Speed Code",
|
||||||
|
"icon": "./timer.svg"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"vscode:prepublish": "tsc -p ./",
|
"vscode:prepublish": "tsc -p ./",
|
||||||
"compile": "tsc -p ./",
|
"compile": "tsc -p ./",
|
||||||
"watch": "tsc -watch -p ./"
|
"watch": "tsc -watch -p ./"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@types/node": "^24.2.0",
|
||||||
"@types/vscode": "^1.70.0",
|
"@types/vscode": "^1.70.0",
|
||||||
"typescript": "^5.0.0",
|
"tslint": "^6.0.0",
|
||||||
"tslint": "^6.0.0"
|
"typescript": "^5.0.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+178
-7
@@ -1,11 +1,182 @@
|
|||||||
import * as vscode from 'vscode';
|
import * as vscode from "vscode";
|
||||||
|
import os from "os";
|
||||||
|
import path from "path";
|
||||||
|
import fs from "fs";
|
||||||
|
import testCode, { TestCase } from "./tester";
|
||||||
|
import * as utils from "./utils";
|
||||||
|
import { MyTreeDataProvider } from "./resultViewer";
|
||||||
|
|
||||||
export function activate(context: vscode.ExtensionContext) {
|
let resetTimer = () => {};
|
||||||
const disposable = vscode.commands.registerCommand('speed-code.startSpeedRun', () => {
|
|
||||||
vscode.window.showInformationMessage('Hello World from My Extension!');
|
|
||||||
});
|
|
||||||
|
|
||||||
context.subscriptions.push(disposable);
|
const speedCodePath = path.join(os.homedir(), ".speed-code/");
|
||||||
|
|
||||||
|
if (!fs.existsSync(speedCodePath)) {
|
||||||
|
fs.mkdirSync(speedCodePath);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function deactivate() {}
|
export async function openAndFocusFile(filePath: string) {
|
||||||
|
const document = await vscode.workspace.openTextDocument(filePath);
|
||||||
|
await vscode.window.showTextDocument(document, {
|
||||||
|
preview: false,
|
||||||
|
preserveFocus: false,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const userCode = `
|
||||||
|
function isPalindrome(s) {
|
||||||
|
return s === s.split('').reverse().join('');
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
const treeDataProvider = new MyTreeDataProvider();
|
||||||
|
|
||||||
|
const treeView = vscode.window.createTreeView("speedCodeResultTab", {
|
||||||
|
treeDataProvider,
|
||||||
|
});
|
||||||
|
|
||||||
|
export function activate(context: vscode.ExtensionContext) {
|
||||||
|
const statusBarItem = vscode.window.createStatusBarItem(
|
||||||
|
vscode.StatusBarAlignment.Left,
|
||||||
|
100
|
||||||
|
);
|
||||||
|
statusBarItem.tooltip = "Speedrun timer";
|
||||||
|
statusBarItem.text = "No active Speedrun";
|
||||||
|
statusBarItem.show();
|
||||||
|
|
||||||
|
let run:
|
||||||
|
| undefined
|
||||||
|
| {
|
||||||
|
path: string;
|
||||||
|
name: string;
|
||||||
|
testCases: TestCase[];
|
||||||
|
elapsed: number;
|
||||||
|
timer: NodeJS.Timeout;
|
||||||
|
};
|
||||||
|
|
||||||
|
resetTimer = () => {
|
||||||
|
if (run) {
|
||||||
|
clearInterval(run.timer);
|
||||||
|
run = undefined;
|
||||||
|
}
|
||||||
|
statusBarItem.text = "No active Speedrun";
|
||||||
|
};
|
||||||
|
|
||||||
|
resetTimer();
|
||||||
|
updateTreeMessage();
|
||||||
|
|
||||||
|
context.subscriptions.push(
|
||||||
|
vscode.commands.registerCommand("speed-code.submitSpeedRun", () => {
|
||||||
|
if (run) {
|
||||||
|
try {
|
||||||
|
const [isValid, res] = testCode(
|
||||||
|
fs.readFileSync(run.path).toString(),
|
||||||
|
run.testCases
|
||||||
|
);
|
||||||
|
|
||||||
|
treeDataProvider.testResults = res;
|
||||||
|
treeDataProvider.testSummary = {
|
||||||
|
elapsed: run.elapsed,
|
||||||
|
valid: isValid,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (isValid) {
|
||||||
|
resetTimer();
|
||||||
|
} else {
|
||||||
|
treeDataProvider.blunders.push([run.elapsed, res]);
|
||||||
|
}
|
||||||
|
|
||||||
|
treeDataProvider.refresh();
|
||||||
|
updateTreeMessage();
|
||||||
|
} catch (e) {
|
||||||
|
treeDataProvider.blunders.push([run.elapsed, String(e)]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
context.subscriptions.push(
|
||||||
|
vscode.commands.registerCommand("speed-code.startSpeedRun", async () => {
|
||||||
|
const quickPick = vscode.window.createQuickPick();
|
||||||
|
quickPick.placeholder = "Type to search";
|
||||||
|
quickPick.matchOnDescription = true;
|
||||||
|
quickPick.items = utils.problems.map((label) => ({ label }));
|
||||||
|
quickPick.onDidChangeValue((value) => {
|
||||||
|
quickPick.items = utils.problems
|
||||||
|
.filter((prob) => prob.toLowerCase().includes(value.toLowerCase()))
|
||||||
|
.map((label) => ({ label }));
|
||||||
|
});
|
||||||
|
quickPick.onDidAccept(async () => {
|
||||||
|
const selection = quickPick.selectedItems[0];
|
||||||
|
if (selection) {
|
||||||
|
const name = selection.label.toLowerCase().replace(' ', '-');
|
||||||
|
treeDataProvider.testSummary = undefined;
|
||||||
|
treeDataProvider.testResults = [];
|
||||||
|
treeDataProvider.blunders = [];
|
||||||
|
treeDataProvider.refresh();
|
||||||
|
const runFilePath = path.join(speedCodePath, name.replace('-', '_') + ".js");
|
||||||
|
fs.writeFileSync(runFilePath, "");
|
||||||
|
openAndFocusFile(runFilePath);
|
||||||
|
|
||||||
|
const response = await fetch(
|
||||||
|
`https://raw.githubusercontent.com/SpeedCodeHQ/problems/refs/heads/main/problems/${name}.json`
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
vscode.window.showErrorMessage(
|
||||||
|
`Failed to fetch test cases for ${name}: ${response.statusText}`
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const startTime = Date.now();
|
||||||
|
run = {
|
||||||
|
path: runFilePath,
|
||||||
|
elapsed: 0,
|
||||||
|
name,
|
||||||
|
testCases: (await response.json()) as TestCase[],
|
||||||
|
timer: setInterval(() => {
|
||||||
|
if (!run) return;
|
||||||
|
const currentTime = Date.now();
|
||||||
|
run.elapsed = (currentTime - startTime) / 1000;
|
||||||
|
statusBarItem.text = utils.format(run.elapsed);
|
||||||
|
statusBarItem.color = "#fff";
|
||||||
|
}, 10),
|
||||||
|
};
|
||||||
|
|
||||||
|
updateTreeMessage();
|
||||||
|
vscode.window.showInformationMessage("Speedrun Started!");
|
||||||
|
}
|
||||||
|
quickPick.hide();
|
||||||
|
});
|
||||||
|
quickPick.show();
|
||||||
|
}),
|
||||||
|
statusBarItem
|
||||||
|
);
|
||||||
|
|
||||||
|
context.subscriptions.push(
|
||||||
|
vscode.commands.registerCommand("speed-code.cancelSpeedRun", () => {
|
||||||
|
resetTimer();
|
||||||
|
treeDataProvider.testSummary = undefined;
|
||||||
|
treeDataProvider.testResults = [];
|
||||||
|
treeDataProvider.blunders = [];
|
||||||
|
treeDataProvider.refresh();
|
||||||
|
}),
|
||||||
|
statusBarItem
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function deactivate() {
|
||||||
|
resetTimer();
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateTreeMessage() {
|
||||||
|
if (
|
||||||
|
!treeDataProvider.testResults.length &&
|
||||||
|
!treeDataProvider.blunders.length &&
|
||||||
|
!treeDataProvider.testSummary
|
||||||
|
) {
|
||||||
|
treeView.message = "No results yet. Start a speedrun!";
|
||||||
|
} else {
|
||||||
|
treeView.message = "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -0,0 +1,116 @@
|
|||||||
|
import * as vscode from "vscode";
|
||||||
|
import { TestResult } from "./tester";
|
||||||
|
import { format } from "./utils";
|
||||||
|
|
||||||
|
class RunTreeItem extends vscode.TreeItem {
|
||||||
|
children?: RunTreeItem[];
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
label: string | vscode.TreeItemLabel,
|
||||||
|
description?: string,
|
||||||
|
children?: RunTreeItem[],
|
||||||
|
expanded: boolean = false
|
||||||
|
) {
|
||||||
|
super(
|
||||||
|
label,
|
||||||
|
children
|
||||||
|
? expanded
|
||||||
|
? vscode.TreeItemCollapsibleState.Expanded
|
||||||
|
: vscode.TreeItemCollapsibleState.Collapsed
|
||||||
|
: vscode.TreeItemCollapsibleState.None
|
||||||
|
);
|
||||||
|
this.children = children;
|
||||||
|
this.description = description;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class MyTreeDataProvider
|
||||||
|
implements vscode.TreeDataProvider<RunTreeItem>
|
||||||
|
{
|
||||||
|
private _onDidChangeTreeData: vscode.EventEmitter<
|
||||||
|
RunTreeItem | undefined | void
|
||||||
|
> = new vscode.EventEmitter();
|
||||||
|
readonly onDidChangeTreeData: vscode.Event<RunTreeItem | undefined | void> =
|
||||||
|
this._onDidChangeTreeData.event;
|
||||||
|
|
||||||
|
testResults: TestResult[] = [];
|
||||||
|
testSummary: undefined | { elapsed: number; valid: boolean };
|
||||||
|
blunders: [number, string | TestResult[]][] = [];
|
||||||
|
|
||||||
|
refresh(): void {
|
||||||
|
this._onDidChangeTreeData.fire();
|
||||||
|
}
|
||||||
|
|
||||||
|
getTreeItem(element: RunTreeItem): RunTreeItem {
|
||||||
|
return element;
|
||||||
|
}
|
||||||
|
|
||||||
|
getChildren(element?: RunTreeItem): Thenable<RunTreeItem[]> {
|
||||||
|
if (!element) {
|
||||||
|
const items: RunTreeItem[] = [];
|
||||||
|
|
||||||
|
if (this.testSummary) {
|
||||||
|
items.push(
|
||||||
|
new RunTreeItem(
|
||||||
|
`Speedrun Summary`,
|
||||||
|
undefined,
|
||||||
|
[
|
||||||
|
new RunTreeItem("Time Elapsed", format(this.testSummary.elapsed)),
|
||||||
|
new RunTreeItem(
|
||||||
|
"Test Passed",
|
||||||
|
this.testSummary.valid ? "✅ Passed" : "❌ Failed"
|
||||||
|
),
|
||||||
|
new RunTreeItem("Blunders", `${this.blunders.length}`),
|
||||||
|
],
|
||||||
|
true
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.blunders.length > 0) {
|
||||||
|
items.push(
|
||||||
|
new RunTreeItem(
|
||||||
|
`Blunders`,
|
||||||
|
undefined,
|
||||||
|
this.blunders.map((blunder, i) =>
|
||||||
|
typeof blunder[1] === "string"
|
||||||
|
? new RunTreeItem(blunder[1], format(blunder[0]))
|
||||||
|
: new RunTreeItem(
|
||||||
|
`Output Blunder ${i + 1}`,
|
||||||
|
format(blunder[0]),
|
||||||
|
blunder[1].map(
|
||||||
|
(t) =>
|
||||||
|
new RunTreeItem(
|
||||||
|
`(${JSON.stringify(t.input)})`,
|
||||||
|
undefined,
|
||||||
|
[
|
||||||
|
new RunTreeItem("Output", `${t.result}`),
|
||||||
|
new RunTreeItem("Expected Output", `${t.expected}`),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.testResults.forEach((t) => {
|
||||||
|
const item = new RunTreeItem(
|
||||||
|
`(${JSON.stringify(t.input)})`,
|
||||||
|
undefined,
|
||||||
|
[
|
||||||
|
new RunTreeItem("Output", `${t.result}`),
|
||||||
|
new RunTreeItem("Expected Output", `${t.expected}`),
|
||||||
|
]
|
||||||
|
);
|
||||||
|
item.description = t.valid ? "✅ Passed" : "❌ Failed";
|
||||||
|
items.push(item);
|
||||||
|
});
|
||||||
|
|
||||||
|
return Promise.resolve(items);
|
||||||
|
} else {
|
||||||
|
return Promise.resolve(element.children || []);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,51 @@
|
|||||||
|
import vm from "vm";
|
||||||
|
import assert from "assert";
|
||||||
|
|
||||||
|
export interface TestCase {
|
||||||
|
input: any[];
|
||||||
|
expected: any;
|
||||||
|
}
|
||||||
|
export interface TestResult extends TestCase {
|
||||||
|
result: any;
|
||||||
|
valid: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function testCode(
|
||||||
|
code: string,
|
||||||
|
testCases: TestCase[]
|
||||||
|
): [boolean, TestResult[]] {
|
||||||
|
const context: Record<string, any> = {};
|
||||||
|
vm.createContext(context);
|
||||||
|
|
||||||
|
const beforeKeys = new Set(Object.keys(context));
|
||||||
|
|
||||||
|
const script = new vm.Script(code);
|
||||||
|
script.runInContext(context);
|
||||||
|
|
||||||
|
const afterKeys = Object.keys(context);
|
||||||
|
const newKeys = afterKeys.filter((k) => !beforeKeys.has(k));
|
||||||
|
|
||||||
|
// Find the last function defined
|
||||||
|
const funcs = newKeys
|
||||||
|
.map((k) => context[k])
|
||||||
|
.filter((v) => typeof v === "function");
|
||||||
|
|
||||||
|
if (funcs.length === 0) {
|
||||||
|
throw new Error("No function was defined in the code.");
|
||||||
|
}
|
||||||
|
|
||||||
|
const targetFn = funcs[funcs.length - 1];
|
||||||
|
|
||||||
|
const tests = testCases.map((t) => {
|
||||||
|
const result = targetFn(...t.input);
|
||||||
|
|
||||||
|
try {
|
||||||
|
assert.strictEqual(result, t.expected);
|
||||||
|
return { ...t, valid: true, result };
|
||||||
|
} catch {
|
||||||
|
return { ...t, valid: false, result };
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return [tests.every((t) => t.valid), tests];
|
||||||
|
}
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
export const problems = ["Two Sum", "Palindrome", "Fibonacci", "Factorial", "Find Missing Number", "Anagram", "Reverse Words In a Sentence"];
|
||||||
|
|
||||||
|
export function format(timeSeconds: number): string {
|
||||||
|
if (timeSeconds > 86400) {
|
||||||
|
const days = Math.floor(timeSeconds / 86400);
|
||||||
|
const hours = Math.floor(timeSeconds / 3600);
|
||||||
|
const minutes = Math.floor(timeSeconds / 60);
|
||||||
|
return `${days}:${hours - days * 24}:${minutes - hours * 60}:${(
|
||||||
|
timeSeconds -
|
||||||
|
minutes * 60
|
||||||
|
).toFixed(3)}`;
|
||||||
|
} else if (timeSeconds >= 3600) {
|
||||||
|
const hours = Math.floor(timeSeconds / 3600);
|
||||||
|
const minutes = Math.floor(timeSeconds / 60);
|
||||||
|
return `${hours}:${minutes - hours * 60}:${(
|
||||||
|
timeSeconds -
|
||||||
|
minutes * 60
|
||||||
|
).toFixed(3)}`;
|
||||||
|
} else if (timeSeconds >= 60) {
|
||||||
|
const minutes = Math.floor(timeSeconds / 60);
|
||||||
|
return `${minutes}:${(timeSeconds - minutes * 60).toFixed(3)}`;
|
||||||
|
} else {
|
||||||
|
return `${timeSeconds}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-timer-icon lucide-timer"><line x1="10" x2="14" y1="2" y2="2"/><line x1="12" x2="15" y1="14" y2="11"/><circle cx="12" cy="14" r="8"/></svg>
|
||||||
|
After Width: | Height: | Size: 340 B |
+2
-1
@@ -7,7 +7,8 @@
|
|||||||
"rootDir": "src",
|
"rootDir": "src",
|
||||||
"strict": true,
|
"strict": true,
|
||||||
"esModuleInterop": true,
|
"esModuleInterop": true,
|
||||||
"skipLibCheck": true
|
"skipLibCheck": true,
|
||||||
|
"types": ["node", "vscode"]
|
||||||
},
|
},
|
||||||
"exclude": ["node_modules", ".vscode-test"]
|
"exclude": ["node_modules", ".vscode-test"]
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user