Skip to content

Commit

Permalink
Stylin
Browse files Browse the repository at this point in the history
  • Loading branch information
platypii committed Jun 6, 2024
1 parent 2c4509e commit d2d6d32
Show file tree
Hide file tree
Showing 10 changed files with 278 additions and 15 deletions.
2 changes: 1 addition & 1 deletion public/bundle.min.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion public/bundle.min.js.map

Large diffs are not rendered by default.

Binary file added public/favicon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions public/file.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions public/folder.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
8 changes: 8 additions & 0 deletions public/logo.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
204 changes: 204 additions & 0 deletions public/styles.css
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,207 @@
margin: 0;
padding: 0;
}

#app {
display: flex;
flex-direction: column;
height: 100vh;
}

.brand {
display: flex !important;
align-items: center;
font-family: 'Century Gothic', 'Helvetica Neue', Helvetica, Arial, sans-serif;
font-size: 1.1em;
font-weight: bold;
text-shadow: 0 0 1px #222;
user-select: none;
}
.brand img {
height: 26px;
width: 26px;
margin-right: 10px;
filter: drop-shadow(0 0 1px #505050);
}

a {
color: #342267;
cursor: pointer;
text-decoration: none;
}
a:hover {
color: #000;
text-decoration: underline;
}

/* layout */
main {
display: flex;
height: 100vh;
max-width: 100vw;
}

/* sidebar */
.nav {
display: flex;
flex-direction: column;
height: 100vh;
min-width: 48px;
background-image: linear-gradient(to bottom, #667, #585669);
box-shadow: 0 0 4px rgba(10, 10, 10, 0.5);
display: flex;
flex-direction: column;
height: 100vh;
}

/* nav links */
.nav ul {
list-style: none;
}
.nav li {
margin: 0;
}
.nav a {
color: #ddd;
cursor: pointer;
display: block;
padding: 10px 12px;
text-decoration: none;
user-select: none;
white-space: nowrap;
}

/* content area */
.content-container {
min-width: 0;
height: 100vh;
display: flex;
flex-direction: column;
flex: 1;
}
.content {
display: flex;
flex-direction: column;
flex: 1;
height: 100vh;
overflow-y: auto;
padding: 0;
/* no outer scrollbars */
overflow: hidden;
}

/* top navbar */
.top-header {
align-items: center;
background: linear-gradient(to right, #353540, #24202b);
color: #dde4ea;
display: flex;
height: 32px;
justify-content: space-between;
min-height: 32px;
padding-left: 8px;
}
.top-header h1 {
font-size: 18px;
margin: 0;
}
.top-header a {
color: #e0e8ee;
}

/* file path */
.path {
margin: 0 2px;
margin-right: 4px;
min-width: 0;
overflow: auto;
}
.path::-webkit-scrollbar {
display: none;
}
.path a {
color: #f0f8ff;
font-family: 'Courier New', Courier, monospace;
font-size: 18px;
text-overflow: ellipsis;
white-space: nowrap;
text-decoration-thickness: 1px;
}
/* hide all but the last path link on small screens */
@media (max-width: 360px) {
.path a:not(:last-child) {
display: none;
}
}

/* error bar */
.error-bar {
max-height: 0;
padding: 0;
background-color: #dd111199;
font-family: monospace;
overflow-y: auto;
transition: max-height 0.3s;
white-space: pre-wrap;
}
.show-error {
max-height: 30%;
padding: 10px;
}

/* file list */
.file-list {
flex: 1;
list-style: none;
overflow-y: auto;
/* browsers like to cover the bottom row */
padding-bottom: 24px;
}
.file-list li {
margin: 0;
}
.file-list li:first-child a {
border-top: none;
}
.file-list a {
border-top: 1px solid #bbb;
color: #444;
display: flex;
padding: 8px 12px;
text-decoration: none;
}
.file-list a:hover {
background-color: #e2e2ee;
}

.file-list a > span {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}

.file-name {
flex: 1;
min-width: 80px;
}
.file-size {
color: #666;
margin: 0 16px;
text-align: right;
}
.file-date {
width: 80px;
text-align: right;
}

/* file icons */
.file {
background: url(/public/file.svg);
background-position: left center;
background-repeat: no-repeat;
background-size: 12px;
padding-left: 22px;
}
.folder {
background-image: url(/public/folder.svg);
}
7 changes: 3 additions & 4 deletions src/Folder.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ export default function Folder() {

// Folder path from url
const path = location.pathname.split('/')
let prefix = decodeURI(path.slice(2).join('/'))
const prefix = decodeURI(path.slice(2).join('/'))

// Fetch files on component mount
useEffect(() => {
Expand All @@ -26,8 +26,7 @@ export default function Folder() {
}, [prefix])

function fileUrl(file: FileMetadata): string {
const key = prefix + '/' + file.key
return file.key.endsWith('/') ? `/files/${key}` : `/files/${key}`
return prefix ? `/files/${prefix}/${file.key}` : `/files/${file.key}`
}

return (
Expand All @@ -45,7 +44,7 @@ export default function Folder() {
{files.map((file, index) =>
<li key={index}>
<a href={fileUrl(file)}>
<span className={cn('file-name', file.key.endsWith('/') ? 'icon-directory' : 'icon-file')}>
<span className={cn('file-name', 'file', file.key.endsWith('/') && 'folder')}>
{file.key}
</span>
{!file.key.endsWith('/') && <>
Expand Down
22 changes: 13 additions & 9 deletions src/Layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -43,15 +43,19 @@ export default function Layout({ children, className, error, title }: LayoutProp

function Sidebar() {
return (
<nav>
<a className="brand" href='/'>
<img
alt="hyperparam"
height={26}
src="/assets/logo.svg"
width={26} />
hyperparam
</a>
<nav className='nav'>
<ul>
<li>
<a className="brand" href='/'>
<img
alt="hyperparam"
height={26}
src="/public/logo.svg"
width={26} />
hyperparam
</a>
</li>
</ul>
</nav>
)
}
Expand Down
42 changes: 42 additions & 0 deletions src/serve.js
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,10 @@ function handleRequest(req) {
} else if (pathname.startsWith('/public/')) {
// serve static files
return handleStatic(pathname.slice(7))
} else if (pathname === '/api/store/list') {
// serve file list
const prefix = parsedUrl.query.prefix?.[0] || ''
return handleListing(prefix)
} else {
return { status: 404, content: 'not found' }
}
Expand Down Expand Up @@ -81,6 +85,44 @@ async function handleStatic(pathname) {
return { status: 200, content, contentType }
}

/**
* List files from local storage
*
* @param {string} prefix file path prefix
* @returns {Promise<ServeResult>}
*/
async function handleListing(prefix) {
const dir = `${process.cwd()}/${prefix}`
try {
const stat = await fs.stat(dir)
if (!stat.isDirectory()) return { status: 400, content: 'not a directory' }
} catch (error) {
return { status: 404, content: 'not found' }
}

const files = []
for (const filename of await fs.readdir(dir, { recursive: false })) {
// get stats for each file
const filePath = `${dir}/${filename}`
const stat = await fs.stat(filePath)

if (stat.isFile()) {
files.push({
key: filename,
fileSize: stat.size,
lastModified: stat.mtime.toISOString(),
})
} else if (stat.isDirectory()) {
files.push({
key: filename + '/',
lastModified: stat.mtime.toISOString(),
})
}
}

return { status: 200, content: JSON.stringify(files), contentType: 'application/json' }
}

/**
* Start http server on given port
* @param {number} port
Expand Down

0 comments on commit d2d6d32

Please sign in to comment.