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
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -1787,6 +1787,66 @@ const messageHandlers: Record<string, (ctx: MessageHandlerContext) => Promise<vo
}
},

toggleCellVisibility: async ({ event, document, webviewPanel, provider, updateWebview }) => {
const typedEvent = event as Extract<EditorPostMessages, { command: "toggleCellVisibility" }>;
const { cellId, hidden } = typedEvent.content;

debug("toggleCellVisibility message received for cell:", cellId, "hidden:", hidden);

try {
const currentCellData = document.getCellData(cellId) || {};

document.updateCellData(cellId, {
...currentCellData,
hidden,
});

try {
const cellForEdits = document.getCell(cellId);
if (cellForEdits) {
if (!cellForEdits.metadata.edits) {
cellForEdits.metadata.edits = [] as any;
}
const ts = Date.now();
let user = "anonymous";
try {
const authApi = await provider.getAuthApi();
const userInfo = await authApi?.getUserInfo();
user = userInfo?.username || "anonymous";
} catch { /* ignore */ }
(cellForEdits.metadata.edits as any[]).push({
editMap: EditMapUtils.metadataNested("data", "hidden"),
value: hidden,
timestamp: ts,
type: EditType.USER_EDIT,
author: user,
validatedBy: [],
});
}
} catch (e) {
console.warn("Failed to record visibility edit entry on cell", e);
}

await document.save(new vscode.CancellationTokenSource().token);

debug(`Successfully toggled visibility for cell: ${cellId}, hidden: ${hidden}`);

const workspaceFolder = vscode.workspace.getWorkspaceFolder(document.uri);
if (workspaceFolder) {
await provider.toggleCellVisibilityInPairedFile(cellId, hidden, document.uri.toString(), workspaceFolder);
} else {
console.warn("No workspace folder found, skipping paired file visibility toggle");
}

updateWebview();
} catch (error) {
console.error("Error toggling cell visibility:", cellId, error);
vscode.window.showErrorMessage(
`Failed to toggle cell visibility: ${error instanceof Error ? error.message : String(error)}`
);
}
},

triggerReindexing: async () => {
debug("Triggering reindexing after all translations completed");
await vscode.commands.executeCommand("codex-editor-extension.forceReindex");
Expand Down
118 changes: 117 additions & 1 deletion src/providers/codexCellEditorProvider/codexCellEditorProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2362,6 +2362,7 @@ export class CodexCellEditorProvider implements vscode.CustomEditorProvider<Code
cellLabel: cell.metadata?.cellLabel,
merged: cell.metadata?.data?.merged,
deleted: cell.metadata?.data?.deleted,
hidden: cell.metadata?.data?.hidden,
data: cell.metadata?.data,
attachments: cell.metadata?.attachments,
metadata: {
Expand Down Expand Up @@ -2394,6 +2395,10 @@ export class CodexCellEditorProvider implements vscode.CustomEditorProvider<Code
return;
}

if (cell.hidden && !isSourceAndCorrectionEditorMode) {
return;
}

if (cell.deleted) {
return;
}
Expand All @@ -2419,6 +2424,7 @@ export class CodexCellEditorProvider implements vscode.CustomEditorProvider<Code
timestamps: cell.timestamps,
cellLabel: cell.cellLabel,
merged: cell.merged,
hidden: cell.hidden,
data: cell.data,
attachments: cell.attachments,
metadata: cell.metadata,
Expand Down Expand Up @@ -3063,6 +3069,107 @@ export class CodexCellEditorProvider implements vscode.CustomEditorProvider<Code
}
}

