Compare commits
6 commits
a75de86d68
...
08ec221138
Author | SHA1 | Date | |
---|---|---|---|
Julien Oculi | 08ec221138 | ||
Julien Oculi | 28397a383a | ||
Julien Oculi | a2f872ae9d | ||
Julien Oculi | b982504705 | ||
Julien Oculi | eb887377a5 | ||
Julien Oculi | d44771dab9 |
28
components/Picture.tsx
Normal file
28
components/Picture.tsx
Normal file
|
@ -0,0 +1,28 @@
|
|||
import { unwrapSignalOrValue } from ':src/utils.ts'
|
||||
import { Ensure } from ':types'
|
||||
import { JSX } from 'preact'
|
||||
|
||||
export type PictureProps =
|
||||
& { formats: string[] }
|
||||
& Ensure<JSX.HTMLAttributes<HTMLImageElement>, 'src' | 'alt' | 'loading'>
|
||||
|
||||
export function Picture(
|
||||
{ formats, src, ...props }: PictureProps,
|
||||
) {
|
||||
const groups = unwrapSignalOrValue(src)?.match(/(?<path>.*)(?<ext>\.\w+)/)
|
||||
?.groups
|
||||
if (groups === undefined) {
|
||||
throw new SyntaxError(`unable to parse path of "${src.valueOf()}"`)
|
||||
}
|
||||
|
||||
const { path } = groups
|
||||
|
||||
return (
|
||||
<picture>
|
||||
{formats.map((format) => (
|
||||
<source type={`image/${format}`} srcset={`${path}.${format}`} />
|
||||
))}
|
||||
<img src={src} {...props} />
|
||||
</picture>
|
||||
)
|
||||
}
|
|
@ -1,4 +1,5 @@
|
|||
import { asset } from '$fresh/runtime.ts'
|
||||
import { Picture } from ':components/Picture.tsx'
|
||||
|
||||
export function SponsorCards() {
|
||||
return (
|
||||
|
@ -13,7 +14,12 @@ function SponsorCard(
|
|||
) {
|
||||
return (
|
||||
<a class='components__sponsor_card' href={href} target='_blank'>
|
||||
<img src={asset(src)} alt={alt} />
|
||||
<Picture
|
||||
src={asset(src)}
|
||||
alt={alt}
|
||||
loading={'lazy'}
|
||||
formats={['avif']}
|
||||
/>
|
||||
</a>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -34,6 +34,7 @@
|
|||
":components/": "./components/",
|
||||
":islands/": "./islands/",
|
||||
":src/": "./src/",
|
||||
":types": "./types.ts",
|
||||
"@cohabit/cohamail/": "./packages/@cohabit__cohamail@0.2.1/",
|
||||
"@cohabit/ressources_manager/": "./packages/@cohabit__ressources_manager@0.1.3/",
|
||||
"@deno/gfm": "jsr:@deno/gfm@^0.8.2",
|
||||
|
|
|
@ -3,7 +3,9 @@ import { cssBundler } from '@jotsr/smart-css-bundler/fresh'
|
|||
|
||||
export default defineConfig({
|
||||
plugins: [
|
||||
cssBundler(['./src/stylesheets/main.css'] //TODO fix bundler out paths , { bundleSubDir: 'css' }
|
||||
cssBundler(
|
||||
['./src/stylesheets/main.css'], //TODO fix bundler out paths , { bundleSubDir: 'css' }
|
||||
{ externalPaths: ['assets'] },
|
||||
),
|
||||
],
|
||||
})
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { JsonParseStream } from '$std/json/mod.ts'
|
||||
import { CSS, render as renderMd } from '@deno/gfm'
|
||||
import { Markdown } from ':components/Markdown.tsx'
|
||||
import { Signal, signal, useSignal } from '@preact/signals'
|
||||
import { JSX } from 'preact'
|
||||
import { useEffect, useRef } from 'preact/hooks'
|
||||
|
@ -15,16 +15,6 @@ const currentReader = signal<ReadableStreamDefaultReader<BotResponse> | null>(
|
|||
)
|
||||
let currentResponse: string[] = []
|
||||
|
||||
function MdCell({ children }: { children: Signal<string> }) {
|
||||
return (
|
||||
<div
|
||||
class='markdown-body'
|
||||
dangerouslySetInnerHTML={{ __html: renderMd(children.value) }}
|
||||
>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
type BotMessage = {
|
||||
role: string
|
||||
content: string
|
||||
|
@ -95,7 +85,6 @@ export default function AiChatBox() {
|
|||
|
||||
return (
|
||||
<>
|
||||
<style dangerouslySetInnerHTML={{ __html: CSS }}></style>
|
||||
<button
|
||||
class='islands__ai_chat_box__button'
|
||||
onClick={() => dialog.current?.showModal()}
|
||||
|
@ -103,7 +92,9 @@ export default function AiChatBox() {
|
|||
<i class='ri-bard-line'></i>
|
||||
</button>
|
||||
<dialog ref={dialog} class='islands__ai_chat_box__dialog'>
|
||||
<div class='islands__ai_chat_box__dialog__content'>{history}</div>
|
||||
<div class='islands__ai_chat_box__dialog__content'>
|
||||
{history}
|
||||
</div>
|
||||
<form ref={form} class='islands__ai_chat_box__dialog__form'>
|
||||
<input
|
||||
type='text'
|
||||
|
@ -139,7 +130,7 @@ async function chatListener(event: Event, history: Signal<JSX.Element[]>) {
|
|||
|
||||
const botEntry = (
|
||||
<span class='islands__ai_chat_box__history_bot'>
|
||||
<MdCell>{botMessage}</MdCell>
|
||||
<Markdown>{botMessage}</Markdown>
|
||||
</span>
|
||||
)
|
||||
|
||||
|
|
|
@ -22,7 +22,7 @@ export default async function Member(_: Request, { params }: PageProps) {
|
|||
const Member = memberMock.at(id)
|
||||
|
||||
if (!Member) {
|
||||
return <h3>Membre inconnu, peut être serai vous le prochain</h3>
|
||||
return <h3>Membre inconnu, peut être serez vous le prochain</h3>
|
||||
}
|
||||
|
||||
const carnet = await getCarnet(db.at(id)!)
|
||||
|
|
|
@ -1,50 +0,0 @@
|
|||
import { Handlers, RouteConfig } from '$fresh/server.ts'
|
||||
|
||||
import { contentType } from '$std/media_types/mod.ts'
|
||||
import { parse } from '$std/path/mod.ts'
|
||||
|
||||
const db = [
|
||||
'julien.oculi',
|
||||
]
|
||||
|
||||
async function getPortfolio(
|
||||
user: string,
|
||||
pathname: string,
|
||||
): Promise<Response> {
|
||||
const url = new URL(
|
||||
pathname,
|
||||
`https://git.cohabit.fr/${user}/.portfolio/raw/branch/main/`,
|
||||
)
|
||||
|
||||
const { ext } = parse(pathname)
|
||||
|
||||
try {
|
||||
const response = await fetch(url)
|
||||
if (response.ok) {
|
||||
return new Response(response.body, {
|
||||
headers: {
|
||||
'Content-Type': contentType(ext) ?? 'text/plain; charset=utf-8',
|
||||
},
|
||||
})
|
||||
}
|
||||
throw new Error(response.statusText)
|
||||
} catch (error) {
|
||||
return new Response(
|
||||
'Portfolio introuvable\n```js\n' + String(error) + '\n```',
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export const config: RouteConfig = {
|
||||
skipAppWrapper: true,
|
||||
}
|
||||
|
||||
export const handler: Handlers = {
|
||||
GET(req, _ctx) {
|
||||
const url = new URL(req.url)
|
||||
const id = Number(url.pathname.split('/')[2])
|
||||
const user = db[id]
|
||||
const query = url.pathname.split('/').slice(4).join('/')
|
||||
return getPortfolio(user, query === '' ? 'index.html' : query)
|
||||
},
|
||||
}
|
20
src/utils.ts
20
src/utils.ts
|
@ -1,3 +1,4 @@
|
|||
import { SignalLike } from '$fresh/src/types.ts'
|
||||
import { JsonValue } from '$std/json/common.ts'
|
||||
import { decodeBase64 } from '@std/encoding/base64'
|
||||
import { JsonStringifyStream } from '@std/json'
|
||||
|
@ -171,3 +172,22 @@ export function base64ToString(base64: string): string {
|
|||
const bytes = decodeBase64(base64)
|
||||
return new TextDecoder().decode(bytes)
|
||||
}
|
||||
|
||||
export function unwrapSignalOrValue<T>(valueOrSignal: T | SignalLike<T>): T {
|
||||
if (typeof valueOrSignal !== 'object') {
|
||||
return valueOrSignal
|
||||
}
|
||||
|
||||
if (valueOrSignal === null) {
|
||||
return valueOrSignal
|
||||
}
|
||||
|
||||
if (
|
||||
'value' in valueOrSignal && 'peek' in valueOrSignal &&
|
||||
'subscribe' in valueOrSignal
|
||||
) {
|
||||
return valueOrSignal.value
|
||||
}
|
||||
|
||||
return valueOrSignal
|
||||
}
|
||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 36 KiB After Width: | Height: | Size: 5.9 KiB |
BIN
static/assets/screenshots/pwa_install_ui-desktop.jpg
Normal file
BIN
static/assets/screenshots/pwa_install_ui-desktop.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 200 KiB |
Binary file not shown.
Before Width: | Height: | Size: 1.7 MiB |
BIN
static/assets/sponsors/cap_emploi_33.avif
Normal file
BIN
static/assets/sponsors/cap_emploi_33.avif
Normal file
Binary file not shown.
BIN
static/assets/sponsors/ehpad_terre_negre.avif
Normal file
BIN
static/assets/sponsors/ehpad_terre_negre.avif
Normal file
Binary file not shown.
BIN
static/assets/sponsors/imb.avif
Normal file
BIN
static/assets/sponsors/imb.avif
Normal file
Binary file not shown.
BIN
static/assets/sponsors/inrae.avif
Normal file
BIN
static/assets/sponsors/inrae.avif
Normal file
Binary file not shown.
BIN
static/assets/sponsors/iut_bordeaux.avif
Normal file
BIN
static/assets/sponsors/iut_bordeaux.avif
Normal file
Binary file not shown.
BIN
static/assets/sponsors/maison_pour_la_science.avif
Normal file
BIN
static/assets/sponsors/maison_pour_la_science.avif
Normal file
Binary file not shown.
BIN
static/assets/sponsors/technoshop.avif
Normal file
BIN
static/assets/sponsors/technoshop.avif
Normal file
Binary file not shown.
BIN
static/assets/sponsors/universite_bordeaux.avif
Normal file
BIN
static/assets/sponsors/universite_bordeaux.avif
Normal file
Binary file not shown.
BIN
static/assets/sponsors/vegetal-signals.avif
Normal file
BIN
static/assets/sponsors/vegetal-signals.avif
Normal file
Binary file not shown.
|
@ -43,7 +43,7 @@
|
|||
],
|
||||
"screenshots": [
|
||||
{
|
||||
"src": "/assets/screenshots/pwa_install_ui-desktop.png",
|
||||
"src": "/assets/screenshots/pwa_install_ui-desktop.jpg",
|
||||
"sizes": "1920x1080",
|
||||
"type": "image/png",
|
||||
"form_factor": "wide",
|
||||
|
|
Loading…
Reference in a new issue