diff --git a/packages/preview/gibz-script/0.1.0/LICENSE.txt b/packages/preview/gibz-script/0.1.0/LICENSE.txt new file mode 100644 index 0000000000..436690a5c1 --- /dev/null +++ b/packages/preview/gibz-script/0.1.0/LICENSE.txt @@ -0,0 +1,5 @@ +MIT No Attribution (MIT-0) + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the β€œSoftware”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so. + +THE SOFTWARE IS PROVIDED β€œAS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/packages/preview/gibz-script/0.1.0/README.md b/packages/preview/gibz-script/0.1.0/README.md new file mode 100644 index 0000000000..900d07fe04 --- /dev/null +++ b/packages/preview/gibz-script/0.1.0/README.md @@ -0,0 +1,112 @@ +## GIBZ Template Package + +A template package providing reusable components for teaching and learning material at [GIBZ](https://www.gibz.ch). +It comes with a configurable document setup (`gibz-conf`) and a collection of styled components such as tasks, hints, supplementary material, and more. + +--- + +## Features + +- πŸ“„ **Configurable document layout** with title page, table of contents, and headers/footers. +- 🎨 **Consistent styling** through a `base_box` and `icon_box` system. +- ✏️ **Task component** with badges for time, social form, results recording, and evaluation. +- πŸ’‘ **Hint**, ❓ **Question**, 🎬 **Video**, πŸ“– **Supplementary Material** boxes. +- πŸ”³ **Black code box** for highlighting code snippets. +- 🌍 **Built-in i18n** (currently German and English). +- 🧩 Exposed via a **flat prefixed API** (`gibz-task`, `gibz-hint`, …) and an optional `GIBZ` namespace. + +--- + +## Quick Start + +Create a new project from this template in the Typst CLI: + +```sh +typst init @preview/gibz-script:0.1.0 +``` + +Or in the [Typst web app](https://typst.app): +**New β†’ From Template β†’ GIBZ**. + +--- + +## Usage + +### Import the flat API (recommended) + +```typst +#import "@preview/gibz-script:0.1.0": gibz-conf, gibz-task, gibz-supplementary + +#show: gibz-script( + moduleNumber: 114, + moduleTitle: "Codierungs-, Kompressions- und VerschlΓΌsselungsverfahren einsetzen", + documentTitle: "Skript", + language: "de", + doc: { + #gibz-hint("Dieses Skript wurde mit Typst geschrieben :-)") + #gibz-supplementary([Cheat Sheet]) + } +) +``` + +### Or use the namespaced API + +```typst +#import "@preview/gibz-script:0.1.0": GIBZ + +#show: GIBZ.script( + moduleNumber: 114, + moduleTitle: "Codierungs-, Kompressions- und VerschlΓΌsselungsverfahren einsetzen", + documentTitle: "Skript", + language: "de", + doc: { + #GIBZ.hint("Dieses Skript wurde mit Typst geschrieben :-)") + #GIBZ.supplementary([Cheat Sheet]) + } +) +``` + +--- + +## API Reference + +### Configuration + +- `gibz-conf(moduleNumber, moduleTitle, documentTitle, language, doc)` + Wraps your document with title page, table of contents, headers/footers, and base styles. + +### Components + +- `gibz-task(...)` – exercise/task box with time, social form, recording, evaluation. +- `gibz-hint(content)` – hint box with πŸ’‘ icon. +- `gibz-question(question, task: none)` – question box with ❓ icon, optional task description. +- `gibz-video(url, title, description: none)` – video reference with 🎬 icon. +- `gibz-supplementary(body, title: none)` – supplementary material box with πŸ“– icon. +- `gibz-black_code_box(body, codly-opts: (:), box-opts: (:))` – code box with dark background. + +### Utilities + +- `gibz-base_box(body, style: (:))` – low-level styled container. +- `gibz-icon_box(icon, content, style: (:))` – container with icon + content layout. +- `gibz-colors.blue` – standard accent color. +- `gibz-t(key, lang: none)` – translation lookup (DE/EN). + +--- + +## Localization + +The package supports German (`de`) and English (`en`) for static labels (e.g., _Exercise_, _Supplementary Material_, _Table of Contents_). +The language is set via `gibz-conf(language: "de" | "en")`. + +--- + +## License + +This package is licensed under the [MIT-0 License](LICENSE), allowing reuse with minimal restrictions. + +--- + +## Contributing + +Issues and pull requests are welcome! +The source is maintained on [GitLab](https://gitlab.com/GIBZ/public/typst-template), with mirrored contributions published to [Typst Universe](https://github.com/typst/packages). diff --git a/packages/preview/gibz-script/0.1.0/lib.typ b/packages/preview/gibz-script/0.1.0/lib.typ new file mode 100644 index 0000000000..9c40aa993f --- /dev/null +++ b/packages/preview/gibz-script/0.1.0/lib.typ @@ -0,0 +1,46 @@ +// lib.typ β€” public entrypoint with flat gibz_ API + optional GIBZ namespace. + +// Import only what we want to expose +#import "src/colors.typ": gibz-blue +#import "src/state.typ": gibz-lang // kept internal; not re-exported +#import "src/layout.typ": _conf +#import "src/components/base_box.typ": base-box +#import "src/components/icon_box.typ": icon-box +#import "src/components/boxes.typ": hint, question, supplementary, video +#import "src/components/codebox.typ": black-code-box +#import "src/components/task.typ": task +#import "src/i18n.typ": t + +#import "src/code.typ": code as gibz-code, code_wrap as _gibz-codly, set_code_style as gibz-set-code-style + + +// ── Flat API (prefixed) ────────────────────────────────────────────────────── +#let gibz-script = _conf +#let gibz-task = task +#let gibz-hint = hint +#let gibz-question = question +#let gibz-video = video +#let gibz-supplementary = supplementary +#let gibz-black-code-box = black-code-box +#let gibz-icon-box = icon-box +#let gibz-base-box = base-box +#let gibz-t = t + +// Colors (both single and grouped) +#let gibz-blue = gibz-blue +#let gibz-colors = (blue: gibz-blue) + +// ── Optional convenience namespace (no duplication; just references) ──────── +#let GIBZ = ( + script: gibz-script, + task: gibz-task, + hint: gibz-hint, + question: gibz-question, + video: gibz-video, + supplementary: gibz-supplementary, + black_code_box: gibz-black-code-box, + icon_box: gibz-icon-box, + base_box: gibz-base-box, + colors: gibz-colors, + t: gibz-t, +) diff --git a/packages/preview/gibz-script/0.1.0/src/code.typ b/packages/preview/gibz-script/0.1.0/src/code.typ new file mode 100644 index 0000000000..41b15accda --- /dev/null +++ b/packages/preview/gibz-script/0.1.0/src/code.typ @@ -0,0 +1,29 @@ +#import "@preview/codly:1.3.0": * + +#let _code_overrides = state("gibz-code-overrides", (:)) + +#let set_code_style(opts: (:)) = { + if type(opts) == dictionary { + _code_overrides.update(_code_overrides.get() + opts) + } +} + +// Lokaler Wrapper fΓΌr BlΓΆcke +#let code(block, opts: (:)) = { + context { + let ov = _code_overrides.get() + let local_opts = if type(opts) == dictionary { ov + opts } else { ov } + codly(..local_opts) + block + } +} + +// Alternative Syntax: #GIBZ.codly(...)[ body ] +#let code_wrap(opts: (:), body) = { + context { + let ov = _code_overrides.get() + let local_opts = if type(opts) == dictionary { ov + opts } else { ov } + codly(..local_opts) + body + } +} diff --git a/packages/preview/gibz-script/0.1.0/src/colors.typ b/packages/preview/gibz-script/0.1.0/src/colors.typ new file mode 100644 index 0000000000..6036de1807 --- /dev/null +++ b/packages/preview/gibz-script/0.1.0/src/colors.typ @@ -0,0 +1,2 @@ +// Central color definitions +#let gibz-blue = rgb(0, 119, 192, 255) diff --git a/packages/preview/gibz-script/0.1.0/src/components/base_box.typ b/packages/preview/gibz-script/0.1.0/src/components/base_box.typ new file mode 100644 index 0000000000..1313c363f6 --- /dev/null +++ b/packages/preview/gibz-script/0.1.0/src/components/base_box.typ @@ -0,0 +1,23 @@ +// A reusable visual container with sensible defaults. +// Override any style via the `style` dict. +#let base-box(body, style: (:)) = { + let defaults = ( + width: 100%, + fill: luma(98%), + stroke: (paint: gray, thickness: 0.5pt), + radius: 6pt, + inset: 12pt, + ) + + // Merge defaults with caller-provided styles (caller wins). + let s = defaults + style + + box( + width: s.width, + fill: s.fill, + stroke: s.stroke, + radius: s.radius, + inset: s.inset, + [ #body ], + ) +} \ No newline at end of file diff --git a/packages/preview/gibz-script/0.1.0/src/components/boxes.typ b/packages/preview/gibz-script/0.1.0/src/components/boxes.typ new file mode 100644 index 0000000000..1a13653fc8 --- /dev/null +++ b/packages/preview/gibz-script/0.1.0/src/components/boxes.typ @@ -0,0 +1,75 @@ +#import "../colors.typ": gibz-blue +#import "../state.typ": gibz-lang +#import "../i18n.typ": t +#import "./base_box.typ": base-box + +// Icon/text two-column box using base_box +#let gibz-box(icon, content) = { + set text(size: 10pt) + base-box( + [ + #grid( + columns: (50pt, 1fr), + align: (center + horizon, left + horizon), + [ + #set text(size: 20pt) + #pad(right: 15pt, bottom: 5pt, icon) + ], + [#content], + ) + ], + ) +} + +#let hint(hint) = gibz-box(emoji.lightbulb, hint) + +#let question(question, task: none) = gibz-box( + emoji.quest, + { + [ + #set text(weight: "bold") + #question + ] + if task != none { + linebreak() + [#task] + } + }, +) + +#let video(url, title, description: none) = { + show link: set text(font: "DejaVu Sans Mono", size: 7pt) + gibz-box( + [#emoji.clapperboard], + [ + #[ + #set text(weight: "bold") + #title #linebreak() + ] + #if description != none { [#description #linebreak()] } + #link(url) + ], + ) +} + +#let supplementary(body, title: none, lang: none) = { + context { + let L = if lang != none { lang } else { gibz-lang.get() } + + base-box( + [ + #block(below: 6pt, [ + #text(size: 0.75em, weight: 600, fill: gibz-blue)[πŸ“– #t("supplementary-material", lang: L)] + ]) + #v(6pt) + + #if title != none { + block(below: 6pt, [ #text(size: 1.15em, weight: 700)[#title] ]) + v(3pt) + } + + #body + ], + ) + } +} diff --git a/packages/preview/gibz-script/0.1.0/src/components/codebox.typ b/packages/preview/gibz-script/0.1.0/src/components/codebox.typ new file mode 100644 index 0000000000..960b721cb1 --- /dev/null +++ b/packages/preview/gibz-script/0.1.0/src/components/codebox.typ @@ -0,0 +1,36 @@ +#import "@preview/codly:1.3.0": * + +#let _gibz_black_code_box( + body, // Inhalt: i. d. R. ein fenced code block + codly-opts: (:), // optionale Codly-Overrides lokal fΓΌr diesen Block + box-opts: (:), // optionale Box-Optionen (inset, radius, ...) +) = { + // Codly-Defaults innerhalb der schwarzen Box + let codly-defaults = ( + zebra-fill: none, + number-format: none, + display-name: false, + stroke: none, + fill: black, + ) + let codly-final = codly-defaults + codly-opts + + box( + width: 100%, + fill: black, + inset: 5pt, + radius: 5pt, + stroke: none, + ..box-opts, + [ + #set text(fill: white) + #context { + codly(..codly-final) + body + } + ], + ) +} + +// Γ–ffentlicher Alias wie bisher +#let black-code-box = _gibz_black_code_box diff --git a/packages/preview/gibz-script/0.1.0/src/components/icon_box.typ b/packages/preview/gibz-script/0.1.0/src/components/icon_box.typ new file mode 100644 index 0000000000..d3e28103fe --- /dev/null +++ b/packages/preview/gibz-script/0.1.0/src/components/icon_box.typ @@ -0,0 +1,24 @@ +#import "./base_box.typ": base-box + +// A reusable "icon + content" box built on base_box +#let icon-box(icon, content, style: (:)) = { + base-box( + [ + #grid( + columns: (50pt, 1fr), + align: (center + horizon, left + horizon), + [ + #set text(size: 20pt) + #pad(right: 15pt, bottom: 5pt, icon) + ], + [#content], + ) + ], + style: ( + fill: gray.lighten(85%), + radius: 10pt, + inset: 15pt, + ) + + style, + ) +} diff --git a/packages/preview/gibz-script/0.1.0/src/components/task.typ b/packages/preview/gibz-script/0.1.0/src/components/task.typ new file mode 100644 index 0000000000..b8fd95b80f --- /dev/null +++ b/packages/preview/gibz-script/0.1.0/src/components/task.typ @@ -0,0 +1,116 @@ +#import "../colors.typ": gibz-blue +#import "../state.typ": gibz-lang +#import "../i18n.typ": t +#import "./base_box.typ": base-box + +#let task-theme = ( + accent: gibz-blue, + bg: luma(98%), + border: 1pt, // keep for future use if you want thicker borders + radius: 8pt, + pad: 12pt, + gap: 8pt, + label-color: luma(35%), +) + +#let _task_badge(label, theme: task-theme) = box( + inset: (x: 7pt, y: 3pt), + radius: 999pt, + stroke: 0.6pt + theme.accent, + fill: theme.bg, + [ + #set text(size: 0.70em, baseline: -1pt) + #label + ], +) + +#let _task_field(name, body, theme: task-theme) = [ + #set text(size: 0.95em) + #block(above: 2pt, below: 2pt, [ + #if name != none { + text(fill: theme.label-color, weight: 600, size: 0.75em)[#name] + linebreak() + } + #body + ]) +] + +// Public component +#let task( + title, + minutes: none, // Number or text (e.g. "2Γ—20") + social: none, // 1: individual; 2: partner; or text label + recording: none, + evaluation: none, + body, + opts: (:), // e.g. (accent: rgb("#16a34a")) + lang: none, // overrides document language ("de" | "en") +) = { + let theme = task-theme + opts + + context { + let L = if lang != none { lang } else { gibz-lang.get() } + + let time-value = if type(minutes) in (int, float) { + str(minutes) + " " + t("minutes", lang: L) + } else { minutes } + + let social-badge = if social == 1 { "πŸ‘€" } else { "πŸ‘₯" } + let social-label = if social == 1 { t("individual-work", lang: L) } else if social == 2 { + t("partner-work", lang: L) + } else { social } + + let badges = [ + #if minutes != none { + _task_badge([πŸ•’ #time-value], theme: theme) + h(6pt) + } + #if social != none { + _task_badge([#social-badge #social-label], theme: theme) + } + ] + + // Outer visual container via base_box + base-box( + [ + // Header + #block(below: 6pt, [ + #text(size: 0.75em, weight: 600, fill: theme.accent)[✏️ #t("exercise", lang: L)] + ]) + #v(6pt) + + // Title + #block(below: 6pt, [ #text(size: 1.15em, weight: 700)[#title] ]) + + // Badges + #if minutes != none or social != none { badges } + + // Spacing + #v(10pt) + + // Task body + #_task_field(none, body, theme: theme) + + // Results recording + #if recording != none { + pad(y: 10pt, line(stroke: 0.6pt + theme.accent, length: 100%)) + _task_field(t("results-recording", lang: L), recording, theme: theme) + v(4pt) + } + + // Evaluation + #if evaluation != none { + pad(y: 10pt, line(stroke: 0.6pt + theme.accent, length: 100%)) + _task_field(t("evaluation", lang: L), evaluation, theme: theme) + v(theme.gap) + } + ], + style: ( + fill: theme.bg.transparentize(50%), + stroke: (paint: theme.accent, thickness: 1pt), // keeps your colored border + radius: theme.radius, + inset: theme.pad, + ), + ) + } +} diff --git a/packages/preview/gibz-script/0.1.0/src/i18n.typ b/packages/preview/gibz-script/0.1.0/src/i18n.typ new file mode 100644 index 0000000000..64f022aa2f --- /dev/null +++ b/packages/preview/gibz-script/0.1.0/src/i18n.typ @@ -0,0 +1,48 @@ +// i18n: central place for all translatable strings +#import "../src/state.typ": gibz-lang + +// Translation table +#let _L = ( + de: ( + exercise: "Aufgabe", + results-recording: "Ergebnissicherung", + evaluation: "Auswertung", + individual-work: "Einzelarbeit", + partner-work: "Partnerarbeit", + minutes: "Minuten", + supplementary-material: "Zusatzmaterial", + table-of-contents: "Inhalt", + module-word: "Modul", + ), + en: ( + exercise: "Exercise", + results-recording: "Results recording", + evaluation: "Evaluation", + individual-work: "Individual work", + partner-work: "Partner work", + minutes: "minutes", + supplementary-material: "Supplementary Material", + table-of-contents: "Table of Contents", + module-word: "Module", + ), +) + +// Resolve a label by key. Language resolution order: +// explicit `lang` > state `gibz-lang` > "de" +#let t(key, lang: none) = { + let L = if lang != none { lang } else { gibz-lang.get() } + + // Get dictionary for this language, or English fallback + let dict = if _L.at(L) != none { _L.at(L) } else { _L.at("en") } + + // Lookup key in chosen dict + if dict.at(key) != none { + dict.at(key) + } else if _L.at("en").at(key) != none { + // Fallback to English if key missing in chosen language + _L.at("en").at(key) + } else { + // Last-resort: show the key + str(key) + } +} diff --git a/packages/preview/gibz-script/0.1.0/src/layout.typ b/packages/preview/gibz-script/0.1.0/src/layout.typ new file mode 100644 index 0000000000..e00586c3e2 --- /dev/null +++ b/packages/preview/gibz-script/0.1.0/src/layout.typ @@ -0,0 +1,119 @@ +#import "./colors.typ": gibz-blue +#import "./state.typ": gibz-lang +#import "./i18n.typ": t + +#import "@preview/octique:0.1.1": * +#import "@preview/codly:1.3.0": * +#import "@preview/codly-languages:0.1.8": * +#import "@preview/hydra:0.6.2": * +#import "@preview/ccicons:1.0.1": * + + +#let _conf( + moduleNumber: none, + moduleTitle: none, + documentTitle: none, + language: "de", + doc, +) = { + gibz-lang.update(language) + + // Title page + set page(fill: gibz-blue, margin: (left: 4cm)) + line(start: (0%, 5%), end: (8.5in, 5%), stroke: (paint: white, thickness: 2pt)) + set text(fill: white) + + align(horizon + left)[ + #text(size: 16pt, weight: "bold")[ + #smallcaps(t("module-word", lang: language) + " " + str(moduleNumber)) + ] + #linebreak() + #text(size: 20pt)[#moduleTitle] + #pad(top: 50pt, bottom: 100pt)[ + #text(size: 28pt)[#documentTitle] + ] + ] + + align(bottom + left)[#datetime.today().display("[day].[month].[year]")] + + // ToC page + set page( + numbering: "1/1", + fill: none, + margin: (x: 2cm, y: 3cm), + footer: context { + ccicon("cc-by-nc-sa", format: "badge", scale: 2) + h(1fr) + set text(8pt) + counter(page).display("1 / 1", both: true) + }, + ) + + show: codly-init.with() + codly( + languages: codly-languages, + aliases: ("csharp": "cs"), + breakable: false, + display-icon: false, + zebra-fill: gray.lighten(99%), + number-align: right, + fill: gray.lighten(90%), + lang-fill: lang => lang.color.transparentize(80%), + header-cell-args: (align: top + center), + ) + + set par(justify: true, linebreaks: "optimized", leading: 0.85em) + set heading(numbering: "1.1") + + show heading.where(level: 1): heading => [#block[#v(1em) #heading #v(0.8em)]] + show heading.where(level: 2): it => [#v(0.5em) #it #v(0.3em)] + + show figure.caption: set text(size: 8pt) + + set text(lang: language, font: "Arial", size: 11pt, fill: black) + + show raw.where(block: false): it => { + box( + stroke: (paint: gibz-blue, thickness: 0.35pt), + inset: (x: 3pt, y: 2pt), + radius: 2pt, + baseline: 1pt, + text(font: "DejaVu Sans Mono", size: 0.75em, fill: gibz-blue)[#it], + ) + } + + show link: it => [ + #text(fill: gibz-blue, it) + #octique-inline("link-external", color: gibz-blue, width: 0.65em, baseline: 0%) + ] + + outline( + depth: 2, + title: [#pad(top: 10pt, bottom: 20pt, t("table-of-contents", lang: language))], + ) + + // Regular content page + set page( + numbering: "1/1", + fill: none, + margin: (x: 2cm, y: 3cm), + header: context { + [ + #set text(8pt, baseline: 4pt) + #t("module-word", lang: language) #str(moduleNumber): #moduleTitle + #h(1fr) + #hydra(1, skip-starting: false, display: (_, it) => it.body) + #line(length: 100%, stroke: 0.3pt) + ] + }, + footer: context { + line(length: 100%, stroke: 0.3pt) + ccicon("cc-by-nc-sa", format: "badge", scale: 2) + h(1fr) + set text(8pt) + counter(page).display("1 / 1", both: true) + }, + ) + + doc +} diff --git a/packages/preview/gibz-script/0.1.0/src/state.typ b/packages/preview/gibz-script/0.1.0/src/state.typ new file mode 100644 index 0000000000..2d18c3c909 --- /dev/null +++ b/packages/preview/gibz-script/0.1.0/src/state.typ @@ -0,0 +1,2 @@ +// Centralized state for language (shared across components) +#let gibz-lang = state("gibz-lang", "de") diff --git a/packages/preview/gibz-script/0.1.0/template/main.typ b/packages/preview/gibz-script/0.1.0/template/main.typ new file mode 100644 index 0000000000..f0d47410f2 --- /dev/null +++ b/packages/preview/gibz-script/0.1.0/template/main.typ @@ -0,0 +1,12 @@ +#import "@preview/gibz-script:0.1.0": * + +#show: gibz-script.with( + moduleNumber: "000", + moduleTitle: "Modultitel", + documentTitle: "Skript", + language: "de", +) + += Intro + +Hello world! diff --git a/packages/preview/gibz-script/0.1.0/thumbnail.png b/packages/preview/gibz-script/0.1.0/thumbnail.png new file mode 100644 index 0000000000..c5b4efc57f Binary files /dev/null and b/packages/preview/gibz-script/0.1.0/thumbnail.png differ diff --git a/packages/preview/gibz-script/0.1.0/typst.toml b/packages/preview/gibz-script/0.1.0/typst.toml new file mode 100644 index 0000000000..11fbd04c3e --- /dev/null +++ b/packages/preview/gibz-script/0.1.0/typst.toml @@ -0,0 +1,15 @@ +[package] +name = "gibz-script" +version = "0.1.0" +entrypoint = "lib.typ" +authors = ["Peter Gisler"] +license = "MIT-0" +description = "Template and components (tasks, hints, supplementary, code box) with DE/EN i18n and a clean flat API to be used at GIBZ for creating teaching materials." +repository = "https://gitlab.com/GIBZ/public/typst-template" +keywords = ["GIBZ", "education", "teaching", "template", "course"] +categories = ["layout", "components", "report"] + +[template] +path = "template" +entrypoint = "main.typ" +thumbnail = "thumbnail.png"