public async toggleCellVisibilityInPairedFile(cellId: string, hidden: boolean, uri: string, workspaceFolder: vscode.WorkspaceFolder) {
debug("Toggling cell visibility in paired file:", { cellId, hidden, uri });

try {
const normalizedPath = uri.replace(/\\/g, "/");
const baseFileName = path.basename(normalizedPath);

let targetPath: vscode.Uri;
let targetFileName: string;
let isSourceToTarget: boolean;

if (baseFileName.endsWith(".source")) {
targetFileName = baseFileName.replace(".source", ".codex");
targetPath = vscode.Uri.joinPath(workspaceFolder.uri, "files", "target", targetFileName);
isSourceToTarget = true;
} else if (baseFileName.endsWith(".codex")) {
targetFileName = baseFileName.replace(".codex", ".source");
targetPath = vscode.Uri.joinPath(workspaceFolder.uri, ".project", "sourceTexts", targetFileName);
isSourceToTarget = false;
} else {
throw new Error(`Unsupported file type for visibility toggle: ${baseFileName}`);
}

await vscode.commands.executeCommand(
"vscode.openWith",
targetPath,
"codex.cellEditor",
{ viewColumn: isSourceToTarget ? vscode.ViewColumn.Two : vscode.ViewColumn.One }
);

let targetDocument: CodexCellDocument | undefined;
const targetDocumentUri = targetPath.toString();

for (const [panelUri] of this.webviewPanels.entries()) {
if (this.isMatchingFilePair(targetDocumentUri, panelUri)) {
targetDocument = await this.openCustomDocument(
vscode.Uri.parse(panelUri),
{},
new vscode.CancellationTokenSource().token
);
break;
}
}

if (!targetDocument) {
targetDocument = await this.openCustomDocument(
targetPath,
{},
new vscode.CancellationTokenSource().token
);
}

const targetCellData = targetDocument.getCellData(cellId) || {};
targetDocument.updateCellData(cellId, {
...targetCellData,
hidden,
});

try {
const cell = (targetDocument as any).getCell(cellId);
if (cell) {
cell.metadata.edits = cell.metadata.edits || [];
cell.metadata.edits.push({
editMap: ["metadata", "data", "hidden"],
value: hidden,
timestamp: Date.now(),
type: "user-edit",
author: "anonymous",
validatedBy: [],
});
}
} catch (e) {
console.warn("Failed to append hidden edit on paired file cell", e);
}

await targetDocument.save(new vscode.CancellationTokenSource().token);

debug(`Successfully toggled visibility for cell ${cellId} in ${isSourceToTarget ? "target" : "source"} file ${targetFileName}`);

for (const [panelUri, panel] of this.webviewPanels.entries()) {
if (this.isMatchingFilePair(targetDocumentUri, panelUri)) {
const docUri = panelUri;
const rev = this.getDocumentRevision(docUri);
const currentPosition = this.currentMilestoneSubsectionMap.get(docUri);
safePostMessageToPanel(panel, {
type: "refreshCurrentPage",
rev,
milestoneIndex: currentPosition?.milestoneIndex ?? 0,
subsectionIndex: currentPosition?.subsectionIndex ?? 0,
});
break;
}
}
} catch (error) {
console.error("Error toggling cell visibility in paired file:", error);
vscode.window.showErrorMessage(
`Failed to toggle visibility in paired file: ${error instanceof Error ? error.message : String(error)}`
);
}
}

public async getAuthApi() {
return getAuthApi();
}
Expand Down Expand Up @@ -4185,10 +4292,19 @@ export class CodexCellEditorProvider implements vscode.CustomEditorProvider<Code
});
});

