mirror of
https://github.com/sehugg/8bitworkshop.git
synced 2026-10-25 00:02:16 +00:00
support #asseteditor/filename/lineno
- Track selected asset in hash - Highlight selected asset on navigation
This commit is contained in:
1 parent
091bd25bd9
commit
fb2b6ea9b6
6 files changed
+67
-12
No files matched your search
@@ -497,6 +497,15 @@ div.asset_file_header {
|
||||
}
|
||||
div.asset_block {
|
||||
margin-top: 8px;
|
||||
transition: outline-color 1.5s ease-out;
|
||||
outline: 2px solid transparent;
|
||||
background-color: transparent;
|
||||
|
||||
}
|
||||
div.asset_block.asset_highlight {
|
||||
outline: 2px solid #99cc99;
|
||||
border-radius: 4px;
|
||||
transition: none;
|
||||
}
|
||||
.asset_snip {
|
||||
font-family: "Andale Mono", "Menlo", "Lucida Console", monospace;
|
||||
|
||||
@@ -1747,6 +1747,8 @@ function replaceURLState() {
|
||||
function hashToViewIdResolved(hash: string): string | null {
|
||||
if (!hash || hash === '#') return null;
|
||||
var id = decodeURIComponent(hash.substring(1));
|
||||
// check for extended asset editor hash (e.g. #asseteditor/filename/startline)
|
||||
if (id.startsWith('asseteditor/')) return '#asseteditor';
|
||||
// check if it's a registered tool window (e.g. #asseteditor)
|
||||
if (projectWindows.isWindow('#' + id)) return '#' + id;
|
||||
// otherwise treat as a file path (e.g. hello.asm)
|
||||
@@ -1769,6 +1771,7 @@ function installHashChangeHandler() {
|
||||
function navigateToInitialHash(initialHash: string) {
|
||||
if (initialHash && initialHash !== '#') {
|
||||
const viewId = hashToViewIdResolved(initialHash) || getCurrentMainFilename();
|
||||
history.replaceState(null, '', initialHash);
|
||||
projectWindows.createOrShow(viewId);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ import { Decoration, DecorationSet, EditorView, ViewPlugin, ViewUpdate, WidgetTy
|
||||
|
||||
// Asset header detection — shows a clickable badge on lines with ;;{json};; or /*{json}*/
|
||||
class AssetHeaderWidget extends WidgetType {
|
||||
constructor(readonly header: string, readonly onClick: () => void) { super() }
|
||||
constructor(readonly header: string, readonly handleClick: (span: HTMLElement) => void) { super() }
|
||||
|
||||
toDOM() {
|
||||
const span = document.createElement("span");
|
||||
@@ -12,7 +12,7 @@ class AssetHeaderWidget extends WidgetType {
|
||||
span.addEventListener("click", (e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
this.onClick();
|
||||
this.handleClick(span);
|
||||
});
|
||||
return span;
|
||||
}
|
||||
@@ -23,7 +23,7 @@ class AssetHeaderWidget extends WidgetType {
|
||||
|
||||
const assetHeaderRegex = /[/;][*;](\{.+?\})[*;][/;]/g;
|
||||
|
||||
function buildAssetHeaderDecorations(view: EditorView, onClick: () => void): DecorationSet {
|
||||
function buildAssetHeaderDecorations(view: EditorView, handleClick: (span: HTMLElement) => void): DecorationSet {
|
||||
const widgets: any[] = [];
|
||||
for (let { from, to } of view.visibleRanges) {
|
||||
const text = view.state.sliceDoc(from, to);
|
||||
@@ -36,7 +36,7 @@ function buildAssetHeaderDecorations(view: EditorView, onClick: () => void): Dec
|
||||
const lineEnd = lineStart + line.length;
|
||||
widgets.push(
|
||||
Decoration.widget({
|
||||
widget: new AssetHeaderWidget(m[0], onClick),
|
||||
widget: new AssetHeaderWidget(m[0], handleClick),
|
||||
side: 1,
|
||||
}).range(lineEnd)
|
||||
);
|
||||
@@ -47,15 +47,22 @@ function buildAssetHeaderDecorations(view: EditorView, onClick: () => void): Dec
|
||||
return Decoration.set(widgets, true);
|
||||
}
|
||||
|
||||
export function createAssetHeaderPlugin(onClick: () => void) {
|
||||
export function createAssetHeaderPlugin(onClick: (lineNumber: number) => void) {
|
||||
let currentView: EditorView;
|
||||
function handleClick(span: HTMLElement) {
|
||||
const pos = currentView.posAtDOM(span);
|
||||
onClick(currentView.state.doc.lineAt(pos).number);
|
||||
}
|
||||
return ViewPlugin.fromClass(class {
|
||||
decorations: DecorationSet;
|
||||
constructor(view: EditorView) {
|
||||
this.decorations = buildAssetHeaderDecorations(view, onClick);
|
||||
currentView = view;
|
||||
this.decorations = buildAssetHeaderDecorations(view, handleClick);
|
||||
}
|
||||
update(update: ViewUpdate) {
|
||||
currentView = update.view;
|
||||
if (update.docChanged || update.viewportChanged) {
|
||||
this.decorations = buildAssetHeaderDecorations(update.view, onClick);
|
||||
this.decorations = buildAssetHeaderDecorations(update.view, handleClick);
|
||||
}
|
||||
}
|
||||
}, {
|
||||
|
||||
@@ -22,7 +22,6 @@ export class AssetEditorView implements ProjectView, pixed.EditorContext {
|
||||
cureditnode: pixed.PixNode;
|
||||
rootnodes: pixed.PixNode[];
|
||||
deferrednodes: pixed.PixNode[];
|
||||
|
||||
createDiv(parent: HTMLElement) {
|
||||
this.maindiv = newDiv(parent, "vertical-scroll");
|
||||
return this.maindiv[0];
|
||||
@@ -134,6 +133,21 @@ export class AssetEditorView implements ProjectView, pixed.EditorContext {
|
||||
node = node.left;
|
||||
}
|
||||
this.cureditnode = node;
|
||||
this.updateHashForSelection(div);
|
||||
}
|
||||
|
||||
updateHashForSelection(div: JQuery) {
|
||||
if (typeof window === 'undefined') return;
|
||||
var block = div ? div.closest('.asset_block[data-fileid]') : null;
|
||||
var fileid = block && block.attr('data-fileid');
|
||||
var startline = block && block.attr('data-startline');
|
||||
var hash = '#asseteditor';
|
||||
if (fileid && startline) {
|
||||
hash += '/' + encodeURIComponent(fileid) + '/' + startline;
|
||||
}
|
||||
if (window.location.hash !== hash) {
|
||||
history.replaceState(null, '', hash);
|
||||
}
|
||||
}
|
||||
|
||||
scanFileTextForAssets(id: string, data: string) {
|
||||
@@ -330,7 +344,9 @@ export class AssetEditorView implements ProjectView, pixed.EditorContext {
|
||||
} else if (typeof data === 'string') {
|
||||
let textfrags = this.scanFileTextForAssets(fileid, data);
|
||||
for (let frag of textfrags) {
|
||||
const block = $('<div class="asset_block"/>').appendTo(this.ensureFileDiv(fileid));
|
||||
const block = $('<div class="asset_block"/>')
|
||||
.attr({ 'data-fileid': fileid, 'data-startline': frag.startline })
|
||||
.appendTo(this.ensureFileDiv(fileid));
|
||||
var snip = $('<div class="asset_snip"/>').appendTo(block);
|
||||
var linenos = $('<span class="asset_linenos"/>').appendTo(snip);
|
||||
$('<span class="asset_lineno"/>').text(frag.startline).appendTo(linenos);
|
||||
@@ -422,6 +438,7 @@ export class AssetEditorView implements ProjectView, pixed.EditorContext {
|
||||
}
|
||||
});
|
||||
this.deferrednodes = [];
|
||||
this.scrollToAssetFromHash();
|
||||
} else {
|
||||
for (var node of this.rootnodes) {
|
||||
node.refreshRight();
|
||||
@@ -429,6 +446,24 @@ export class AssetEditorView implements ProjectView, pixed.EditorContext {
|
||||
}
|
||||
}
|
||||
|
||||
scrollToAssetFromHash(): void {
|
||||
var hash = window.location.hash;
|
||||
if (!hash || !hash.startsWith('#asseteditor/')) return;
|
||||
var parts = hash.substring(1).split('/'); // ['asseteditor', ...filename, startline]
|
||||
var fileid = decodeURIComponent(parts.slice(1, -1).join('/'));
|
||||
var startline = parts[parts.length - 1];
|
||||
if (!fileid || !startline) return;
|
||||
// defer to allow DOM to settle
|
||||
setTimeout(() => {
|
||||
const block = this.maindiv.find(`.asset_block[data-fileid="${fileid}"][data-startline="${startline}"]`);
|
||||
if (block.length) {
|
||||
block[0].scrollIntoView({ behavior: "smooth", block: "center" });
|
||||
block.addClass('asset_highlight');
|
||||
setTimeout(() => block.removeClass('asset_highlight'), 500);
|
||||
}
|
||||
}, 0);
|
||||
}
|
||||
|
||||
setVisible?(showing: boolean): void {
|
||||
// TODO: make into toolbar?
|
||||
if (showing) {
|
||||
|
||||
@@ -245,8 +245,8 @@ export class SourceEditor implements ProjectView {
|
||||
|
||||
highlightLines.field,
|
||||
|
||||
createAssetHeaderPlugin(() => {
|
||||
projectWindows.createOrShow('#asseteditor');
|
||||
createAssetHeaderPlugin((lineNumber: number) => {
|
||||
window.location.hash = 'asseteditor/' + encodeURIComponent(this.path) + '/' + lineNumber;
|
||||
}),
|
||||
|
||||
textTransformFilterCompartment.of([]),
|
||||
|
||||
+2
-1
@@ -83,7 +83,8 @@ export class ProjectWindows {
|
||||
}
|
||||
} else {
|
||||
const hash = id.startsWith('#') ? id : '#' + encodeURIComponent(id);
|
||||
if (window.location.hash !== hash) {
|
||||
// don't overwrite an extended hash (e.g. #asseteditor/file/line) with the base hash
|
||||
if (window.location.hash !== hash && !window.location.hash.startsWith(hash + '/')) {
|
||||
history.replaceState(null, '', hash);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user