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
1 change: 0 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ desktop/dist/
desktop/out
server/datastores/
server/temp/
server/assets/scripts/widget.styles.gen.ts

.env
.env.production
Expand Down
40 changes: 18 additions & 22 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -422,35 +422,31 @@ Pour que l'agent IA cite systématiquement ses sources dans ses réponses, ajout

### Via une fenêtre modale

Le bouton launcher est **vôtre** (HTML + CSS). PIERRE injecte automatiquement l'iframe modale — aucune feuille de style sur votre page (hors un effet d'animation à l'apparition de la fenêtre modale).

```html
<script crossorigin="anonymous" src="https://180.81.82.83/assets/dist/js/widget.js"></script>
<p
id="pierre-ia"
data-url="https://180.81.82.83"
data-configuration="default"
style="
right: 20px;
bottom: 20px;
color: #000;
font-size: 30px;
font-weight: bold;
padding: 2px 12px;
background-color: #fff;
border-radius: 8px;
border: 4px solid black;
box-shadow: 2px 2px 6px #00000080;"
<script
crossorigin="anonymous"
src="https://180.81.82.83/assets/dist/js/pierre.js"
data-pierre-config="default"
async
></script>

<button
type="button"
data-pierre-open
style="position:fixed; bottom:20px; right:20px; z-index:9999;"
>
iA?
</p>
</button>
```

avec :

- `iA?` : le nom d'affichage du bouton (libre à vous de le modifier)
- `style` : le style CSS du bouton (libre à vous de le modifier)
- `180.81.82.83` dans l'URL du script le domaine/IP du serveur où le script est accessible
- `data-url` : le domaine/IP (sans slash de fin) du serveur où PIERRE est accessible
- `data-configuration` : `default` ou le nom du répertoire que vous avez créé plus tôt dans `./customization/chatbot` (_cf._ supra).
- `src` : URL absolue du serveur PIERRE (`/assets/dist/js/pierre.js`)
- `data-pierre-config` : `default` ou nom du répertoire dans `./customization/chatbot` si l'on souhaite afficher un différent profil.
- `data-pierre-open` : marque le bouton qui ouvre la modale (obligatoire)
- `style` sur le bouton : entièrement libre et à votre main

### Via une iframe

Expand Down
128 changes: 84 additions & 44 deletions bun.lock

Large diffs are not rendered by default.

70 changes: 40 additions & 30 deletions config/build-server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,13 @@ import toc from 'markdown-toc'

const ROOT = import.meta.dir.replace(/\/config$/, '')
const SERVER = join(ROOT, 'server')
const WIDGET_SCRIPTS = join(SERVER, 'assets/scripts')
const PIERRE_HOST = join(SERVER, 'assets/host')
const PIERRE_EMBED = join(SERVER, 'assets/embed')

const WIDGET_SHELL_RADIUS = '12px'
const WIDGET_SHADOW_REST = '0 2px 8px rgba(0, 0, 0, 0.04), 0 16px 48px -12px rgba(15, 23, 42, 0.18)'
const WIDGET_NOISE_BG = `url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 512 512'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.78' numOctaves='4' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E")`
const PIERRE_MODAL_SHELL_RADIUS = '12px'
const PIERRE_MODAL_SHADOW_REST =
'0 2px 8px rgba(0, 0, 0, 0.04), 0 16px 48px -12px rgba(15, 23, 42, 0.18)'
const PIERRE_MODAL_NOISE_BG = `url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 512 512'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.78' numOctaves='4' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E")`

