Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 21 additions & 38 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,7 @@ Simple Coding Time Tracker is a powerful extension for Visual Studio Code that h
- **Automatic Time Tracking**: Seamlessly tracks your coding time in the background.
- **Project-based Tracking**: Organizes time data by project for easy analysis.
- **Smart Activity Detection**: Automatically pauses tracking during periods of inactivity.
- **Status Bar Display**: Shows your today's total coding time duration in real-time.
- **Tooltip on Status Bar**: Shows the total coding time weekly, monthly and all time basis.
- **Focused Work Detection**: Intelligently tracks time even when VS Code isn't focused.
- **Interactive Data Visualization**:
- Project Summary Chart: Visual breakdown of time spent on each project
- Daily Activity Timeline: Interactive line chart showing your coding patterns
Expand All @@ -22,10 +21,6 @@ Simple Coding Time Tracker is a powerful extension for Visual Studio Code that h
- Project Filtering: Focus on specific projects
- Quick Reset: One-click reset for search filters
- **Data Persistence**: Safely stores your time data for long-term analysis.
- **Configurable Settings**:
- Save Interval: Customize how often your coding time data is saved (default: 5 seconds)
- Inactivity Timeout: Set how long to wait before stopping the timer when no activity is detected (default: 5 minutes)
- Focus Timeout: Set how long to continue tracking after VS Code loses focus (default: 60 seconds)

## Installation

Expand Down Expand Up @@ -56,24 +51,16 @@ You can customize the extension's behavior through VS Code settings:
- **Save Interval**: How often to save your coding time data (in seconds)
- Default: 5 seconds
- Lower values provide more frequent updates but may impact performance
- Higher values are more efficient but update less frequently
- **Inactivity Timeout**: How long to wait before stopping the timer when no activity is detected (in minutes)
- Default: 5 minutes
- Higher values are more efficient but update less frequently
- **Inactivity Timeout**: How long to wait before stopping the timer when no activity is detected but you are focused on VS Code (in seconds)
- Default: 150 seconds (2.5 minutes)
- Lower values will stop tracking sooner when you're not actively coding
- Higher values will continue tracking for longer during breaks
- **Focus Timeout**: How long to continue tracking after VS Code loses focus (in seconds)
- Default: 180 seconds (3 minutes)
- Determines how long to keep tracking when you switch to other applications
- Useful for when you're referencing documentation or testing your application

### Available Commands

The extension provides the following commands through the Command Palette:

- **Show Summary** (`SCTT: Show Coding Time Summary`):
Displays a comprehensive summary of your coding activity with interactive charts and visualizations.

- **Reset Timer for Today** (`SCTT: Reset Coding Timer for Today`):
Resets the coding time tracker for the current day, allowing you to start anew.

- **Reset All Timers** (`SCTT: Reset All Coding Timers`):
Resets all coding time trackers with a confirmation prompt to prevent unintended resets.

## Screenshots

Expand All @@ -85,44 +72,40 @@ The summary page provides a detailed report of your coding activity with interac
- Theme-aware visualizations that adapt to your VS Code theme
- Advanced search and filtering capabilities