// Refresh all webviews to show/hide merged cells appropriately
// Refresh all open webviews so hidden/merged cells are shown or filtered correctly
for (const [docUri, panel] of this.webviewPanels) {
if (this.currentDocument && docUri === this.currentDocument.uri.toString()) {
await this.refreshWebview(panel, this.currentDocument);
} else {
const rev = this.getDocumentRevision(docUri);
const currentPosition = this.currentMilestoneSubsectionMap.get(docUri);
safePostMessageToPanel(panel, {
type: "refreshCurrentPage",
rev,
milestoneIndex: currentPosition?.milestoneIndex ?? 0,
subsectionIndex: currentPosition?.subsectionIndex ?? 0,
});
}
}
}
Expand Down
1 change: 1 addition & 0 deletions src/providers/codexCellEditorProvider/utils/cellUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ export function convertCellToQuillContent(cell: CustomNotebookCellData): QuillCe
cellLabel: cell.metadata?.cellLabel,
merged: cell.metadata?.data?.merged,
deleted: cell.metadata?.data?.deleted,
hidden: cell.metadata?.data?.hidden,
data: cell.metadata?.data,
attachments: cell.metadata?.attachments || {},
metadata: {
Expand Down
9 changes: 9 additions & 0 deletions types/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -550,6 +550,13 @@ export type EditorPostMessages =
};
}
| { command: "toggleCorrectionEditorMode"; }
| {
command: "toggleCellVisibility";
content: {
cellId: string;
hidden: boolean;
};
}
| {
command: "cancelMerge";
content: {
Expand Down Expand Up @@ -709,6 +716,7 @@ type CodexData = Timestamps & {
verse?: string;
merged?: boolean;
deleted?: boolean;
hidden?: boolean;
originalText?: string;
globalReferences?: string[]; // Array of cell IDs in original format (e.g., "GEN 1:1") used for header generation
milestoneIndex?: number | null; // 0-based milestone index for O(1) lookup (null if no milestone)
Expand Down Expand Up @@ -939,6 +947,7 @@ interface QuillCellContent {
cellLabel?: string;
merged?: boolean;
deleted?: boolean;
hidden?: boolean;
data?: { [key: string]: any; footnotes?: Footnote[]; };
attachments?: { [attachmentId: string]: { type: string; isDeleted?: boolean; isMissing?: boolean; url?: string; validatedBy?: ValidationEntry[]; }; };
metadata?: {
Expand Down
35 changes: 33 additions & 2 deletions webviews/codex-webviews/src/CodexCellEditor/CellContentDisplay.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -341,13 +341,21 @@ const CellContentDisplay: React.FC<CellContentDisplayProps> = React.memo(
};

const handleCancelMerge = (e: React.MouseEvent) => {
e.stopPropagation(); // Prevent the cell click handler from firing
e.stopPropagation();
vscode.postMessage({
command: "cancelMerge",
content: { cellId: cellIds[0] },
} as any);
};

const handleToggleVisibility = (e: React.MouseEvent) => {
e.stopPropagation();
vscode.postMessage({
command: "toggleCellVisibility",
content: { cellId: cellIds[0], hidden: !cell.hidden },
} as EditorPostMessages);
};

// Handler for merging cell with previous cell
const handleMergeWithPrevious = (e: React.MouseEvent) => {
e.stopPropagation(); // Prevent the cell click handler from firing
Expand Down Expand Up @@ -656,9 +664,10 @@ const CellContentDisplay: React.FC<CellContentDisplayProps> = React.memo(
overflow: "visible",
maxWidth: "100%",
boxSizing: "border-box",
transition: "border 0.3s ease",
transition: "border 0.3s ease, opacity 0.2s ease",
overflowWrap: "break-word",
wordWrap: "break-word",
opacity: cell.hidden && isSourceText && isCorrectionEditorMode ? 0.45 : 1,
wordBreak: "break-word",
}}
>
Expand Down Expand Up @@ -958,6 +967,28 @@ const CellContentDisplay: React.FC<CellContentDisplayProps> = React.memo(
</Button>
</div>
)}
{isSourceText && isCorrectionEditorMode && (
<div style={{ flexShrink: 0 }}>
<Button
variant="ghost"
style={{
height: "16px",
width: "16px",
padding: 0,
display: "flex",
alignItems: "center",
justifyContent: "center",
}}
onClick={handleToggleVisibility}
title={cell.hidden ? "Show cell in normal mode" : "Hide cell in normal mode"}
>
<i
className={`codicon ${cell.hidden ? "codicon-eye-closed" : "codicon-eye"}`}
style={{ fontSize: "12px" }}
/>
</Button>
</div>
)}
</div>
</div>
)}
Expand Down
14 changes: 10 additions & 4 deletions webviews/codex-webviews/src/CodexCellEditor/CellList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -134,19 +134,24 @@ const CellList: React.FC<CellListProps> = ({
// State to track unresolved comments count for each cell
const [cellCommentsCount, setCellCommentsCount] = useState<Map<string, number>>(new Map());

// Filter out merged cells if we're in correction editor mode for source text
const filteredTranslationUnits = useMemo(() => {
let filtered = translationUnits;

const isSourceCorrectionMode = isSourceText && isCorrectionEditorMode;

// Filter out merged cells if we're in correction editor mode for source text
if (isSourceText && isCorrectionEditorMode) {
if (isSourceCorrectionMode) {
filtered = filtered.filter((unit) => {
// Check if cell has merged metadata in the data property
const cellData = unit.data as any;
return !cellData?.merged;
});
}

// Hide hidden cells unless we're in source correction mode (where the eyeball toggle is available)
if (!isSourceCorrectionMode) {
filtered = filtered.filter((unit) => !unit.hidden);
}

// Filter out milestone cells from the view (they remain in JSON)
filtered = filtered.filter((unit) => unit.cellType !== CodexCellTypes.MILESTONE);

Expand Down Expand Up @@ -563,7 +568,8 @@ const CellList: React.FC<CellListProps> = ({
unit.cellType !== CodexCellTypes.PARATEXT &&
unit.cellType !== CodexCellTypes.MILESTONE &&
!isChildCell(unit) &&
!unit.merged
!unit.merged &&
!unit.hidden
) {
visibleCellCount++;
}
Expand Down
Loading