diff --git a/frontend/eslint.config.mjs b/frontend/eslint.config.mjs
index c0f9fd678..0b92de05b 100644
--- a/frontend/eslint.config.mjs
+++ b/frontend/eslint.config.mjs
@@ -1,9 +1,18 @@
// @ts-check
import eslint from '@eslint/js';
+import globals from "globals";
import { defineConfig } from 'eslint/config';
import tseslint from 'typescript-eslint';
+const srcGlobs = [
+ 'src/**/*.{js,mjs,jsx,ts,tsx}',
+];
+const nodeGlobs = [
+ 'scripts/**/*.{js,ts,mjs}',
+ '*.config.{js,ts,mjs}',
+];
+
export default defineConfig(
eslint.configs.recommended,
tseslint.configs.recommended,
@@ -15,7 +24,6 @@ export default defineConfig(
},
{
rules: {
- "no-undef": "off", // Temporarily disabled until codebase conformant
"@typescript-eslint/no-empty-object-type": [
"error",
{
@@ -38,5 +46,23 @@ export default defineConfig(
},
],
},
- }
+ },
+ // Config for browser scripts
+ {
+ files: srcGlobs,
+ languageOptions: {
+ globals: {
+ ...globals.browser,
+ }
+ }
+ },
+ // Config for node scripts
+ {
+ files: nodeGlobs,
+ languageOptions: {
+ globals: {
+ ...globals.node,
+ }
+ }
+ },
);
diff --git a/frontend/package-lock.json b/frontend/package-lock.json
index 316e1af8a..f73143e83 100644
--- a/frontend/package-lock.json
+++ b/frontend/package-lock.json
@@ -41,6 +41,7 @@
"@tanstack/react-virtual": "^3.13.12",
"autoprefixer": "^10.4.21",
"axios": "^1.12.2",
+ "globals": "^16.4.0",
"i18next": "^25.5.2",
"i18next-browser-languagedetector": "^8.2.0",
"i18next-http-backend": "^3.0.2",
@@ -1760,6 +1761,19 @@
"mlly": "^1.7.4"
}
},
+ "node_modules/@iconify/utils/node_modules/globals": {
+ "version": "15.15.0",
+ "resolved": "https://registry.npmjs.org/globals/-/globals-15.15.0.tgz",
+ "integrity": "sha512-7ACyT3wmyp3I61S4fG682L0VA2RGD9otkqGJIwNUMF1SWUombIIk+af1unuDYgMm082aHYwD+mzJvv9Iu8dsgg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
"node_modules/@isaacs/cliui": {
"version": "8.0.2",
"resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz",
@@ -6415,10 +6429,9 @@
}
},
"node_modules/globals": {
- "version": "15.15.0",
- "resolved": "https://registry.npmjs.org/globals/-/globals-15.15.0.tgz",
- "integrity": "sha512-7ACyT3wmyp3I61S4fG682L0VA2RGD9otkqGJIwNUMF1SWUombIIk+af1unuDYgMm082aHYwD+mzJvv9Iu8dsgg==",
- "dev": true,
+ "version": "16.4.0",
+ "resolved": "https://registry.npmjs.org/globals/-/globals-16.4.0.tgz",
+ "integrity": "sha512-ob/2LcVVaVGCYN+r14cnwnoDPUufjiYgSqRhiFD0Q1iI4Odora5RE8Iv1D24hAz5oMophRGkGz+yuvQmmUMnMw==",
"license": "MIT",
"engines": {
"node": ">=18"
diff --git a/frontend/package.json b/frontend/package.json
index 0ec14e357..319653af1 100644
--- a/frontend/package.json
+++ b/frontend/package.json
@@ -37,6 +37,7 @@
"@tanstack/react-virtual": "^3.13.12",
"autoprefixer": "^10.4.21",
"axios": "^1.12.2",
+ "globals": "^16.4.0",
"i18next": "^25.5.2",
"i18next-browser-languagedetector": "^8.2.0",
"i18next-http-backend": "^3.0.2",
diff --git a/frontend/public/locales/en-GB/translation.json b/frontend/public/locales/en-GB/translation.json
index 61043172c..03c2eefac 100644
--- a/frontend/public/locales/en-GB/translation.json
+++ b/frontend/public/locales/en-GB/translation.json
@@ -83,6 +83,8 @@
"save": "Save",
"saveToBrowser": "Save to Browser",
"download": "Download",
+ "pin": "Pin",
+ "unpin": "Unpin",
"undoOperationTooltip": "Click to undo the last operation and restore the original files",
"undo": "Undo",
"moreOptions": "More Options",
diff --git a/frontend/src/components/fileEditor/FileEditor.module.css b/frontend/src/components/fileEditor/FileEditor.module.css
index 173738c29..17184bbf4 100644
--- a/frontend/src/components/fileEditor/FileEditor.module.css
+++ b/frontend/src/components/fileEditor/FileEditor.module.css
@@ -34,9 +34,9 @@
.header {
height: 2.25rem;
border-radius: 0.0625rem 0.0625rem 0 0;
- display: grid;
- grid-template-columns: 44px 1fr 44px;
+ display: flex;
align-items: center;
+ justify-content: space-between;
padding: 0 6px;
user-select: none;
background: var(--bg-toolbar);
@@ -86,14 +86,23 @@
}
.headerIndex {
+ position: absolute;
+ left: 50%;
+ transform: translateX(-50%);
text-align: center;
font-weight: 500;
font-size: 18px;
letter-spacing: 0.04em;
}
-.kebab {
- justify-self: end;
+.headerActions {
+ display: flex;
+ align-items: center;
+ gap: 8px;
+ margin-left: auto;
+}
+
+.headerIconButton {
color: #FFFFFF !important;
}
@@ -216,6 +225,11 @@
color: rgba(0, 0, 0, 0.35);
}
+.pinned {
+ color: #FFC107 !important;
+}
+
+
/* Unsupported file indicator */
.unsupportedPill {
margin-left: 1.75rem;
@@ -384,4 +398,4 @@
.addFileSubtext {
font-size: 0.875rem;
opacity: 0.8;
-}
\ No newline at end of file
+}
diff --git a/frontend/src/components/fileEditor/FileEditor.tsx b/frontend/src/components/fileEditor/FileEditor.tsx
index 626eaab4f..90ee6eebe 100644
--- a/frontend/src/components/fileEditor/FileEditor.tsx
+++ b/frontend/src/components/fileEditor/FileEditor.tsx
@@ -288,7 +288,7 @@ const FileEditor = ({
// File operations using context
- const handleDeleteFile = useCallback((fileId: FileId) => {
+ const handleCloseFile = useCallback((fileId: FileId) => {
const record = activeStirlingFileStubs.find(r => r.id === fileId);
const file = record ? selectors.getFile(record.id) : null;
if (record && file) {
@@ -467,7 +467,7 @@ const FileEditor = ({
selectedFiles={localSelectedIds}
selectionMode={selectionMode}
onToggleFile={toggleFile}
- onDeleteFile={handleDeleteFile}
+ onCloseFile={handleCloseFile}
onViewFile={handleViewFile}
_onSetStatus={showStatus}
onReorderFiles={handleReorderFiles}
diff --git a/frontend/src/components/fileEditor/FileEditorThumbnail.tsx b/frontend/src/components/fileEditor/FileEditorThumbnail.tsx
index f09bfeeb1..8159e30a9 100644
--- a/frontend/src/components/fileEditor/FileEditorThumbnail.tsx
+++ b/frontend/src/components/fileEditor/FileEditorThumbnail.tsx
@@ -1,10 +1,10 @@
import React, { useState, useCallback, useRef, useMemo, useEffect } from 'react';
-import { Text, ActionIcon, CheckboxIndicator } from '@mantine/core';
+import { Text, ActionIcon, CheckboxIndicator, Tooltip } from '@mantine/core';
import { alert } from '../toast';
import { useTranslation } from 'react-i18next';
import MoreVertIcon from '@mui/icons-material/MoreVert';
import DownloadOutlinedIcon from '@mui/icons-material/DownloadOutlined';
-import DeleteOutlineIcon from '@mui/icons-material/DeleteOutline';
+import CloseIcon from '@mui/icons-material/Close';
import UnarchiveIcon from '@mui/icons-material/Unarchive';
import PushPinIcon from '@mui/icons-material/PushPin';
import PushPinOutlinedIcon from '@mui/icons-material/PushPinOutlined';
@@ -29,7 +29,7 @@ interface FileEditorThumbnailProps {
selectedFiles: FileId[];
selectionMode: boolean;
onToggleFile: (fileId: FileId) => void;
- onDeleteFile: (fileId: FileId) => void;
+ onCloseFile: (fileId: FileId) => void;
onViewFile: (fileId: FileId) => void;
_onSetStatus: (status: string) => void;
onReorderFiles?: (sourceFileId: FileId, targetFileId: FileId, selectedFileIds: FileId[]) => void;
@@ -44,7 +44,7 @@ const FileEditorThumbnail = ({
index,
selectedFiles,
onToggleFile,
- onDeleteFile,
+ onCloseFile,
_onSetStatus,
onReorderFiles,
onDownloadFile,
@@ -258,18 +258,60 @@ const FileEditorThumbnail = ({
{index + 1}
- {/* Kebab menu */}
-