586 lines
25 KiB
HTML
586 lines
25 KiB
HTML
<div class="drive-container" x-data="driveApp()" x-cloak>
|
|
<!-- Header -->
|
|
<div class="drive-header">
|
|
<div class="header-content">
|
|
<h1 class="drive-title">
|
|
<span class="drive-icon">📁</span>
|
|
General Bots Drive
|
|
</h1>
|
|
<div class="header-actions">
|
|
<button class="button-primary" @click="showUploadDialog = true">
|
|
<svg
|
|
width="20"
|
|
height="20"
|
|
viewBox="0 0 24 24"
|
|
fill="none"
|
|
stroke="currentColor"
|
|
stroke-width="2"
|
|
>
|
|
<path
|
|
d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"
|
|
></path>
|
|
<polyline points="17 8 12 3 7 8"></polyline>
|
|
<line x1="12" y1="3" x2="12" y2="15"></line>
|
|
</svg>
|
|
Upload
|
|
</button>
|
|
<button class="button-secondary" @click="createFolder()">
|
|
<svg
|
|
width="20"
|
|
height="20"
|
|
viewBox="0 0 24 24"
|
|
fill="none"
|
|
stroke="currentColor"
|
|
stroke-width="2"
|
|
>
|
|
<path
|
|
d="M22 19a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5l2 3h9a2 2 0 0 1 2 2z"
|
|
></path>
|
|
<line x1="12" y1="11" x2="12" y2="17"></line>
|
|
<line x1="9" y1="14" x2="15" y2="14"></line>
|
|
</svg>
|
|
New Folder
|
|
</button>
|
|
</div>
|
|
</div>
|
|
<div class="search-bar">
|
|
<svg
|
|
width="20"
|
|
height="20"
|
|
viewBox="0 0 24 24"
|
|
fill="none"
|
|
stroke="currentColor"
|
|
stroke-width="2"
|
|
>
|
|
<circle cx="11" cy="11" r="8"></circle>
|
|
<path d="m21 21-4.35-4.35"></path>
|
|
</svg>
|
|
<input
|
|
type="text"
|
|
x-model="searchQuery"
|
|
placeholder="Search files and folders..."
|
|
class="search-input"
|
|
/>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Main Content -->
|
|
<div class="drive-layout">
|
|
<!-- Sidebar Navigation -->
|
|
<aside class="drive-sidebar">
|
|
<div class="sidebar-section">
|
|
<h3 class="sidebar-heading">Quick Access</h3>
|
|
<template x-for="item in quickAccess" :key="item.id">
|
|
<button
|
|
class="nav-item"
|
|
:class="{ active: currentView === item.id }"
|
|
@click="currentView = item.id"
|
|
>
|
|
<span class="nav-icon" x-text="item.icon"></span>
|
|
<span class="nav-label" x-text="item.label"></span>
|
|
<span
|
|
class="nav-badge"
|
|
x-show="item.count"
|
|
x-text="item.count"
|
|
></span>
|
|
</button>
|
|
</template>
|
|
</div>
|
|
|
|
<div class="sidebar-section">
|
|
<h3 class="sidebar-heading">Storage</h3>
|
|
<div class="storage-info">
|
|
<div class="storage-bar">
|
|
<div
|
|
class="storage-used"
|
|
:style="`width: ${storagePercent}%`"
|
|
></div>
|
|
</div>
|
|
<p class="storage-text">
|
|
<span x-text="storageUsed"></span> of
|
|
<span x-text="storageTotal"></span> used
|
|
</p>
|
|
</div>
|
|
</div>
|
|
</aside>
|
|
|
|
<!-- File Tree and List -->
|
|
<main class="drive-main">
|
|
<!-- Breadcrumb -->
|
|
<div class="breadcrumb">
|
|
<template x-for="(crumb, index) in breadcrumbs" :key="index">
|
|
<span class="breadcrumb-item">
|
|
<button
|
|
@click="navigateToPath(crumb.path)"
|
|
x-text="crumb.name"
|
|
></button>
|
|
<span
|
|
class="breadcrumb-separator"
|
|
x-show="index < breadcrumbs.length - 1"
|
|
>/</span
|
|
>
|
|
</span>
|
|
</template>
|
|
</div>
|
|
|
|
<!-- View Toggle -->
|
|
<div class="view-controls">
|
|
<div class="view-toggle">
|
|
<button
|
|
class="view-button"
|
|
:class="{ active: viewMode === 'tree' }"
|
|
@click="viewMode = 'tree'"
|
|
title="Tree View"
|
|
>
|
|
<svg
|
|
width="20"
|
|
height="20"
|
|
viewBox="0 0 24 24"
|
|
fill="none"
|
|
stroke="currentColor"
|
|
stroke-width="2"
|
|
>
|
|
<line x1="8" y1="6" x2="21" y2="6"></line>
|
|
<line x1="8" y1="12" x2="21" y2="12"></line>
|
|
<line x1="8" y1="18" x2="21" y2="18"></line>
|
|
<line x1="3" y1="6" x2="3.01" y2="6"></line>
|
|
<line x1="3" y1="12" x2="3.01" y2="12"></line>
|
|
<line x1="3" y1="18" x2="3.01" y2="18"></line>
|
|
</svg>
|
|
</button>
|
|
<button
|
|
class="view-button"
|
|
:class="{ active: viewMode === 'grid' }"
|
|
@click="viewMode = 'grid'"
|
|
title="Grid View"
|
|
>
|
|
<svg
|
|
width="20"
|
|
height="20"
|
|
viewBox="0 0 24 24"
|
|
fill="none"
|
|
stroke="currentColor"
|
|
stroke-width="2"
|
|
>
|
|
<rect x="3" y="3" width="7" height="7"></rect>
|
|
<rect x="14" y="3" width="7" height="7"></rect>
|
|
<rect x="14" y="14" width="7" height="7"></rect>
|
|
<rect x="3" y="14" width="7" height="7"></rect>
|
|
</svg>
|
|
</button>
|
|
</div>
|
|
<select class="sort-select" x-model="sortBy">
|
|
<option value="name">Name</option>
|
|
<option value="modified">Modified</option>
|
|
<option value="size">Size</option>
|
|
<option value="type">Type</option>
|
|
</select>
|
|
</div>
|
|
|
|
<!-- Tree View -->
|
|
<div class="file-tree" x-show="viewMode === 'tree'">
|
|
<template x-for="item in filteredItems" :key="item.id">
|
|
<div>
|
|
<div
|
|
class="tree-item"
|
|
:class="{
|
|
selected: selectedItem?.id === item.id,
|
|
folder: item.type === 'folder'
|
|
}"
|
|
:style="`padding-left: ${item.depth * 24 + 12}px`"
|
|
@click="selectItem(item)"
|
|
@dblclick="item.type === 'folder' && toggleFolder(item)"
|
|
>
|
|
<button
|
|
class="tree-toggle"
|
|
x-show="item.type === 'folder'"
|
|
@click.stop="toggleFolder(item)"
|
|
>
|
|
<svg
|
|
width="16"
|
|
height="16"
|
|
viewBox="0 0 24 24"
|
|
fill="none"
|
|
stroke="currentColor"
|
|
stroke-width="2"
|
|
>
|
|
<polyline
|
|
:points="item.expanded ? '6 9 12 15 18 9' : '9 18 15 12 9 6'"
|
|
></polyline>
|
|
</svg>
|
|
</button>
|
|
<span
|
|
class="tree-icon"
|
|
x-text="getFileIcon(item)"
|
|
></span>
|
|
<span class="tree-label" x-text="item.name"></span>
|
|
<span class="tree-meta">
|
|
<span
|
|
class="tree-size"
|
|
x-show="item.type !== 'folder'"
|
|
x-text="item.size"
|
|
></span>
|
|
<span
|
|
class="tree-date"
|
|
x-text="item.modified"
|
|
></span>
|
|
</span>
|
|
<div class="tree-actions">
|
|
<button
|
|
class="action-button"
|
|
x-show="isEditableFile(item)"
|
|
@click.stop="editFile(item)"
|
|
title="Edit"
|
|
>
|
|
<svg
|
|
width="16"
|
|
height="16"
|
|
viewBox="0 0 24 24"
|
|
fill="none"
|
|
stroke="currentColor"
|
|
stroke-width="2"
|
|
>
|
|
<path
|
|
d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7"
|
|
></path>
|
|
<path
|
|
d="M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z"
|
|
></path>
|
|
</svg>
|
|
</button>
|
|
<button
|
|
class="action-button"
|
|
@click.stop="downloadItem(item)"
|
|
title="Download"
|
|
>
|
|
<svg
|
|
width="16"
|
|
height="16"
|
|
viewBox="0 0 24 24"
|
|
fill="none"
|
|
stroke="currentColor"
|
|
stroke-width="2"
|
|
>
|
|
<path
|
|
d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"
|
|
></path>
|
|
<polyline
|
|
points="7 10 12 15 17 10"
|
|
></polyline>
|
|
<line
|
|
x1="12"
|
|
y1="15"
|
|
x2="12"
|
|
y2="3"
|
|
></line>
|
|
</svg>
|
|
</button>
|
|
<button
|
|
class="action-button"
|
|
@click.stop="shareItem(item)"
|
|
title="Share"
|
|
>
|
|
<svg
|
|
width="16"
|
|
height="16"
|
|
viewBox="0 0 24 24"
|
|
fill="none"
|
|
stroke="currentColor"
|
|
stroke-width="2"
|
|
>
|
|
<circle cx="18" cy="5" r="3"></circle>
|
|
<circle cx="6" cy="12" r="3"></circle>
|
|
<circle cx="18" cy="19" r="3"></circle>
|
|
<line
|
|
x1="8.59"
|
|
y1="13.51"
|
|
x2="15.42"
|
|
y2="17.49"
|
|
></line>
|
|
<line
|
|
x1="15.41"
|
|
y1="6.51"
|
|
x2="8.59"
|
|
y2="10.49"
|
|
></line>
|
|
</svg>
|
|
</button>
|
|
<button
|
|
class="action-button danger"
|
|
@click.stop="deleteItem(item)"
|
|
title="Delete"
|
|
>
|
|
<svg
|
|
width="16"
|
|
height="16"
|
|
viewBox="0 0 24 24"
|
|
fill="none"
|
|
stroke="currentColor"
|
|
stroke-width="2"
|
|
>
|
|
<polyline
|
|
points="3 6 5 6 21 6"
|
|
></polyline>
|
|
<path
|
|
d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"
|
|
></path>
|
|
</svg>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
<template
|
|
x-if="item.type === 'folder' && item.expanded"
|
|
>
|
|
<div x-html="renderChildren(item)"></div>
|
|
</template>
|
|
</div>
|
|
</template>
|
|
</div>
|
|
|
|
<!-- Grid View -->
|
|
<div class="file-grid" x-show="viewMode === 'grid'">
|
|
<template x-for="item in filteredItems" :key="item.id">
|
|
<div
|
|
class="grid-item"
|
|
:class="{ selected: selectedItem?.id === item.id }"
|
|
@click="selectItem(item)"
|
|
@dblclick="item.type === 'folder' && openFolder(item)"
|
|
>
|
|
<div class="grid-icon" x-text="getFileIcon(item)"></div>
|
|
<div class="grid-name" x-text="item.name"></div>
|
|
<div class="grid-meta">
|
|
<span
|
|
x-show="item.type !== 'folder'"
|
|
x-text="item.size"
|
|
></span>
|
|
<span x-text="item.modified"></span>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
</div>
|
|
|
|
<!-- Empty State -->
|
|
<div class="empty-state" x-show="filteredItems.length === 0">
|
|
<svg
|
|
width="80"
|
|
height="80"
|
|
viewBox="0 0 24 24"
|
|
fill="none"
|
|
stroke="currentColor"
|
|
stroke-width="1"
|
|
>
|
|
<path
|
|
d="M22 19a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5l2 3h9a2 2 0 0 1 2 2z"
|
|
></path>
|
|
</svg>
|
|
<h3>No files found</h3>
|
|
<p>Upload files or create a new folder to get started</p>
|
|
</div>
|
|
</main>
|
|
|
|
<!-- Details Panel -->
|
|
<aside class="drive-details" x-show="selectedItem">
|
|
<div class="details-header">
|
|
<h3>Details</h3>
|
|
<button class="close-button" @click="selectedItem = null">
|
|
<svg
|
|
width="20"
|
|
height="20"
|
|
viewBox="0 0 24 24"
|
|
fill="none"
|
|
stroke="currentColor"
|
|
stroke-width="2"
|
|
>
|
|
<line x1="18" y1="6" x2="6" y2="18"></line>
|
|
<line x1="6" y1="6" x2="18" y2="18"></line>
|
|
</svg>
|
|
</button>
|
|
</div>
|
|
<template x-if="selectedItem">
|
|
<div class="details-content">
|
|
<div class="details-preview">
|
|
<div
|
|
class="preview-icon"
|
|
x-text="getFileIcon(selectedItem)"
|
|
></div>
|
|
</div>
|
|
<div class="details-info">
|
|
<h4 x-text="selectedItem.name"></h4>
|
|
<div class="info-row">
|
|
<span class="info-label">Type</span>
|
|
<span
|
|
class="info-value"
|
|
x-text="selectedItem.type"
|
|
></span>
|
|
</div>
|
|
<div
|
|
class="info-row"
|
|
x-show="selectedItem.type !== 'folder'"
|
|
>
|
|
<span class="info-label">Size</span>
|
|
<span
|
|
class="info-value"
|
|
x-text="selectedItem.size"
|
|
></span>
|
|
</div>
|
|
<div class="info-row">
|
|
<span class="info-label">Modified</span>
|
|
<span
|
|
class="info-value"
|
|
x-text="selectedItem.modified"
|
|
></span>
|
|
</div>
|
|
<div class="info-row">
|
|
<span class="info-label">Created</span>
|
|
<span
|
|
class="info-value"
|
|
x-text="selectedItem.created"
|
|
></span>
|
|
</div>
|
|
</div>
|
|
<div class="details-actions">
|
|
<button
|
|
class="button-primary"
|
|
x-show="isEditableFile(selectedItem)"
|
|
@click="editFile(selectedItem)"
|
|
>
|
|
<svg
|
|
width="16"
|
|
height="16"
|
|
viewBox="0 0 24 24"
|
|
fill="none"
|
|
stroke="currentColor"
|
|
stroke-width="2"
|
|
>
|
|
<path
|
|
d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7"
|
|
></path>
|
|
<path
|
|
d="M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z"
|
|
></path>
|
|
</svg>
|
|
Edit
|
|
</button>
|
|
<button
|
|
class="button-secondary"
|
|
@click="downloadItem(selectedItem)"
|
|
>
|
|
Download
|
|
</button>
|
|
<button
|
|
class="button-secondary"
|
|
@click="shareItem(selectedItem)"
|
|
>
|
|
Share
|
|
</button>
|
|
<button
|
|
class="button-secondary danger"
|
|
@click="deleteItem(selectedItem)"
|
|
>
|
|
Delete
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
</aside>
|
|
</div>
|
|
|
|
<!-- Text Editor Modal -->
|
|
<div
|
|
class="editor-modal"
|
|
x-show="showEditor"
|
|
x-cloak
|
|
@click.self="closeEditor()"
|
|
>
|
|
<div class="editor-container">
|
|
<!-- Editor Header -->
|
|
<div class="editor-header">
|
|
<div class="editor-title">
|
|
<svg
|
|
width="20"
|
|
height="20"
|
|
viewBox="0 0 24 24"
|
|
fill="none"
|
|
stroke="currentColor"
|
|
stroke-width="2"
|
|
>
|
|
<path
|
|
d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7"
|
|
></path>
|
|
<path
|
|
d="M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z"
|
|
></path>
|
|
</svg>
|
|
<span x-text="editorFileName"></span>
|
|
</div>
|
|
<div class="editor-actions">
|
|
<button
|
|
class="button-primary"
|
|
@click="saveFile()"
|
|
:disabled="editorSaving"
|
|
>
|
|
<svg
|
|
width="16"
|
|
height="16"
|
|
viewBox="0 0 24 24"
|
|
fill="none"
|
|
stroke="currentColor"
|
|
stroke-width="2"
|
|
>
|
|
<path
|
|
d="M19 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11l5 5v11a2 2 0 0 1-2 2z"
|
|
></path>
|
|
<polyline points="17 21 17 13 7 13 7 21"></polyline>
|
|
<polyline points="7 3 7 8 15 8"></polyline>
|
|
</svg>
|
|
<span
|
|
x-text="editorSaving ? 'Saving...' : 'Save'"
|
|
></span>
|
|
</button>
|
|
<button class="button-secondary" @click="closeEditor()">
|
|
<svg
|
|
width="16"
|
|
height="16"
|
|
viewBox="0 0 24 24"
|
|
fill="none"
|
|
stroke="currentColor"
|
|
stroke-width="2"
|
|
>
|
|
<line x1="18" y1="6" x2="6" y2="18"></line>
|
|
<line x1="6" y1="6" x2="18" y2="18"></line>
|
|
</svg>
|
|
Close
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Editor Content -->
|
|
<div class="editor-content">
|
|
<template x-if="editorLoading">
|
|
<div class="editor-loading">
|
|
<div class="loading-spinner"></div>
|
|
<p>Loading file...</p>
|
|
</div>
|
|
</template>
|
|
<template x-if="!editorLoading">
|
|
<textarea
|
|
class="editor-textarea"
|
|
x-model="editorContent"
|
|
placeholder="Start typing..."
|
|
spellcheck="false"
|
|
></textarea>
|
|
</template>
|
|
</div>
|
|
|
|
<!-- Editor Footer -->
|
|
<div class="editor-footer">
|
|
<span class="editor-info">
|
|
<span x-text="editorContent.length"></span> characters ·
|
|
<span x-text="editorContent.split('\\n').length"></span>
|
|
lines
|
|
</span>
|
|
<span class="editor-path" x-text="editorFilePath"></span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|