![Coding Summary](./images/coding_summary.png)
![Coding Summary](https://raw.githubusercontent.com/twentyTwo/static-file-hosting/main/vsc-ext-coding-time-tracker-files/sctt-light.png)

#### Dark theme
![Coding Summary Dark Theme](./images/summry_blck.png)
![Coding Summary Dark Theme](https://raw.githubusercontent.com/twentyTwo/static-file-hosting/main/vsc-ext-coding-time-tracker-files/sctt-dark.png))

#### Filtering options
![Filter](./images/filter_summry.png)

#### Status Bar
Status bar resets to zero at midnight each day and hence shows the coding time for the current day.
![Status Bar](./images/statusbar.png)
![Status Bar](https://raw.githubusercontent.com/twentyTwo/static-file-hosting/main/vsc-ext-coding-time-tracker-files/statusbar.png)

#### Tooltip
Tooltip shows the total coding time weekly, monthly and all time basis.
![Tooltip](./images/tooltip.png)
![Tooltip](https://raw.githubusercontent.com/twentyTwo/static-file-hosting/main/vsc-ext-coding-time-tracker-files/tooltip.png)

#### Automatic Pause/Resume
When the user is inactive for a period of time, the timer automatically pauses and resumes when the user starts typing again coding again.
![Pause/Resume icon](./images/paused_time.png)
![Pause/Resume icon](https://raw.githubusercontent.com/twentyTwo/static-file-hosting/main/vsc-ext-coding-time-tracker-files/paused_time.png)

It is configurable from the settings. Default value is 5 minutes.
![Settings](./images/settings.png)
#### Settings
![Settings](https://raw.githubusercontent.com/twentyTwo/static-file-hosting/main/vsc-ext-coding-time-tracker-files/settings.png)

### All Command Palette Commands
There are total 3 commands in the command palette available for this extension.

1. SCTT: Show Coding Time Summary
2. SCTT: Reset Coding Timer for Today
3. SCTT: Reset All Coding Timers

![All Command Palette Commands](./images/commands.png)

## Technical Documentation

For technical details about development, release process, and internal architecture, please see [TECHNICAL.md](TECHNICAL.md).

## Changelog

### [0.3.9] - 2025-05-25
- Added Focus Timeout setting to intelligently track time when VS Code loses focus
- Fixed version tracking in GitHub Actions workflow to prevent publishing issues
- Updated documentation to clarify timeout settings and their purposes
- Enhanced error handling in the publishing workflow

### [0.3.4] - 2025-04-19
- Handle multi-root workspaces, external files, and virtual files more effectively.
- Added a verify-changes job to check if a version update is required and ensure non-documentation files are modified before publishing. This prevents unnecessary releases.
Expand Down
Binary file removed images/coding_summary.png
Binary file not shown.
Binary file removed images/commands.png
Binary file not shown.
Binary file removed images/filter_summry.png
Binary file not shown.
Binary file removed images/paused_time.png
Binary file not shown.
Binary file removed images/settings.png
Binary file not shown.
Binary file removed images/statusbar.png
Binary file not shown.
Binary file removed images/statusbar_click.png
Binary file not shown.
Binary file removed images/summry_blck.png
Binary file not shown.
Binary file removed images/tooltip.png
Binary file not shown.
111 changes: 80 additions & 31 deletions src/summaryView.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,20 @@
import * as vscode from 'vscode';
import { Database, SummaryData, TimeEntry } from './database';
import { ThemeIcon } from 'vscode';
import { formatTime } from './utils';
import { TimeTracker } from './timeTracker';
import * as vscode from "vscode";
import { Database, SummaryData, TimeEntry } from "./database";
import { ThemeIcon } from "vscode";
import { formatTime } from "./utils";
import { TimeTracker } from "./timeTracker";

export class SummaryViewProvider implements vscode.WebviewViewProvider {
private panel: vscode.WebviewPanel | undefined;
private context: vscode.ExtensionContext;
private database: Database;
private timeTracker: TimeTracker;

constructor(context: vscode.ExtensionContext, database: Database, timeTracker: TimeTracker) {
constructor(
context: vscode.ExtensionContext,
database: Database,
timeTracker: TimeTracker
) {
this.context = context;
this.database = database;
this.timeTracker = timeTracker;
Expand All @@ -23,16 +27,23 @@ export class SummaryViewProvider implements vscode.WebviewViewProvider {
): void | Thenable<void> {
webviewView.webview.options = {
enableScripts: true,
localResourceRoots: [this.context.extensionUri]
localResourceRoots: [this.context.extensionUri],
};

webviewView.webview.onDidReceiveMessage(
async message => {
if (message.command === 'refresh') {
async (message) => {
if (message.command === "refresh") {
await this.show(webviewView.webview);
} else if (message.command === 'search') {
const searchResults = await this.database.searchEntries(message.startDate, message.endDate, message.project);
webviewView.webview.postMessage({ command: 'searchResult', data: searchResults });
} else if (message.command === "search") {
const searchResults = await this.database.searchEntries(
message.startDate,
message.endDate,
message.project
);
webviewView.webview.postMessage({
command: "searchResult",
data: searchResults,
});
}
},
undefined,
Expand All @@ -47,39 +58,57 @@ export class SummaryViewProvider implements vscode.WebviewViewProvider {
const projects = await this.getUniqueProjects();
const totalTime = {
today: formatTime(this.timeTracker.getTodayTotal()),
yesterday: formatTime(this.timeTracker.getYesterdayTotal()),
weekly: formatTime(this.timeTracker.getWeeklyTotal()),
monthly: formatTime(this.timeTracker.getMonthlyTotal()),
yearly: formatTime(this.timeTracker.getYearlyTotal()), // Add this line
allTime: formatTime(this.timeTracker.getAllTimeTotal())
allTime: formatTime(this.timeTracker.getAllTimeTotal()),
};

if (webview) {
webview.html = this.getHtmlForWebview(projects);
webview.postMessage({ command: 'update', data: summaryData, projects: projects, totalTime: totalTime });
webview.postMessage({
command: "update",
data: summaryData,
projects: projects,
totalTime: totalTime,
});
} else if (this.panel) {
this.panel.reveal();
this.panel.webview.html = this.getHtmlForWebview(projects);
this.panel.webview.postMessage({ command: 'update', data: summaryData, projects: projects, totalTime: totalTime });
this.panel.webview.postMessage({
command: "update",
data: summaryData,
projects: projects,
totalTime: totalTime,
});
} else {
this.panel = vscode.window.createWebviewPanel(
'codingTimeSummary',
'Coding Time Summary',
"codingTimeSummary",
"Coding Time Summary",
vscode.ViewColumn.One,
{
enableScripts: true,
retainContextWhenHidden: true
retainContextWhenHidden: true,
}
);

this.panel.webview.html = this.getHtmlForWebview(projects);

this.panel.webview.onDidReceiveMessage(
async message => {
if (message.command === 'refresh') {
async (message) => {
if (message.command === "refresh") {
await this.show(this.panel?.webview);
} else if (message.command === 'search') {
const searchResults = await this.database.searchEntries(message.startDate, message.endDate, message.project);
this.panel?.webview.postMessage({ command: 'searchResult', data: searchResults });
} else if (message.command === "search") {
const searchResults = await this.database.searchEntries(
message.startDate,
message.endDate,
message.project
);
this.panel?.webview.postMessage({
command: "searchResult",
data: searchResults,
});
}
},
undefined,
Expand All @@ -90,33 +119,48 @@ export class SummaryViewProvider implements vscode.WebviewViewProvider {
this.panel = undefined;
});

this.panel.webview.postMessage({ command: 'update', data: summaryData, projects: projects, totalTime: totalTime });
this.panel.webview.postMessage({
command: "update",
data: summaryData,
projects: projects,
totalTime: totalTime,
});
}
}

// Modify the updateContent method to accept a webview parameter
private async updateContent(webview?: vscode.Webview) {
const summaryData = await this.database.getSummaryData();
const projects = await this.getUniqueProjects();

if (webview) {
webview.html = this.getHtmlForWebview(projects);
webview.postMessage({ command: 'update', data: summaryData, projects: projects });
webview.postMessage({
command: "update",
data: summaryData,
projects: projects,
});
} else if (this.panel) {
this.panel.webview.html = this.getHtmlForWebview(projects);
this.panel.webview.postMessage({ command: 'update', data: summaryData, projects: projects });
this.panel.webview.postMessage({
command: "update",
data: summaryData,
projects: projects,
});
}
}

private async getUniqueProjects(): Promise<string[]> {
const entries = await this.database.getEntries();
const projectSet = new Set(entries.map(entry => entry.project));
const projectSet = new Set(entries.map((entry) => entry.project));
return Array.from(projectSet).sort();
}

private getHtmlForWebview(projects: string[]): string {
const projectOptions = projects.map(project => `<option value="${project}">${project}</option>`).join('');

const projectOptions = projects
.map((project) => `<option value="${project}">${project}</option>`)
.join("");

return `
<!DOCTYPE html>
<html lang="en">
Expand Down Expand Up @@ -388,6 +432,10 @@ export class SummaryViewProvider implements vscode.WebviewViewProvider {
<h3>Today</h3>
<p id="today-total">Loading...</p>
</div>
<div class="total-time-item">
<h3>Yesterday</h3>
<p id="yesterday-total">Loading...</p>
</div>
<div class="total-time-item">
<h3>This Week</h3>
<p id="weekly-total">Loading...</p>
Expand Down Expand Up @@ -575,6 +623,7 @@ export class SummaryViewProvider implements vscode.WebviewViewProvider {

function updateTotalTimeSection(totalTime) {
document.getElementById('today-total').textContent = totalTime.today;
document.getElementById('yesterday-total').textContent = totalTime.yesterday;
document.getElementById('weekly-total').textContent = totalTime.weekly;
document.getElementById('monthly-total').textContent = totalTime.monthly;
document.getElementById('yearly-total').textContent = totalTime.yearly;
Expand Down Expand Up @@ -1145,4 +1194,4 @@ export class SummaryViewProvider implements vscode.WebviewViewProvider {
</html>
`;
}
}
}
10 changes: 10 additions & 0 deletions src/timeTracker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,16 @@ export class TimeTracker implements vscode.Disposable {
return todayTotal;
}

getYesterdayTotal(): number {
const yesterday = this.getLocalDateString( new Date(Date.now() - 24 * 60 * 60 * 1000));
const entries = this.database.getEntries();
const yesterdayTotal = entries
.filter((entry: TimeEntry) => entry.date === yesterday)
.reduce((sum: number, entry: TimeEntry) => sum + entry.timeSpent, 0);

return yesterdayTotal;
}

getCurrentProjectTime(): number {
const today = this.getLocalDateString(new Date());
const currentProject = this.getCurrentProject();
Expand Down