Skip to content

Commit b2d29c0

Browse files
committed
Implement page with Bewesoft's collection
1 parent 9d765a3 commit b2d29c0

File tree

10 files changed

+2033
-28
lines changed

10 files changed

+2033
-28
lines changed

package-lock.json

Lines changed: 26 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,9 @@
3838
"type": "module",
3939
"dependencies": {
4040
"@humanspeak/svelte-markdown": "^0.7.7",
41+
"@lucide/svelte": "^0.484.0",
4142
"@svelte-plugins/tooltips": "^3.0.1",
43+
"copy-to-clipboard": "^3.3.3",
4244
"qrcode": "^1.5.4",
4345
"slugify": "^1.6.6",
4446
"spayd": "^3.0.4",

src/app.css

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,15 @@ a.plain {
4242
text-decoration-thickness: 1px;
4343
}
4444

45+
details summary {
46+
cursor: pointer;
47+
}
48+
49+
details summary::-webkit-details-marker,
50+
details summary::marker {
51+
color: var(--color-primary);
52+
}
53+
4554
a.plain:hover {
4655
color: var(--color-primary);
4756
text-decoration: underline;
@@ -72,11 +81,19 @@ hr {
7281
border-style: solid;
7382
}
7483

75-
code {
84+
code, .codeblock {
7685
background-color: var(--color-secondary-transparent);
7786
padding: 3px;
7887
}
7988

89+
.codeblock {
90+
margin: 0.5em 1em;
91+
padding: 1em;
92+
overflow-x: scroll;
93+
max-height: 80vh;
94+
overflow-y: scroll;
95+
}
96+
8097
button {
8198
background: transparent;
8299
border: none;

src/lib/AssetBox.svelte

Lines changed: 63 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -3,38 +3,72 @@
33
import Box from "./Box.svelte";
44
import SvelteMarkdown from '@humanspeak/svelte-markdown'
55
import Capsule from "./Capsule.svelte";
6+
import LucideDownload from "@lucide/svelte/icons/download";
67
78
interface Props {
8-
data: AssetData;
9+
data: AssetData | Promise<AssetData | undefined>;
910
}
1011
1112
let { data }: Props = $props();
13+
14+
function formatFileSize(bytes?: number | null): string {
15+
if (!bytes) return 'unknown size';
16+
const units = ['B', 'KiB', 'MiB', 'GiB'];
17+
let i = 0;
18+
while (bytes >= 1024 && i < units.length - 1) {
19+
bytes /= 1024;
20+
i++;
21+
}
22+
return `${bytes.toFixed(1)} ${units[i]}`;
23+
}
1224
</script>
1325

1426
<Box>
1527
<div class="asset">
16-
<div>
17-
<div class="asset-name">
18-
<a href="{ data.inventory_url }">
19-
{#if data.id}
20-
<Capsule>
21-
HH{data.id}
22-
</Capsule>
28+
{#await data}
29+
<em>Načítám předmět...</em>
30+
{:then data}
31+
{#if data}
32+
<div>
33+
<div class="asset-name">
34+
<a href="{ data.inventory_url }">
35+
{#if data.id}
36+
<Capsule>
37+
HH{data.id}
38+
</Capsule>
39+
{/if}
40+
</a>
41+
<a href="{ data.inventory_url }">
42+
{ data.name }
43+
</a>
44+
</div>
45+
<p>
46+
<SvelteMarkdown source={data.description} />
47+
</p>
48+
{#if data.primary_dump_path}
49+
<a href="https://inventory.herniarchiv.cz/files/{data.primary_dump_path}" class="download">
50+
<LucideDownload style="vertical-align: top;" />
51+
Stáhnout dump
52+
{#if data.primary_dump_size}
53+
({formatFileSize(data.primary_dump_size)})
54+
{/if}
55+
</a>
2356
{/if}
24-
</a>
25-
<a href="{ data.inventory_url }">
26-
{ data.name }
27-
</a>
57+
</div>
58+
{#if data.picture.url}
59+
<a href="{ data.inventory_url }" class="asset-photo">
60+
<img src="{ data.picture.url }" class="asset-img" alt="">
61+
</a>
62+
{/if}
63+
{:else}
64+
<em>Chyba: předmět nebyl dodán.</em>
65+
{/if}
66+
{:catch error}
67+
<div>
68+
<em>Chyba při načítání předmětu.</em>
69+
<div style="color: #888;">{error}</div>
2870
</div>
29-
<p>
30-
<SvelteMarkdown source={data.description} />
31-
</p>
32-
</div>
33-
{#if data.picture.url}
34-
<a href="{ data.inventory_url }" class="asset-photo">
35-
<img src="{ data.picture.url }" class="asset-img" alt="">
36-
</a>
37-
{/if}
71+
{/await}
3872
</div>
3973
</Box>
4074

@@ -65,6 +99,14 @@
6599
object-fit: cover;
66100
}
67101
102+
.download {
103+
text-decoration: none;
104+
}
105+
106+
.download:hover {
107+
text-decoration: underline;
108+
}
109+
68110
69111
@media only screen and (max-width: 600px) {
70112
.asset {

src/lib/Heading.svelte

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
<script lang="ts">
2+
import type {HTMLAttributes} from 'svelte/elements';
3+
import copy from 'copy-to-clipboard';
4+
import IconLink from '@lucide/svelte/icons/link';
5+
import { page } from '$app/state';
6+
interface Props {
7+
h: '1' | '2' | '3' | '4' | '5' | '6';
8+
children?: import('svelte').Snippet;
9+
};
10+
11+
let { h: level, children, ...props }: Props & HTMLAttributes<HTMLHeadingElement> = $props();
12+
let element = $derived(`h${level}` as 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6');
13+
</script>
14+
15+
<svelte:element class="heading" this={element} {...props}>
16+
{@render children?.()}
17+
{#if props.id}
18+
<a
19+
href="#{props.id}"
20+
class="anchor"
21+
onclick={() => {
22+
if (props.id) {
23+
const url = page.url;
24+
url.hash = props.id;
25+
copy(url.toString());
26+
}
27+
}}
28+
>
29+
<IconLink />
30+
</a>
31+
{/if}
32+
</svelte:element>
33+
34+
<style>
35+
.heading .anchor {
36+
opacity: 0;
37+
transition: opacity 0.2s;
38+
}
39+
40+
.heading:hover .anchor {
41+
opacity: 1;
42+
}
43+
</style>

src/lib/Key.svelte

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
<script lang="ts">
2+
interface Props {
3+
children?: import('svelte').Snippet;
4+
}
5+
6+
let { children }: Props = $props();
7+
</script>
8+
9+
<span class="key">
10+
{@render children?.()}
11+
</span>
12+
13+
<style>
14+
.key {
15+
font-family: 'IBM Plex Mono', monospace;
16+
background-color: #f0f0f0;
17+
padding: 0.05em 0.5em;
18+
border-radius: 0.25em;
19+
border-color: #b0b0b0;
20+
border-style: solid;
21+
border-width: 1px 2px 3px 1px;
22+
font-size: 0.8em;
23+
}
24+
</style>

src/lib/rhinventory_api.ts

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ type RHInventoryAPIAsset = {
88
description: string;
99
primary_image_path: string;
1010
primary_document_path: string;
11+
primary_dump_size: number;
1112
primary_dump_path: string;
1213
}
1314

@@ -17,26 +18,33 @@ type RHInventoryAssetsResponse = {
1718
}
1819

1920
export async function loadRHInventoryAssetData(
20-
params: {tagId: number, page: number}
21+
params: {tagId: number, page: number, pageSize?: number}
2122
): Promise<
2223
{
2324
assets: AssetData[],
2425
assetCount: number
2526
}
2627
> {
27-
// TODO handle page
28-
const offset = (params.page-1) * PAGE_SIZE
29-
const url = `https://api.inventory.herniarchiv.cz/asset/list-by-tag?tag_id=${params.tagId}&limit=${PAGE_SIZE}&offset=${offset}`;
28+
if (!params.pageSize) {
29+
params.pageSize = PAGE_SIZE;
30+
}
31+
if (params.page <= 0) {
32+
throw new Error("Page must be 1 or larger.");
33+
}
34+
const offset = (params.page-1) * params.pageSize
35+
const url = `https://api.inventory.herniarchiv.cz/asset/list-by-tag?tag_id=${params.tagId}&limit=${params.pageSize}&offset=${offset}`;
3036
const response = await fetch(url);
3137
const apiAssets = (await response.json()) as RHInventoryAssetsResponse;
3238

3339
console.log(`Loaded ${apiAssets.assets.length} assets from RHInventory API`);
3440

3541
const assets = apiAssets.assets.map(APIAsset => {
3642
return {
37-
id: APIAsset.id,
43+
id: APIAsset.id.toString(),
3844
name: APIAsset.name,
3945
picture: {url: APIAsset.primary_image_path},
46+
primary_dump_path: APIAsset.primary_dump_path,
47+
primary_dump_size: APIAsset.primary_dump_size,
4048
description: APIAsset.description,
4149
inventory_url: `https://inventory.herniarchiv.cz/asset/${APIAsset.id}` /* TODO use slug */
4250
}

src/routes/projects/ProjectBoxes.svelte

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,21 @@
1010
</script>
1111

1212
<div class={all ? "project-page": "main-page"}>
13+
<ThinBox project={all} href="/projects/bewesoft/" img="/photos/projects/bewesoft.jpg" show_arrow={false}>
14+
<h3>
15+
<a href="/projects/bewesoft/">
16+
<Loc
17+
cs="Fond Jiřího Bernáška (BeWe Soft)"
18+
en="Jiří Bernášek Collection (BeWe Soft)"
19+
/>
20+
</a>
21+
</h3>
22+
<p><Loc
23+
cs="Jiří Bernášek je bývalým tvůrcem her a programů pro osmibitové počítače Atari. Tento fond obsahuje výběr několika jeho archivních disket obsahující programy a zdrojové kódy k Bernáškově hrám a jinému softwaru."
24+
en="Jiří Bernášek is a former game and software developer for eight-bit Atari computers. This collection contains a selection of his archival floppy disks containing programs and source codes for Bernášek's games and other software."
25+
/></p>
26+
</ThinBox>
27+
1328
<ThinBox project={all} href="/projects/atari-klub-citov/" img="/photos/projects/citov.jpg" show_arrow={false}>
1429
<h3>
1530
<a href="/projects/atari-klub-citov/">
@@ -32,6 +47,7 @@
3247
/></p>
3348
</ThinBox>
3449

50+
{#if all}
3551
<ThinBox project={all} href="/gallery/emil-fafek/" img="/photos/projects/fafek.jpg" show_arrow={false}>
3652
<h3>
3753
<a href="/gallery/emil-fafek/">
@@ -47,7 +63,6 @@
4763
/></p>
4864
</ThinBox>
4965

50-
{#if all}
5166
<ThinBox project={true} href="https://www.vice.com/en/article/ouya-is-shutting-down-and-fans-are-preserving-games-before-they-disappear/" title="Ouya" img="/photos/projects/ouya.jpg" show_arrow={false}>
5267
<p>
5368
<Loc

0 commit comments

Comments
 (0)