p-doom/crowd-code-dataset-0.2 huggingface dataset:
[content] package.nls.json (json)
{
	"displayName": "crowd-code",
	"description": "Record the code in SRT and JSON format",
	"command.startRecording.title": "crowd-code: Start Recording",
	"command.stopRecording.title": "crowd-code: Stop Recording",
	"command.openSettings.title": "crowd-code: Open Settings",
	"command.refreshRecordFiles.title": "Refresh",
	"command.addToGitignore.title": "Add to .gitignore",
	"command.showUserId.title": "crowd-code: Show User ID",
	"config.title": "crowd-code",
	"config.exportPath.description": "Set the export path. Use `${workspaceFolder}` to export to the workspace folder. In case the path does not exist in the workspace, it will be created.",
	"config.createPathOutsideWorkspace.description": "Create the export path outside the workspace",
	"config.addToGitignore.description": "Add the export path to .gitignore when creating the folder",
	"config.exportFormats.description": "Select the formats to export recording data",
	"config.minimalMode.description": "Enable minimal mode. Display only icons and no text",
	"config.showTimer.description": "Show timer. The timer will be displayed even in minimal mode",
	"config.askFolderName.description": "Ask for a custom folder name before starting a recording",
	"view.recordFiles.name": "Recorded Files",
	"view.recordFiles.contextualTitle": "Recorded Files",
	"view.actions.name": "Actions",
	"view.actions.contextualTitle": "Actions",
	"command.deleteRecordFile.title": "Delete",
	"command.revealInExplorer.title": "Reveal in File Explorer",
	"dialog.deleteConfirmation": "Are you sure you want to delete {name}?",
	"dialog.yes": "Yes",
	"dialog.no": "No",
	"dialog.enterFolderName.prompt": "Enter a name for the recording folder",
	"dialog.enterFolderName.placeholder": "Enter recording folder name",
	"error.noWorkspace": "No workspace folder found",
	"error.noExportPath": "No export path specified"
[content] README.md (markdown)

The extension will automatically record changes in your text editor. When you stop the recording, it will finalize the data and save it to a CSV (source), JSON and SRT files.

You can customize the recording experience with these features:

- Choose the export formats (SRT or JSON or both)
- Set custom names for recording folders
- Automatically add the export path to .gitignore

You can also use the command palette to access the extension's features.
Available commands:

- `vs-code-recorder.startRecording`: Start the recording
- `vs-code-recorder.stopRecording`: Stop the recording
- `vs-code-recorder.openSettings`: Open the extension settings

## 📄 Output

The recorded changes are saved in a CSV file in your workspace.

Then, this file is processed to generate output files in SRT and JSON formats, providing a detailed and accessible log of your coding session.

## ▶️ Play it back!

Playback is a feature by the upstream repository. We have not tested playback using our modified repository (e.g. cursor movement and terminal capture are not implemented upstream; chances are high that playback simply breaks using recordings captured by crowd-code). If you want to try this nonetheless:

- The output files can be played back in the [VS Code Recorder Player web app](https://github.com/mattia-consiglio/vs-code-recorder-player).
- 🚧 React component available soon...

## 🔧 Extension Settings

- `vsCodeRecorder.export.exportPath`: Set the export path. Use `${workspaceFolder}` to export to the workspace folder. In case the path does not exist in the workspace, it will be created.

  Default: `${workspaceFolder}/vs-code-recorder/`

- `vsCodeRecorder.export.createPathOutsideWorkspace`: Create the export path outside the workspace if it doesn't exist

  Default: `false`

- `vsCodeRecorder.export.addToGitignore`: Add the export path to .gitignore when creating the folder

  Default: `false`

- `vsCodeRecorder.export.exportFormats`: Enabled export formats (SRT or JSON or both)

  Default: `["JSON", "SRT"]`

- `vsCodeRecorder.recording.askFolderName`: Ask for a custom folder name before starting a recording

  Default: `false`

- `vsCodeRecorder.appearance.minimalMode`: Enable or disable the minimal mode

  Default: `false`

- `vsCodeRecorder.appearance.showTimer`: Enable or disable the display time
[tab] package.nls.json (json)
{
	"displayName": "crowd-code",
	"description": "Record the code in SRT and JSON format",
	"command.startRecording.title": "crowd-code: Start Recording",
	"command.stopRecording.title": "crowd-code: Stop Recording",
	"command.openSettings.title": "crowd-code: Open Settings",
	"command.refreshRecordFiles.title": "Refresh",
	"command.addToGitignore.title": "Add to .gitignore",
	"command.showUserId.title": "crowd-code: Show User ID",
	"config.title": "crowd-code",
	"config.exportPath.description": "Set the export path. Use `${workspaceFolder}` to export to the workspace folder. In case the path does not exist in the workspace, it will be created.",
	"config.createPathOutsideWorkspace.description": "Create the export path outside the workspace",
	"config.addToGitignore.description": "Add the export path to .gitignore when creating the folder",
	"config.exportFormats.description": "Select the formats to export recording data",
	"config.minimalMode.description": "Enable minimal mode. Display only icons and no text",
	"config.showTimer.description": "Show timer. The timer will be displayed even in minimal mode",
	"config.askFolderName.description": "Ask for a custom folder name before starting a recording",
	"view.recordFiles.name": "Recorded Files",
	"view.recordFiles.contextualTitle": "Recorded Files",
	"view.actions.name": "Actions",
	"view.actions.contextualTitle": "Actions",
	"command.deleteRecordFile.title": "Delete",
	"command.revealInExplorer.title": "Reveal in File Explorer",
	"dialog.deleteConfirmation": "Are you sure you want to delete {name}?",
	"dialog.yes": "Yes",
	"dialog.no": "No",
	"dialog.enterFolderName.prompt": "Enter a name for the recording folder",
	"dialog.enterFolderName.placeholder": "Enter recording folder name",
	"error.noWorkspace": "No workspace folder found",
	"error.noExportPath": "No export path specified"
}
[content] package.nls.json (json)
{
	"displayName": "crowd-code",
	"description": "Record the code in SRT and JSON format",
	"command.startRecording.title": "crowd-code: Start Recording",
	"command.stopRecording.title": "crowd-code: Stop Recording",
	"command.openSettings.title": "crowd-code: Open Settings",
	"command.refreshRecordFiles.title": "Refresh",
	"command.addToGitignore.title": "Add to .gitignore",
	"command.showUserId.title": "crowd-code: Show User ID",
	"config.title": "crowd-code",
	"config.exportPath.description": "Set the export path. Use `${workspaceFolder}` to export to the workspace folder. In case the path does not exist in the workspace, it will be created.",
	"config.createPathOutsideWorkspace.description": "Create the export path outside the workspace",
	"config.addToGitignore.description": "Add the export path to .gitignore when creating the folder",
	"config.exportFormats.description": "Select the formats to export recording data",
	"config.minimalMode.description": "Enable minimal mode. Display only icons and no text",
	"config.showTimer.description": "Show timer. The timer will be displayed even in minimal mode",
	"config.askFolderName.description": "Ask for a custom folder name before starting a recording",
	"view.recordFiles.name": "Recorded Files",
	"view.recordFiles.contextualTitle": "Recorded Files",
	"view.actions.name": "Actions",
	"view.actions.contextualTitle": "Actions",
	"command.deleteRecordFile.title": "Delete",
	"command.revealInExplorer.title": "Reveal in File Explorer",
	"dialog.deleteConfirmation": "Are you sure you want to delete {name}?",
	"dialog.yes": "Yes",
	"dialog.no": "No",
	"dialog.enterFolderName.prompt": "Enter a name for the recording folder",
	"dialog.enterFolderName.placeholder": "Enter recording folder name",
	"error.noWorkspace": "No workspace folder found",
	"error.noExportPath": "No export path specified"
}
[content] src/recording.ts (typescript)
        logToOutput(vscode.l10n.t('No active recording to panic'), 'info')
        return
    }

    if (!recording.startDateTime) {
        vscode.window.showErrorMessage(vscode.l10n.t('Recording start time not available'))
        logToOutput(vscode.l10n.t('Recording start time not available'), 'error')
        return
    }

    const exportPath = getExportPath()
    if (!exportPath) {
        vscode.window.showErrorMessage(vscode.l10n.t('Export path not available'))
        logToOutput(vscode.l10n.t('Export path not available'), 'error')
        return
    }

    const baseFilePath = generateBaseFilePath(recording.startDateTime, false, recording.customFolderName, sessionUuid)
    if (!baseFilePath) {
        vscode.window.showErrorMessage(vscode.l10n.t('Could not generate file path'))
        logToOutput(vscode.l10n.t('Could not generate file path'), 'error')
        return
    }

    const filePath = path.join(exportPath, `${baseFilePath}.csv`)
    
    try {
        // Check if file exists
        if (!fs.existsSync(filePath)) {
            vscode.window.showWarningMessage(vscode.l10n.t('No recording file found to panic'))
            logToOutput(vscode.l10n.t('No recording file found to panic'), 'info')
            return
        }

        // Read the current file content
        const fileContent = await fs.promises.readFile(filePath, 'utf-8')
        const lines = fileContent.split('
')
        
        if (lines.length <= 1) {
            vscode.window.showWarningMessage(vscode.l10n.t('Recording file is empty'))
            logToOutput(vscode.l10n.t('Recording file is empty'), 'info')
[content] src/recording.ts (typescript)
 * Creates and updates the panic button status bar item.
 */
export function updatePanicButton(): void {
    if (!recording.isRecording) {
        // Hide panic button when not recording
        if (global.panicStatusBarItem) {
            global.panicStatusBarItem.hide()
        }
        return
    }

    // Create panic button if it doesn't exist
    if (!global.panicStatusBarItem) {
        global.panicStatusBarItem = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Right, 8999) // Position it to the left of the recording button
        extContext.subscriptions.push(global.panicStatusBarItem)
    }

    global.panicStatusBarItem.text = '$(alert)'
    global.panicStatusBarItem.tooltip = vscode.l10n.t('Panic Button: Delete last 10 seconds of recording')
    global.panicStatusBarItem.command = commands.panicButton
    global.panicStatusBarItem.show()
}

/**
[content] src/recording.ts (typescript)
    updatePanicButton()
    actionsProvider.setRecordingState(true)

    // Set up a timer to send data to the Lambda endpoint periodically
    uploadIntervalId = setInterval(async () => {
        if (!exportPath) {
            return;
        }

        const filePath = path.join(exportPath, `${baseFilePath}.csv`);
        const extensionVersion = extContext.extension.packageJSON.version as string;
        const userId = extContext.globalState.get<string>('userId');

        try {
            const fileContent = await fs.promises.readFile(filePath, 'utf-8');

            if (fileContent) {
                const payload = {
                    fileName: `${baseFilePath}.csv`,
                    content: fileContent,
                    version: extensionVersion,
                    userId: userId
                };
                await axios.post(API_GATEWAY_URL, payload);
                console.log(`Successfully sent ${payload.fileName} to Lambda endpoint.`);
            }
        } catch (error: any) {
            if (error.code === 'ENOENT') {
                console.warn(`File not found at ${filePath}. It might be created on first write.`);
            } else {
                console.error(`Error sending data to Lambda: ${error.message}`);
                if (axios.isAxiosError(error) && error.response) {
                    console.error("Lambda response status:", error.response.status);
                    console.error("Lambda response data:", error.response.data);
                }
            }
        }
    }, 5 * 60 * 1000); // 5 minutes
}

/**
 * Stops the recording process and finalizes the recording data.
 * @param context - The extension context.
 */
export function stopRecording(force = false): Promise<void> | void {
    if (!recording.isRecording) {
        notificationWithProgress(vscode.l10n.t('Not recording'))
        return
    }

    recording.isRecording = false
    clearInterval(intervalId)
    clearInterval(uploadIntervalId); // Clear the upload timer
    recording.timer = 0
    recording.activatedFiles?.clear()
    const index = extContext.subscriptions.indexOf(onChangeSubscription)
    if (index !== -1) {
        extContext.subscriptions.splice(index, 1)
    }
    updateStatusBarItem()
    updatePanicButton()
[content] src/recording.ts (typescript)
        vscode.window.showWarningMessage(vscode.l10n.t('panic.noActiveRecording'))
        logToOutput(vscode.l10n.t('panic.noActiveRecording'), 'info')
        return
    }

    if (!recording.startDateTime) {
        vscode.window.showErrorMessage(vscode.l10n.t('Recording start time not available'))
        logToOutput(vscode.l10n.t('Recording start time not available'), 'error')
        return
    }

    const exportPath = getExportPath()
    if (!exportPath) {
        vscode.window.showErrorMessage(vscode.l10n.t('Export path not available'))
        logToOutput(vscode.l10n.t('Export path not available'), 'error')
        return
    }

    const baseFilePath = generateBaseFilePath(recording.startDateTime, false, recording.customFolderName, sessionUuid)
    if (!baseFilePath) {
        vscode.window.showErrorMessage(vscode.l10n.t('Could not generate file path'))
        logToOutput(vscode.l10n.t('Could not generate file path'), 'error')
        return
    }

    const filePath = path.join(exportPath, `${baseFilePath}.csv`)
    
    try {
        // Check if file exists
        if (!fs.existsSync(filePath)) {
            vscode.window.showWarningMessage(vscode.l10n.t('panic.noRecordingFile'))
            logToOutput(vscode.l10n.t('panic.noRecordingFile'), 'info')
            return
        }

        // Read the current file content
        const fileContent = await fs.promises.readFile(filePath, 'utf-8')
        const lines = fileContent.split('
')
        
        if (lines.length <= 1) {
            vscode.window.showWarningMessage(vscode.l10n.t('panic.emptyFile'))
            logToOutput(vscode.l10n.t('panic.emptyFile'), 'info')
            return
        }

        // Calculate the cutoff time (10 seconds ago from now)
        const now = new Date().getTime()
        const cutoffTime = now - recording.startDateTime.getTime() - 10000 // 10 seconds in milliseconds

        // Keep the header line and filter out lines from the last 10 seconds
        const headerLine = lines[0]
        const dataLines = lines.slice(1).filter(line => {
            if (!line.trim()) return false // Skip empty lines
            
            const lineArr = line.split(/,(?=(?:[^"]*"[^"]*")*[^"]*$)/)
            if (lineArr.length < 2) return false // Skip malformed lines
            
            const timeStr = lineArr[1]
            const time = parseInt(timeStr, 10)
            
            if (isNaN(time)) return false // Skip lines with invalid time
            
            return time <= cutoffTime
        })

        // Rewrite the file with filtered content
        const newContent = [headerLine, ...dataLines].join('
')
        await fs.promises.writeFile(filePath, newContent, 'utf-8')

        // Update the sequence number to reflect the removed entries
        const removedLines = lines.length - dataLines.length - 1 // -1 for header
        if (removedLines > 0) {
            recording.sequence = Math.max(0, recording.sequence - removedLines)
        }

        vscode.window.showInformationMessage(vscode.l10n.t('panic.success'))
        logToOutput(vscode.l10n.t('panic.success'), 'info')
[content] src/recording.ts (typescript)
    const secondsToRemove = (panicButtonPressCount + 1) * 10 // Show what the next press will remove
    panicStatusBarItem.text = '$(alert)'
    panicStatusBarItem.tooltip = vscode.l10n.t('panic.tooltipProgressive', { seconds: secondsToRemove })
[content] src/recording.ts (typescript)
let panicButtonTimeoutId: NodeJS.Timeout | undefined;

const API_GATEWAY_URL = 'https://knm3fmbwbi.execute-api.us-east-1.amazonaws.com/v1/recordings';
const PANIC_BUTTON_TIMEOUT = 3000; // 3 seconds timeout for successive presses
[content] src/recording.ts (typescript)
 * Each successive press within 3 seconds removes more time: 10s, 20s, 30s, etc.
 * After 3 seconds of inactivity, the next press will be treated as a fresh press (10s).
 */
export async function panicButton(): Promise<void> {
    if (!recording.isRecording) {
        vscode.window.showWarningMessage(vscode.l10n.t('panic.noActiveRecording'))
        logToOutput(vscode.l10n.t('panic.noActiveRecording'), 'info')
        return
    }

    if (!recording.startDateTime) {
        vscode.window.showErrorMessage(vscode.l10n.t('Recording start time not available'))
        logToOutput(vscode.l10n.t('Recording start time not available'), 'error')
        return
    }

    const exportPath = getExportPath()
    if (!exportPath) {
        vscode.window.showErrorMessage(vscode.l10n.t('Export path not available'))
        logToOutput(vscode.l10n.t('Export path not available'), 'error')
        return
    }

    const baseFilePath = generateBaseFilePath(recording.startDateTime, false, recording.customFolderName, sessionUuid)
    if (!baseFilePath) {
        vscode.window.showErrorMessage(vscode.l10n.t('Could not generate file path'))
        logToOutput(vscode.l10n.t('Could not generate file path'), 'error')
        return
    }

    const filePath = path.join(exportPath, `${baseFilePath}.csv`)
    
    try {
        // Check if file exists
        if (!fs.existsSync(filePath)) {
            vscode.window.showWarningMessage(vscode.l10n.t('panic.noRecordingFile'))
            logToOutput(vscode.l10n.t('panic.noRecordingFile'), 'info')
            return
        }

        // Read the current file content
        const fileContent = await fs.promises.readFile(filePath, 'utf-8')
        const lines = fileContent.split('
')
        
        if (lines.length <= 1) {
            vscode.window.showWarningMessage(vscode.l10n.t('panic.emptyFile'))
            logToOutput(vscode.l10n.t('panic.emptyFile'), 'info')
            return
        }

        // Handle timeout logic for successive presses
        if (panicButtonTimeoutId) {
            // Clear existing timeout - this is a successive press
            clearTimeout(panicButtonTimeoutId)
            panicButtonPressCount++
        } else {
            // This is a fresh press or first press after timeout
            panicButtonPressCount = 1
        }

        // Set new timeout
        panicButtonTimeoutId = setTimeout(() => {
            panicButtonTimeoutId = undefined
            // Reset counter after timeout (next press will be fresh)
            panicButtonPressCount = 0
        }, PANIC_BUTTON_TIMEOUT)
[content] src/recording.ts (typescript)
        if (panicStatusBarItem) {
            panicStatusBarItem.hide()
        }
        return
    }

    // Create panic button if it doesn't exist
    if (!panicStatusBarItem) {
        panicStatusBarItem = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Right, 8999) // Position it to the left of the recording button
        extContext.subscriptions.push(panicStatusBarItem)