create file tabs are now shared

This commit is contained in:
Ben Floyd 2025-04-28 13:10:34 -06:00
parent 24b082d85c
commit 9f1d8bdc20
5 changed files with 160 additions and 46 deletions

20
main.ts
View file

@ -92,8 +92,6 @@ export default class StreamsPlugin extends Plugin {
})
);
/**
*
* TESTING
@ -123,11 +121,6 @@ export default class StreamsPlugin extends Plugin {
*
*/
// Initial setup
const activeLeaf = this.app.workspace.getActiveViewOfType(MarkdownView)?.leaf;
if (activeLeaf) {
@ -138,6 +131,18 @@ export default class StreamsPlugin extends Plugin {
this.settings.streams
.filter(stream => stream.addCommand)
.forEach(stream => this.addStreamCommand(stream));
// Listen for CreateFileView state changes and update calendar widget accordingly
this.registerEvent(
// Use 'on' directly on app.workspace without type checking
// @ts-ignore - Custom event not in type definitions
this.app.workspace.on('streams-create-file-state-changed', (view: any) => {
this.log.debug('Create file state changed, updating calendar widget');
if (view && view.leaf) {
this.updateCalendarWidgetForCreateView(view.leaf);
}
})
);
}
private loadStyles() {
@ -410,7 +415,6 @@ export default class StreamsPlugin extends Plugin {
}
}
/**
*
* TESTING

View file

@ -3,6 +3,7 @@ import { Stream } from '../../types';
import { Logger } from '../utils/Logger';
import { OpenStreamDateCommand } from '../commands/OpenStreamDateCommand';
import { OpenTodayStreamCommand } from '../commands/OpenTodayStreamCommand';
import { CREATE_FILE_VIEW_TYPE } from './CreateFileView';
interface ContentIndicator {
exists: boolean;
@ -29,12 +30,49 @@ export class CalendarWidget {
this.widget = document.createElement('div');
this.widget.addClass('stream-calendar-widget');
// Find the markdown view container
const markdownView = leaf.view as MarkdownView;
const contentContainer = markdownView.contentEl;
// Find the content container based on view type
let contentContainer: HTMLElement | null = null;
// Handle different view types
const viewType = leaf.view.getViewType();
this.log.debug(`Creating calendar widget for view type: ${viewType}`);
if (viewType === 'markdown') {
// For markdown views
const markdownView = leaf.view as MarkdownView;
contentContainer = markdownView.contentEl;
// Get current file's date if it exists
const currentFile = markdownView.file;
if (currentFile) {
const match = currentFile.basename.match(/^\d{4}-\d{2}-\d{2}/);
if (match) {
this.currentViewedDate = match[0];
}
}
} else if (viewType === CREATE_FILE_VIEW_TYPE) {
// For create file views
contentContainer = (leaf.view as any).contentEl;
// Try to get the date from the view's state
try {
const state = leaf.view.getState();
if (state && state.date) {
const dateString = state.date as string;
// Extract date string in YYYY-MM-DD format for currentViewedDate
this.currentViewedDate = dateString.split('T')[0];
this.log.debug(`Set currentViewedDate from state: ${this.currentViewedDate}`);
}
} catch (error) {
this.log.error('Error getting date from CreateFileView state:', error);
}
} else {
// For other view types
contentContainer = (leaf.view as any).contentEl;
}
if (!contentContainer) {
console.error('Could not find content container');
this.log.error('Could not find content container');
return;
}
@ -48,15 +86,6 @@ export class CalendarWidget {
this.fileModifyHandler = this.handleFileModify.bind(this);
this.app.vault.on('modify', this.fileModifyHandler);
// Get current file's date if it exists
const currentFile = markdownView.file;
if (currentFile) {
const match = currentFile.basename.match(/^\d{4}-\d{2}-\d{2}/);
if (match) {
this.currentViewedDate = match[0];
}
}
this.initializeWidget();
this.loadStyles();
}

View file

@ -29,9 +29,40 @@ export class CreateFileView extends ItemView {
}
getDisplayText(): string {
// Extract just the filename without extension for the tab title
const fileName = this.filePath.split('/').pop()?.replace('.md', '') || '';
return fileName;
// Format the date for display in a user-friendly way
try {
// Extract date from the filename
const fileName = this.filePath.split('/').pop() || '';
const match = fileName.match(/(\d{4}-\d{2}-\d{2})\.md$/);
if (match && match[1]) {
// Get a readable date format
const [year, month, day] = match[1].split('-').map(n => parseInt(n, 10));
const dateObj = new Date(year, month - 1, day);
if (!isNaN(dateObj.getTime())) {
// Format as MMM DD, YYYY
const dateString = dateObj.toLocaleDateString('en-US', {
month: 'short',
day: 'numeric',
year: 'numeric'
});
// Return a title like "Work Notes: Apr 27, 2025"
if (this.stream && this.stream.name) {
return `${this.stream.name}: ${dateString}`;
}
return dateString;
}
}
// Fallback to just the filename without extension
return fileName.replace('.md', '');
} catch (error) {
this.log.error('Error formatting display text:', error);
return this.filePath.split('/').pop()?.replace('.md', '') || '';
}
}
getState(): any {
@ -79,32 +110,34 @@ export class CreateFileView extends ItemView {
}
}
// Validate the date
if (isNaN(this.date.getTime())) {
this.log.error(`Invalid date after parsing: ${state.date}`);
// Try to extract from filename as fallback
this.extractDateFromFilename();
}
// Check if date has changed
if (oldDate && this.date) {
dateChanged = oldDate.toDateString() !== this.date.toDateString();
if (oldDate) {
dateChanged = oldDate.getTime() !== this.date.getTime();
} else {
dateChanged = true;
}
} catch (error) {
this.log.error(`Error setting date: ${error}`);
// Try to extract from filename as fallback
this.extractDateFromFilename();
this.log.error(`Error parsing date: ${error}`);
this.date = new Date(); // Fallback to current date
}
} else {
// Try to extract from filename if no date provided
this.extractDateFromFilename();
}
// If date or file path changed, refresh the view
if (dateChanged || oldFilePath !== this.filePath) {
this.log.debug("Date or file path changed, refreshing view");
await this.refreshView();
// Determine if we need to refresh the view
const filePathChanged = oldFilePath !== this.filePath;
// Check for a flag in the result that indicates this was just a title update
// to prevent infinite loops
const isJustTitleUpdate = result && result.isTitleRefresh;
if ((filePathChanged || dateChanged) && !isJustTitleUpdate) {
this.log.debug(`State changed significantly, refreshing view`);
// Use a setTimeout to ensure this runs after setState completes
setTimeout(() => {
this.refreshView();
// Trigger a custom event so that the plugin can update the calendar widget
this.app.workspace.trigger('streams-create-file-state-changed', this);
}, 0);
}
}
}
@ -234,5 +267,33 @@ export class CreateFileView extends ItemView {
// Force a refresh of the view content
this.contentEl.empty();
await this.onOpen();
// Update the tab title
this.updateTabTitle();
// Let Obsidian know the view has updated
this.app.workspace.trigger('layout-change');
}
/**
* Updates the tab title to match the current date
* This is needed because when navigating between dates the tab title
* doesn't automatically update
*/
private updateTabTitle(): void {
try {
this.log.debug('Updating tab title');
// Force Obsidian to update the tab title by setting the view state again
// This will call getDisplayText() internally and update the tab UI
this.leaf.setViewState({
type: this.getViewType(),
state: this.getState(),
}, { history: false, isTitleRefresh: true });
this.log.debug(`Updated tab title to: ${this.getDisplayText()}`);
} catch (error) {
this.log.error('Error updating tab title:', error);
}
}
}

