Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions bin/serve.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,12 @@ const mimeTypes = {
'.woff2': 'font/woff2',
}

// Paths to ignore for logging
const ignorePrefix = [
'/favicon.svg',
'/assets/',
]

/**
* @template T
* @typedef {T | Promise<T>} Awaitable<T>
Expand Down Expand Up @@ -286,6 +292,11 @@ function startServer(port, path) {
pipe(content, res)
}

// Ignored logging paths
if (ignorePrefix.some(p => req.url?.startsWith(p))) {
return
}

// log request
const endTime = new Date()
const ms = endTime.getTime() - startTime.getTime()
Expand Down
4 changes: 2 additions & 2 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@
<meta charset="UTF-8">
<title>hyperparam</title>
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Public+Sans:wght@300;500&display=swap"/>
<link href="favicon.png" rel="icon" />
<link href="favicon.svg" rel="icon" />
<meta name="description" content="hyperparam is the missing UI for machine learning" />
<meta name="theme-color" content="#6b00ff">
<meta name="theme-color" content="#4433aa">
<meta name="viewport" content="width=device-width, initial-scale=1" />
</head>
<body>
Expand Down
10 changes: 5 additions & 5 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -58,15 +58,15 @@
"hightable": "0.20.1",
"hyparquet": "1.19.0",
"hyparquet-compressors": "1.1.1",
"icebird": "0.3.0"
"icebird": "0.3.1"
},
"devDependencies": {
"@eslint/js": "9.37.0",
"@storybook/react-vite": "9.1.10",
"@testing-library/react": "16.3.0",
"@types/node": "24.7.0",
"@types/react": "19.2.0",
"@types/react-dom": "19.2.0",
"@types/node": "24.7.2",
"@types/react": "19.2.2",
"@types/react-dom": "19.2.1",
"@vitejs/plugin-react": "5.0.4",
"@vitest/coverage-v8": "3.2.4",
"eslint": "9.37.0",
Expand All @@ -82,7 +82,7 @@
"react-dom": "19.2.0",
"storybook": "9.1.10",
"typescript": "5.9.3",
"typescript-eslint": "8.45.0",
"typescript-eslint": "8.46.0",
"vite": "7.1.9",
"vitest": "3.2.4"
},
Expand Down
Binary file removed public/favicon.png
Binary file not shown.
6 changes: 6 additions & 0 deletions public/favicon.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
24 changes: 15 additions & 9 deletions src/components/Breadcrumb/Breadcrumb.module.css
Original file line number Diff line number Diff line change
@@ -1,16 +1,23 @@
:root {
--color-background-dark: #333;
--border-radius-lg: 8px;
--space-3xs: clamp(0.3125rem, 0.3125rem + 0vw, 0.3125rem);
}

.breadcrumb {
/* top navbar */
align-items: center;
display: flex;
font-size: 18px;
height: 32px;
font-size: 16px;
height: 3em;
justify-content: space-between;
gap: 10px;
min-height: 32px;
padding-left: 20px;
padding-right: 10px;
border-bottom: 1px solid #ddd;
background: #eee;
background: var(--color-background-dark);
padding: 0 10px 0 20px;
border-radius: var(--border-radius-lg);
margin: var(--space-3xs);
/* TODO(SL): forbid overflow? */

