import { Editor } from '../../editor/Editor.js';
import { ContextMenuDirectory, ContextMenuReadOnlyEntry } from '../context-menu/context-menu.js';
declare const CodeMirror: any;
class CodePanel extends HTMLElement
{
currentPath: string | null = null;
_pinned: boolean = false;
_cm: any = null;
_initialized = false;
_ignoreChange = false;
connectedCallback(): void
{
if ( this._initialized ) return;
this._initialized = true;
this.className = 'code-panel';
this.innerHTML = `
Open a file from the file tree
`;
const editorDiv = this.querySelector( '.cp-editor' ) as HTMLElement;
this._cm = CodeMirror( editorDiv,
{
value: '',
lineNumbers: true,
theme: 'cp-dark',
indentWithTabs: false,
tabSize: 2,
indentUnit: 2,
lineWrapping: false,
readOnly: false,
} );
this._cm.on( 'change', () => this._onContentChanged() );
this.querySelector( '.cp-pin' )!.addEventListener( 'click', () => this._togglePin() );
this.querySelector( '.cp-save' )!.addEventListener( 'click', () => this._save() );
this.querySelector( '.cp-undo' )!.addEventListener( 'click', () => this._cm.undo() );
this.querySelector( '.cp-redo' )!.addEventListener( 'click', () => this._cm.redo() );
Editor.get().onDocumentOpened.addListener( ( e ) =>
{
if ( 'code-panel' !== e.editorTag ) return;
if ( e.targetElement && e.targetElement !== this ) return;
if ( ! this._pinned ) this._loadDocument( e.path, e.content );
} );
document.addEventListener( 'keydown', ( e: KeyboardEvent ) =>
{
if ( ! this.currentPath ) return;
if ( e.ctrlKey && e.key === 's' ) { e.preventDefault(); this._save(); }
} );
}
_loadDocument( path: string, content: string ): void
{
this.currentPath = path;
this._updateTabLabel( path );
this.querySelector( '.cp-empty' )!.setAttribute( 'style', 'display:none' );
( this.querySelector( '.cp-editor' ) as HTMLElement ).style.display = '';
this._ignoreChange = true;
this._cm.setValue( content );
this._cm.clearHistory();
this._cm.setOption( 'mode', this._resolveMode( path ) );
this._ignoreChange = false;
this._updateButtons( false );
this._cm.refresh();
}
_resolveMode( filePath: string ): string
{
const name = filePath.slice( filePath.lastIndexOf( '/' ) + 1 );
if ( name.endsWith( '.js' ) || name.endsWith( '.json' ) ) return 'javascript';
if ( name.endsWith( '.ts' ) ) return 'javascript';
if ( name.endsWith( '.css' ) ) return 'css';
if ( name.endsWith( '.html' ) || name.endsWith( '.htm' ) ) return 'htmlmixed';
if ( name.endsWith( '.xml' ) || name.endsWith( '.svg' ) ) return 'xml';
if ( name.endsWith( '.md' ) ) return 'markdown';
return 'null';
}
_onContentChanged(): void
{
if ( this._ignoreChange || ! this.currentPath ) return;
const content = this._cm.getValue();
Editor.get().markDirty( this.currentPath, content );
this._updateButtons( true );
}
_togglePin(): void
{
this._pinned = ! this._pinned;
this.querySelector( '.cp-pin' )!.classList.toggle( 'pinned', this._pinned );
}
async _save(): Promise
{
if ( ! this.currentPath ) return;
await Editor.get().save( this.currentPath );
this._updateButtons( false );
}
_updateTabLabel( path: string ): void
{
const name = path ? path.slice( path.lastIndexOf( '/' ) + 1 ) : '';
this.dispatchEvent( new CustomEvent( 'panel:label-change', { bubbles: true, detail: { label: '📝 ' + name } } ) );
}
_updateButtons( dirty: boolean ): void
{
( this.querySelector( '.cp-save' ) as HTMLButtonElement ).disabled = ! dirty;
( this.querySelector( '.cp-undo' ) as HTMLButtonElement ).disabled = this._cm.historySize().undo < 1;
( this.querySelector( '.cp-redo' ) as HTMLButtonElement ).disabled = this._cm.historySize().redo < 1;
}
addContextMenuEntries( dir: ContextMenuDirectory ): void
{
if ( this.currentPath )
{
dir.add( new ContextMenuReadOnlyEntry( dir, `Editing: ${this.currentPath}` ) );
}
else
{
dir.add( new ContextMenuReadOnlyEntry( dir, 'No file open' ) );
}
}
}
customElements.define( 'code-panel', CodePanel );