fix: move giteaWorkspace() script before x-data div to fix init race
The <script> defining giteaWorkspace() was placed AFTER the <div x-data> in the DOM, causing Alpine to try to evaluate giteaWorkspace() before the function was defined. This resulted in 'Uncaught ReferenceError' for all component properties (fileLoading, fileLanguage, showFile, etc.). Fix: moved the <script> block to appear BEFORE the x-data div in the HTML source, ensuring the function is defined when Alpine initializes.
This commit is contained in:
+154
-154
@@ -35,160 +35,6 @@
|
||||
.gw-content pre[class*="language-"]{background:var(--bg-tertiary)!important;border:1px solid var(--border)!important;border-radius:8px!important;padding:16px!important;}
|
||||
</style>
|
||||
|
||||
<div class="gw-main" x-data="giteaWorkspace()">
|
||||
<!-- File view -->
|
||||
<div x-show="showFile && !showEditor" x-transition>
|
||||
<div class="gw-file-toolbar">
|
||||
<strong x-text="filePath || ''"></strong>
|
||||
<span x-text="formatSize(fileSize)"></span>
|
||||
<span style="flex:1;"></span>
|
||||
<button class="btn" @click="openEditor()">✏️ Edit</button>
|
||||
<button class="btn btn-danger" @click="deleteCurrentFile()">🗑 Delete</button>
|
||||
</div>
|
||||
<div class="gw-content">
|
||||
<!-- Skeleton loading -->
|
||||
<div x-show="fileLoading" class="skeleton-file">
|
||||
<div class="skeleton" style="height:15px;width:60%;margin-bottom:12px;"></div>
|
||||
<div class="skeleton" style="height:15px;width:80%;margin-bottom:12px;"></div>
|
||||
<div class="skeleton" style="height:15px;width:40%;margin-bottom:12px;"></div>
|
||||
<div class="skeleton" style="height:15px;width:75%;margin-bottom:12px;"></div>
|
||||
<div class="skeleton" style="height:15px;width:55%;margin-bottom:12px;"></div>
|
||||
<div class="skeleton" style="height:15px;width:90%;margin-bottom:12px;"></div>
|
||||
<div class="skeleton" style="height:15px;width:35%;margin-bottom:12px;"></div>
|
||||
<div class="skeleton" style="height:15px;width:65%;margin-bottom:12px;"></div>
|
||||
<div class="skeleton" style="height:15px;width:45%;margin-bottom:12px;"></div>
|
||||
<div class="skeleton" style="height:15px;width:70%;margin-bottom:12px;"></div>
|
||||
<div class="skeleton" style="height:15px;width:50%;"></div>
|
||||
</div>
|
||||
<!-- Syntax-highlighted code -->
|
||||
<pre x-show="!fileLoading"><code :class="'language-'+fileLanguage" x-ref="codeBlock" x-text="fileContent"></code></pre>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Editor view -->
|
||||
<template x-if="showEditor">
|
||||
<div>
|
||||
<div class="gw-file-toolbar">
|
||||
<strong>Editing: <span x-text="filePath || 'new file'"></span></strong>
|
||||
<span style="flex:1;"></span>
|
||||
<button class="btn" @click="showEditor=false">Cancel</button>
|
||||
</div>
|
||||
<div class="gw-content">
|
||||
<textarea x-model="editContent"></textarea>
|
||||
</div>
|
||||
<div class="gw-commit-bar">
|
||||
<input type="text" x-model="commitMessage" placeholder="Commit message" list="commit-history">
|
||||
<datalist id="commit-history">
|
||||
<template x-for="msg in commitHistory" :key="msg">
|
||||
<option :value="msg">
|
||||
</template>
|
||||
</datalist>
|
||||
<button class="btn-primary" @click="saveFile()">💾 Commit</button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<!-- File tree browser (like local-workspace) -->
|
||||
<template x-if="!showFile && !showEditor && !showPrivate">
|
||||
<div class="gw-content">
|
||||
<!-- Breadcrumb -->
|
||||
<div class="gw-breadcrumb" style="display:flex;align-items:center;gap:4px;margin-bottom:16px;font-size:14px;color:var(--text-secondary);">
|
||||
<a href="#" @click.prevent="navigateToRoot()" style="color:var(--text-secondary);text-decoration:none;" x-text="owner + '/' + repo"></a>
|
||||
<template x-for="(b, i) in folderStack" :key="i">
|
||||
<span style="display:contents;">
|
||||
<span style="color:var(--text-tertiary);">/</span>
|
||||
<a href="#" @click.prevent="navigateToFolder(i)" style="color:var(--text-secondary);text-decoration:none;" x-text="b"></a>
|
||||
</span>
|
||||
</template>
|
||||
<span x-show="treeLoading" style="color:var(--text-tertiary);">Loading...</span>
|
||||
</div>
|
||||
|
||||
<!-- Tree items -->
|
||||
<div x-show="!treeLoading">
|
||||
<template x-if="treeItems.length === 0">
|
||||
<div class="empty-state">
|
||||
<h2>📁 Empty</h2>
|
||||
<p>No files in this folder</p>
|
||||
<button class="btn btn-primary" @click="showNewFile=!showNewFile">📄+ New file</button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<div class="ws-tree">
|
||||
<template x-for="item in sortedTreeItems" :key="item.path">
|
||||
<div class="ws-tree-item"
|
||||
@click="item.type==='folder' ? drillDown(item.path) : openFile(item.path, item.sha)"
|
||||
@contextmenu.prevent="openGwContext($event, item)"
|
||||
style="display:flex;align-items:center;gap:8px;padding:6px 8px;border-radius:6px;cursor:pointer;">
|
||||
<span x-text="item.type==='folder' ? '📁' : '📄'" style="font-size:16px;"></span>
|
||||
<span x-text="item.name" style="flex:1;font-size:14px;" :style="{ fontWeight: item.type==='folder' ? '500' : '400' }"></span>
|
||||
<span x-text="item.type==='folder' ? '' : formatSize(item.size)" style="font-size:12px;color:var(--text-tertiary);"></span>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
|
||||
<!-- Context menu -->
|
||||
<div class="gw-context-menu"
|
||||
x-show="gwCtx.visible"
|
||||
:style="'left:' + gwCtx.x + 'px; top:' + gwCtx.y + 'px'"
|
||||
@click.outside="gwCtx.visible = false"
|
||||
style="display:none;position:fixed;background:var(--bg-secondary);border:1px solid var(--border-strong);border-radius:8px;box-shadow:0 8px 24px rgba(0,0,0,.4);z-index:1100;min-width:160px;padding:4px;">
|
||||
<div class="ctx-item" @click="gwRename()">✏️ Rename</div>
|
||||
<div class="ctx-item ctx-danger" @click="gwDelete()">🗑 Delete</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- New file form -->
|
||||
<div x-show="showNewFile" style="margin-top:16px;max-width:400px;">
|
||||
<input type="text" x-model="newFilePath" placeholder="path/to/file.md" class="settings-input" style="margin-bottom:8px;">
|
||||
<textarea x-model="newFileContent" placeholder="File content..." class="settings-input" style="height:120px;margin-bottom:8px;"></textarea>
|
||||
<input type="text" x-model="newFileMsg" placeholder="Commit message" class="settings-input" style="margin-bottom:8px;">
|
||||
<button class="btn btn-primary" @click="createNewFile()">💾 Create file</button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<!-- Private pages view -->
|
||||
<template x-if="showPrivate && !editingPrivate">
|
||||
<div class="gw-content">
|
||||
<div style="display:flex;align-items:center;justify-content:space-between;margin-bottom:16px;">
|
||||
<h3>🔒 Private Pages</h3>
|
||||
<button class="btn btn-primary" @click="newPrivate()">+ New Page</button>
|
||||
</div>
|
||||
<template x-if="privatePages.length === 0">
|
||||
<div class="empty-state" style="padding:40px 20px;">
|
||||
<p>No private pages yet. Create one to store notes linked to this project.</p>
|
||||
</div>
|
||||
</template>
|
||||
<template x-for="pp in privatePages" :key="pp.id">
|
||||
<div style="background:var(--bg-secondary);border:1px solid var(--border);border-radius:8px;padding:16px;margin-bottom:8px;cursor:pointer;display:flex;align-items:center;justify-content:space-between;"
|
||||
@click="openPrivate(pp.id)">
|
||||
<div>
|
||||
<div style="font-weight:500;" x-text="pp.title"></div>
|
||||
<div style="font-size:12px;color:var(--text-dim);" x-text="pp.updated_at ? new Date(pp.updated_at+'Z').toLocaleString() : ''"></div>
|
||||
</div>
|
||||
<button class="btn btn-danger" style="padding:4px 8px;font-size:11px;" @click.stop="deletePrivate(pp.id)">🗑</button>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<!-- Private page editor -->
|
||||
<template x-if="editingPrivate">
|
||||
<div>
|
||||
<div class="gw-file-toolbar">
|
||||
<strong>Editing: <span x-text="editingPrivateTitle || 'Untitled'"></span></strong>
|
||||
<span style="flex:1;"></span>
|
||||
<button class="btn" @click="savePrivate()">💾 Save</button>
|
||||
<button class="btn" @click="editingPrivate=null;editingPrivateTitle='';editingPrivateContent='';">Cancel</button>
|
||||
</div>
|
||||
<div class="gw-content">
|
||||
<input x-model="editingPrivateTitle" placeholder="Page title" style="width:100%;padding:10px 12px;background:var(--bg-tertiary);border:1px solid var(--border);border-radius:8px;color:var(--text);font-size:16px;margin-bottom:12px;outline:none;">
|
||||
<textarea x-model="editingPrivateContent" placeholder="Start writing…" style="min-height:60vh;"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
function giteaWorkspace() {
|
||||
const params = new URLSearchParams(window.location.search);
|
||||
@@ -814,4 +660,158 @@ function giteaWorkspace() {
|
||||
};
|
||||
}
|
||||
</script>
|
||||
<div class="gw-main" x-data="giteaWorkspace()">
|
||||
<!-- File view -->
|
||||
<div x-show="showFile && !showEditor" x-transition>
|
||||
<div class="gw-file-toolbar">
|
||||
<strong x-text="filePath || ''"></strong>
|
||||
<span x-text="formatSize(fileSize)"></span>
|
||||
<span style="flex:1;"></span>
|
||||
<button class="btn" @click="openEditor()">✏️ Edit</button>
|
||||
<button class="btn btn-danger" @click="deleteCurrentFile()">🗑 Delete</button>
|
||||
</div>
|
||||
<div class="gw-content">
|
||||
<!-- Skeleton loading -->
|
||||
<div x-show="fileLoading" class="skeleton-file">
|
||||
<div class="skeleton" style="height:15px;width:60%;margin-bottom:12px;"></div>
|
||||
<div class="skeleton" style="height:15px;width:80%;margin-bottom:12px;"></div>
|
||||
<div class="skeleton" style="height:15px;width:40%;margin-bottom:12px;"></div>
|
||||
<div class="skeleton" style="height:15px;width:75%;margin-bottom:12px;"></div>
|
||||
<div class="skeleton" style="height:15px;width:55%;margin-bottom:12px;"></div>
|
||||
<div class="skeleton" style="height:15px;width:90%;margin-bottom:12px;"></div>
|
||||
<div class="skeleton" style="height:15px;width:35%;margin-bottom:12px;"></div>
|
||||
<div class="skeleton" style="height:15px;width:65%;margin-bottom:12px;"></div>
|
||||
<div class="skeleton" style="height:15px;width:45%;margin-bottom:12px;"></div>
|
||||
<div class="skeleton" style="height:15px;width:70%;margin-bottom:12px;"></div>
|
||||
<div class="skeleton" style="height:15px;width:50%;"></div>
|
||||
</div>
|
||||
<!-- Syntax-highlighted code -->
|
||||
<pre x-show="!fileLoading"><code :class="'language-'+fileLanguage" x-ref="codeBlock" x-text="fileContent"></code></pre>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Editor view -->
|
||||
<template x-if="showEditor">
|
||||
<div>
|
||||
<div class="gw-file-toolbar">
|
||||
<strong>Editing: <span x-text="filePath || 'new file'"></span></strong>
|
||||
<span style="flex:1;"></span>
|
||||
<button class="btn" @click="showEditor=false">Cancel</button>
|
||||
</div>
|
||||
<div class="gw-content">
|
||||
<textarea x-model="editContent"></textarea>
|
||||
</div>
|
||||
<div class="gw-commit-bar">
|
||||
<input type="text" x-model="commitMessage" placeholder="Commit message" list="commit-history">
|
||||
<datalist id="commit-history">
|
||||
<template x-for="msg in commitHistory" :key="msg">
|
||||
<option :value="msg">
|
||||
</template>
|
||||
</datalist>
|
||||
<button class="btn-primary" @click="saveFile()">💾 Commit</button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<!-- File tree browser (like local-workspace) -->
|
||||
<template x-if="!showFile && !showEditor && !showPrivate">
|
||||
<div class="gw-content">
|
||||
<!-- Breadcrumb -->
|
||||
<div class="gw-breadcrumb" style="display:flex;align-items:center;gap:4px;margin-bottom:16px;font-size:14px;color:var(--text-secondary);">
|
||||
<a href="#" @click.prevent="navigateToRoot()" style="color:var(--text-secondary);text-decoration:none;" x-text="owner + '/' + repo"></a>
|
||||
<template x-for="(b, i) in folderStack" :key="i">
|
||||
<span style="display:contents;">
|
||||
<span style="color:var(--text-tertiary);">/</span>
|
||||
<a href="#" @click.prevent="navigateToFolder(i)" style="color:var(--text-secondary);text-decoration:none;" x-text="b"></a>
|
||||
</span>
|
||||
</template>
|
||||
<span x-show="treeLoading" style="color:var(--text-tertiary);">Loading...</span>
|
||||
</div>
|
||||
|
||||
<!-- Tree items -->
|
||||
<div x-show="!treeLoading">
|
||||
<template x-if="treeItems.length === 0">
|
||||
<div class="empty-state">
|
||||
<h2>📁 Empty</h2>
|
||||
<p>No files in this folder</p>
|
||||
<button class="btn btn-primary" @click="showNewFile=!showNewFile">📄+ New file</button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<div class="ws-tree">
|
||||
<template x-for="item in sortedTreeItems" :key="item.path">
|
||||
<div class="ws-tree-item"
|
||||
@click="item.type==='folder' ? drillDown(item.path) : openFile(item.path, item.sha)"
|
||||
@contextmenu.prevent="openGwContext($event, item)"
|
||||
style="display:flex;align-items:center;gap:8px;padding:6px 8px;border-radius:6px;cursor:pointer;">
|
||||
<span x-text="item.type==='folder' ? '📁' : '📄'" style="font-size:16px;"></span>
|
||||
<span x-text="item.name" style="flex:1;font-size:14px;" :style="{ fontWeight: item.type==='folder' ? '500' : '400' }"></span>
|
||||
<span x-text="item.type==='folder' ? '' : formatSize(item.size)" style="font-size:12px;color:var(--text-tertiary);"></span>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
|
||||
<!-- Context menu -->
|
||||
<div class="gw-context-menu"
|
||||
x-show="gwCtx.visible"
|
||||
:style="'left:' + gwCtx.x + 'px; top:' + gwCtx.y + 'px'"
|
||||
@click.outside="gwCtx.visible = false"
|
||||
style="display:none;position:fixed;background:var(--bg-secondary);border:1px solid var(--border-strong);border-radius:8px;box-shadow:0 8px 24px rgba(0,0,0,.4);z-index:1100;min-width:160px;padding:4px;">
|
||||
<div class="ctx-item" @click="gwRename()">✏️ Rename</div>
|
||||
<div class="ctx-item ctx-danger" @click="gwDelete()">🗑 Delete</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- New file form -->
|
||||
<div x-show="showNewFile" style="margin-top:16px;max-width:400px;">
|
||||
<input type="text" x-model="newFilePath" placeholder="path/to/file.md" class="settings-input" style="margin-bottom:8px;">
|
||||
<textarea x-model="newFileContent" placeholder="File content..." class="settings-input" style="height:120px;margin-bottom:8px;"></textarea>
|
||||
<input type="text" x-model="newFileMsg" placeholder="Commit message" class="settings-input" style="margin-bottom:8px;">
|
||||
<button class="btn btn-primary" @click="createNewFile()">💾 Create file</button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<!-- Private pages view -->
|
||||
<template x-if="showPrivate && !editingPrivate">
|
||||
<div class="gw-content">
|
||||
<div style="display:flex;align-items:center;justify-content:space-between;margin-bottom:16px;">
|
||||
<h3>🔒 Private Pages</h3>
|
||||
<button class="btn btn-primary" @click="newPrivate()">+ New Page</button>
|
||||
</div>
|
||||
<template x-if="privatePages.length === 0">
|
||||
<div class="empty-state" style="padding:40px 20px;">
|
||||
<p>No private pages yet. Create one to store notes linked to this project.</p>
|
||||
</div>
|
||||
</template>
|
||||
<template x-for="pp in privatePages" :key="pp.id">
|
||||
<div style="background:var(--bg-secondary);border:1px solid var(--border);border-radius:8px;padding:16px;margin-bottom:8px;cursor:pointer;display:flex;align-items:center;justify-content:space-between;"
|
||||
@click="openPrivate(pp.id)">
|
||||
<div>
|
||||
<div style="font-weight:500;" x-text="pp.title"></div>
|
||||
<div style="font-size:12px;color:var(--text-dim);" x-text="pp.updated_at ? new Date(pp.updated_at+'Z').toLocaleString() : ''"></div>
|
||||
</div>
|
||||
<button class="btn btn-danger" style="padding:4px 8px;font-size:11px;" @click.stop="deletePrivate(pp.id)">🗑</button>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<!-- Private page editor -->
|
||||
<template x-if="editingPrivate">
|
||||
<div>
|
||||
<div class="gw-file-toolbar">
|
||||
<strong>Editing: <span x-text="editingPrivateTitle || 'Untitled'"></span></strong>
|
||||
<span style="flex:1;"></span>
|
||||
<button class="btn" @click="savePrivate()">💾 Save</button>
|
||||
<button class="btn" @click="editingPrivate=null;editingPrivateTitle='';editingPrivateContent='';">Cancel</button>
|
||||
</div>
|
||||
<div class="gw-content">
|
||||
<input x-model="editingPrivateTitle" placeholder="Page title" style="width:100%;padding:10px 12px;background:var(--bg-tertiary);border:1px solid var(--border);border-radius:8px;color:var(--text);font-size:16px;margin-bottom:12px;outline:none;">
|
||||
<textarea x-model="editingPrivateContent" placeholder="Start writing…" style="min-height:60vh;"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
|
||||
Reference in New Issue
Block a user