h1 {
Expand All @@ -32,10 +39,9 @@
display: none;
}
a {
color: #222622;
font-family: "Courier New", Courier, monospace;
font-weight: 600;
font-size: 18px;
color: #eee;
font-weight: 500;
font-size: 12pt;
text-overflow: ellipsis;
white-space: nowrap;
text-decoration-thickness: 1px;
Expand Down
4 changes: 2 additions & 2 deletions src/components/ContentWrapper/ContentWrapper.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,13 @@

& > header:first-child {
align-items: center;
background-color: #f2f2f2;
color: #444;
display: flex;
font-size: 10pt;
gap: 16px;
height: 24px;
overflow: hidden;
padding: 0 16px;
padding: 0 8px;
/* all one line */
text-overflow: ellipsis;
white-space: nowrap;
Expand Down
5 changes: 0 additions & 5 deletions src/components/Dropdown/Dropdown.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,6 @@
overflow-x: hidden;
padding: 0;
}
.dropdownButton:active,
.dropdownButton:focus,
.dropdownButton:hover {
color: #113;
}

/* caret */
.dropdownButton::before {
Expand Down
2 changes: 1 addition & 1 deletion src/components/Folder/Folder.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@
.search {
background: #fff url("../../assets/search.svg") no-repeat center right 8px;
border: 1px solid transparent;
border-radius: 8px;
border-radius: 12px;
flex-shrink: 1;
font-size: 12px;
height: 24px;
Expand Down
4 changes: 2 additions & 2 deletions src/components/SideBar/SideBar.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
height: 100vh;
}
.sideBar > div {
background-image: linear-gradient(to bottom, #f2f2f2, #e4e4e4);
background: #eef0f9;
box-shadow: 0 0 6px rgba(10, 10, 10, 0.4);
height: 100vh;
position: absolute;
Expand All @@ -23,7 +23,7 @@
filter: drop-shadow(0 0 2px #bbb);
font-family: "Century Gothic", "Helvetica Neue", Helvetica, Arial, sans-serif;
font-size: 1.1em;
font-weight: 500;
font-weight: 600;
text-orientation: mixed;
letter-spacing: 0.3px;
padding: 10px 12px;
Expand Down
16 changes: 4 additions & 12 deletions src/lib/sources/hyperparamSource.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,6 @@ export interface HyperparamFileMetadata {
lastModified: string
}

function canParse(sourceId: string): boolean {
/// we expect relative paths, such as path/to/file or path/to/dir/
/// let's just check that it is empty or starts with a "word" character
return sourceId === '' || /^[\w]/.test(sourceId)
}

function getSourceParts(sourceId: string): SourcePart[] {
const parts = sourceId.split('/')
const sourceParts = [
Expand Down Expand Up @@ -66,12 +60,10 @@ async function listFiles(prefix: string, { endpoint, requestInit }: {endpoint: s
}

export function getHyperparamSource(sourceId: string, { endpoint, requestInit }: {endpoint: string, requestInit?: RequestInit}): FileSource | DirSource | undefined {
if (!URL.canParse(endpoint)) {
throw new Error('Invalid endpoint')
}
if (!canParse(sourceId)) {
return undefined
}
if (!URL.canParse(endpoint)) throw new Error('Invalid endpoint')
if (sourceId.startsWith('/')) throw new Error('Source cannot start with a /')
if (sourceId.includes('..')) throw new Error('Source cannot include ..')

const sourceParts = getSourceParts(sourceId)
if (getKind(sourceId) === 'file') {
return {
Expand Down
33 changes: 16 additions & 17 deletions test/lib/sources/hyperparamSource.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,27 +6,26 @@ globalThis.fetch = vi.fn()
describe('getHyperparamSource', () => {
const endpoint = 'http://localhost:3000'

test.for([
'test.txt',
'no-extension',
'folder/subfolder/test.txt',
])('recognizes a local file path', (sourceId: string) => {
expect(getHyperparamSource(sourceId, { endpoint })?.kind).toBe('file')
it('recognizes local files', () => {
expect(getHyperparamSource('test.txt', { endpoint })?.kind).toBe('file')
expect(getHyperparamSource('no-extension', { endpoint })?.kind).toBe('file')
expect(getHyperparamSource('folder/subfolder/test.txt', { endpoint })?.kind).toBe('file')
})

test.for([
'',
'folder1/',
'folder1/folder2/',
])('recognizes a folder', (sourceId: string) => {
expect(getHyperparamSource(sourceId, { endpoint })?.kind).toBe('directory')
it('recognizes folders', () => {
expect(getHyperparamSource('', { endpoint })?.kind).toBe('directory')
expect(getHyperparamSource('folder1/', { endpoint })?.kind).toBe('directory')
expect(getHyperparamSource('folder1/folder2/', { endpoint })?.kind).toBe('directory')
})

test.for([
'/',
'////',
])('does not support a heading slash', (sourceId: string) => {
expect(getHyperparamSource(sourceId, { endpoint })).toBeUndefined()
it('throws on leading slash', () => {
expect(() => getHyperparamSource('/', { endpoint })).toThrow('Source cannot start with a /')
expect(() => getHyperparamSource('/folder/', { endpoint })).toThrow('Source cannot start with a /')
})

it('throws on .. in path', () => {
expect(() => getHyperparamSource('..', { endpoint })).toThrow('Source cannot include ..')
expect(() => getHyperparamSource('folder/../file.txt', { endpoint })).toThrow('Source cannot include ..')
})

test.for([
Expand Down