View file

@ -114,8 +114,20 @@ export async function openStreamDate(app: App, stream: Stream, date: Date = new
}
}
// Get or create a leaf
const leaf = app.workspace.getLeaf('tab');
// Look for an existing CreateFileView leaf to reuse
let leaf: WorkspaceLeaf | null = null;
// First, look for an existing CreateFileView leaf
const existingCreateFileViewLeaves = app.workspace.getLeavesOfType(CREATE_FILE_VIEW_TYPE);
if (existingCreateFileViewLeaves.length > 0) {
// Reuse the first one we find
leaf = existingCreateFileViewLeaves[0];
log.debug('Reusing existing CreateFileView leaf');
} else {
// If no existing CreateFileView leaf, create a new one
leaf = app.workspace.getLeaf('tab');
log.debug('Created a new leaf for CreateFileView');
}
// Register the view type if not already registered
// Access the viewRegistry through app as any since it might not be in the type definitions

View file

@ -81,6 +81,14 @@ If your plugin does not need CSS, delete this file.
margin-right: 8px;
}
/* Special positioning for calendar in create file view */
.streams-create-file-container .stream-calendar-widget {
position: fixed;
top: 80px;
right: 0;
margin-right: 0;
}
.stream-calendar-collapsed {
position: absolute;
top: 0;