function minifyCss(css: string): string {
return css
Expand All @@ -21,22 +23,23 @@ function minifyCss(css: string): string {
.trim()
}

async function generateWidgetStyles(): Promise<void> {
const css = await Bun.file(join(WIDGET_SCRIPTS, 'widget.css')).text()
const minified = minifyCss(css)
.replaceAll('__NOISE_BG__', WIDGET_NOISE_BG)
.replaceAll('__SHELL_RADIUS__', WIDGET_SHELL_RADIUS)
.replaceAll('__SHADOW_REST__', WIDGET_SHADOW_REST)
async function buildPierreEmbedModalCss(): Promise<string> {
const css = await Bun.file(join(PIERRE_EMBED, 'pierre-embed-modal.css')).text()
return minifyCss(css)
.replaceAll('__NOISE_BG__', PIERRE_MODAL_NOISE_BG)
.replaceAll('__SHELL_RADIUS__', PIERRE_MODAL_SHELL_RADIUS)
.replaceAll('__SHADOW_REST__', PIERRE_MODAL_SHADOW_REST)
}

await Bun.write(
join(WIDGET_SCRIPTS, 'widget.styles.gen.ts'),
`export const WIDGET_CSS = ${JSON.stringify(minified)}\n`
)
function assertPierreHostMinified(pierrePath: string, pierre: string): void {
if (pierre.includes('pierre_is_open') || pierre.includes('pierre-embed-modal')) {
throw new Error(`${pierrePath} was not minified — check oxfmt/format-on-save`)
}
}

function assertWidgetMinified(widgetPath: string, widget: string): void {
if (widget.includes('pierre_is_open') || widget.includes('\n.pierre-ia,\n')) {
throw new Error(`${widgetPath} was not minified — check oxfmt/format-on-save`)
function assertPierreEmbedMinified(pierreEmbedPath: string, pierreEmbed: string): void {
if (pierreEmbed.includes('\nconst MODAL_ID')) {
throw new Error(`${pierreEmbedPath} was not minified — check oxfmt/format-on-save`)
}
}

Expand All @@ -46,6 +49,7 @@ const timestamp = Date.now()
// Remove old files
await $`rm -rf ${SERVER}/assets/dist/css`
await $`rm -rf ${SERVER}/assets/dist/js`
await $`rm -f ${ROOT}/docs/assets/pierre.js`
await $`rm -f ${ROOT}/docs/assets/widget.js`
await $`find ${ROOT} -name ".DS_Store" -type f -delete`

Expand Down Expand Up @@ -75,17 +79,28 @@ for (const mdRoot of mdRoots) {
await $`bun lint`
await $`bun format`

await generateWidgetStyles()

// Compile production CSS file
await $`bunx @tailwindcss/cli@latest -i ${SERVER}/assets/tailwind/style.css -o ${SERVER}/assets/dist/css/style.${timestamp}.css --minify`

const embed_frame_css = minifyCss(
await Bun.file(join(PIERRE_EMBED, 'pierre-embed-frame.css')).text()
)
await Bun.write(join(SERVER, 'assets/dist/css/pierre-embed-frame.css'), embed_frame_css)
await Bun.write(
join(SERVER, 'assets/dist/css/pierre-embed-modal.css'),
await buildPierreEmbedModalCss()
)

// Transpile and minify .ts/.tsx scripts into .js to work in browser.
await $`bun build --entrypoints ${SERVER}/assets/scripts/ai.tsx ${SERVER}/assets/scripts/widget.ts --outdir ${SERVER}/assets/dist/js --minify --target browser`
await $`mv ${SERVER}/assets/dist/js/ai.js ${SERVER}/assets/dist/js/ai.${timestamp}.js`
await $`bun build ${SERVER}/assets/scripts/ai.tsx --outfile ${SERVER}/assets/dist/js/ai.${timestamp}.js --minify --target browser`
await $`bun build ${PIERRE_HOST}/pierre.ts --outfile ${SERVER}/assets/dist/js/pierre.js --minify --target browser`
await $`bun build ${PIERRE_EMBED}/pierre-embed.ts --outfile ${SERVER}/assets/dist/js/pierre-embed.js --minify --target browser`

const pierreDistPath = join(SERVER, 'assets/dist/js/pierre.js')
assertPierreHostMinified(pierreDistPath, await Bun.file(pierreDistPath).text())

const widgetDistPath = join(SERVER, 'assets/dist/js/widget.js')
assertWidgetMinified(widgetDistPath, await Bun.file(widgetDistPath).text())
const pierreEmbedDistPath = join(SERVER, 'assets/dist/js/pierre-embed.js')
assertPierreEmbedMinified(pierreEmbedDistPath, await Bun.file(pierreEmbedDistPath).text())

// Update "timestamped filepath" in all Views
const views = await readdir(join(SERVER, 'views'))
Expand All @@ -105,12 +120,7 @@ for (const view of views) {
)
}

// Copy transpiled/minified widget.js in `docs` folder, aka PIERRE website
await $`cp ${SERVER}/assets/dist/js/widget.js ${ROOT}/docs/assets`

assertWidgetMinified(
join(ROOT, 'docs/assets/widget.js'),
await Bun.file(join(ROOT, 'docs/assets/widget.js')).text()
)
// Copy pierre.js for the PIERRE website (docs)
await $`cp ${SERVER}/assets/dist/js/pierre.js ${ROOT}/docs/assets`

console.log(`✅ BUILD DONE!`)
1 change: 1 addition & 0 deletions docs/assets/pierre.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading
Loading