Marcel
    • Create new note
    • Create a note from template
      • Sharing URL Link copied
      • /edit
      • View mode
        • Edit mode
        • View mode
        • Book mode
        • Slide mode
        Edit mode View mode Book mode Slide mode
      • Customize slides
      • Note Permission
      • Read
        • Only me
        • Signed-in users
        • Everyone
        Only me Signed-in users Everyone
      • Write
        • Only me
        • Signed-in users
        • Everyone
        Only me Signed-in users Everyone
      • Engagement control Commenting, Suggest edit, Emoji Reply
    • Invite by email
      Invitee

      This note has no invitees

    • Publish Note

      Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

      Your note will be visible on your profile and discoverable by anyone.
      Your note is now live.
      This note is visible on your profile and discoverable online.
      Everyone on the web can find and read all notes of this public team.
      See published notes
      Unpublish note
      Please check the box to agree to the Community Guidelines.
      View profile
    • Commenting
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
      • Everyone
    • Suggest edit
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
    • Emoji Reply
    • Enable
    • Versions and GitHub Sync
    • Note settings
    • Note Insights New
    • Engagement control
    • Make a copy
    • Transfer ownership
    • Delete this note
    • Save as template
    • Insert from template
    • Import from
      • Dropbox
      • Google Drive
      • Gist
      • Clipboard
    • Export to
      • Dropbox
      • Google Drive
      • Gist
    • Download
      • Markdown
      • HTML
      • Raw HTML
Menu Note settings Note Insights Versions and GitHub Sync Sharing URL Create Help
Create Create new note Create a note from template
Menu
Options
Engagement control Make a copy Transfer ownership Delete this note
Import from
Dropbox Google Drive Gist Clipboard
Export to
Dropbox Google Drive Gist
Download
Markdown HTML Raw HTML
Back
Sharing URL Link copied
/edit
View mode
  • Edit mode
  • View mode
  • Book mode
  • Slide mode
Edit mode View mode Book mode Slide mode
Customize slides
Note Permission
Read
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
Write
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
Engagement control Commenting, Suggest edit, Emoji Reply
  • Invite by email
    Invitee

    This note has no invitees

  • Publish Note

    Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

    Your note will be visible on your profile and discoverable by anyone.
    Your note is now live.
    This note is visible on your profile and discoverable online.
    Everyone on the web can find and read all notes of this public team.
    See published notes
    Unpublish note
    Please check the box to agree to the Community Guidelines.
    View profile
    Engagement control
    Commenting
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    • Everyone
    Suggest edit
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    Emoji Reply
    Enable
    Import from Dropbox Google Drive Gist Clipboard
       Owned this note    Owned this note      
    Published Linked with GitHub
    • Any changes
      Be notified of any changes
    • Mention me
      Be notified of mention me
    • Unsubscribe
    --- title: Lokalisiert - Dokumentation author: Carina Strehler, Dennis Lex, Marcel Arndt tags: Lokalisiert, Essen, Teilen, Restaurant --- ## Templates * https://arc42.org/overview/ * https://www.dokchess.de # Lokalisiert - Dokumentation ## Inhalt 1. Einführung und Ziele (Marcel) 2. Randbedingungen (Marcel) 3. Kontextabgrenzung (Marcel) 4. Lösungsstrategie (Marcel) 5. Bausteinsicht (Dennis) 6. Laufzeitsicht (Dennis) 7. Verteilungssicht (Dennis) 8. Konzepte (Carina) 9. Entscheidungen (Carina) 10. Qualitätsszenarien (Carina) 11. Risiken (Carina) ## Einführung und Ziele ### Aufgabenstellung #### Was ist *Lokalisiert*? * *Lokalisiert* ist eine Website zur Suche von Restaurants * Sie soll beim Bestellvorgang unterstützen, aber dennoch die Möglichkeit lassen die Lieferung selbst abzuwickeln und somit Abgaben an die Plattform zu vermeiden. * Ein brutalistisches Design soll modern wirken und den Nutzer zum Entdecken der Anwendung motivieren. #### Kernfunktionalität * Suche der Lokale nach Adresse * Umwandeln der Adresse in Koordinaten * Anzeigen des Lokals auf einer interaktiven Karte * Anzeigen der Suchergebnisse, sortiert nach Entfernung * Registrierung der Lokale \* * Editieren der Lokalinformationen \* \* nur als eingeloggter User möglich #### Features * Registrierung eines Users * Suchen der Lokale, sortiert nach vordefinierten Tags * Anzeigen der Speisekarte * Editieren der Speisekarte durch Ersteller \* * Erstellen von geteilten Speisekarten für Gruppenbestellungen \* * Ansehen des Ergebnisses der geteilten Speicherkarte über eine Benutzer-View \* * Hosting der Website über Firebase \* nur als eingeloggter User möglich ### Stakeholder * *Entwickler* wollen möglichst einfach strukturierten und lesbaren Quellcode. * *Phillip Heidegger* möchte, dass möglichst alle im Meeting besprochenen Punkte umgesetzt wurden und funktional sind. * *Restaurants* möchten eine einfach zu bedienende Plattform, auf welcher sie eine Internetpräsenz erstellen können und neue Kunden für Bestellungen gewinnen können. * *Kunden* möchten schnell und unkompliziert ihre Bestellung sammeln und aufgeben können. ## Randbedingungen ### Technische Randbedingungen * Die Anwendung soll eine Vielzahl an Browsern unterstützen und auch in neueren Versionen dieser voll funktionsfähig bleiben. * Im Frontend-Bereich soll Angular verwendet werden. Die Version ist frei wählbar. * Im Backend-Bereich soll die von *Google* bereitgestellte Cloud-Plattform *Firebase* verwendet werden. * Alle eingesetzten Frameworks, sowie die genutzten Dienste von Firebase sollen kostenlos nutzbar und im Optimalfall auch frei verfügbar sein. ### Organisatorische Randbedingungen * Team * Carina Strehler * Dennis Lex * Marcel Arndt * Mit der Entwicklung wurde im Mai 2021 begonnen, einen ersten lauffähigen Prototyp gab es im Juni 2021, der Release der Version 1.0 ist für den 22. Juli 2021 geplant * Während des Entwicklungsprozesses wurde ein Kanban-Board zur Organisation, gepaart mit Weekly-Meetings zur Absprache und Koordination, eingesetzt. ## Kontextabgrenzung ### Fachlicher Kontext **Grafik sollte hier sein :P** * Ein eingeloggter Nutzer registriert sich zunächst beim System und hat anschließend die Möglichkeit eine *Sammelbestellung* zu administrieren oder an ihr teilzunehmen, ein *Lokal* zu registrieren, administrieren oder nach einem *Lokal* zu suchen. * Ein nicht eingeloggter Nutzer kann an einer *Sammelbestellung* teilnehmen und nach einem *Lokal* suchen. * Eine *Sammelbestellung* ist ein Modul, welches von Nutzern zum vereinfachten Sammeln der Bestellung bei einem *Lokal* genutzt werden kann. * Ein *Lokal* ist Bestandteil des Systems *Lokalisiert* und speichert Informationen in Bezug auf Öffnungszeiten, Standort, sowie Kontaktdaten. ### Technischer Kontext **Hier auch eine Grafik :grin:** * Das Persistieren und Abrufen der Daten erfolgt über das **Backend-As-A-Service** *Firebase*. Dies ermöglicht *Echtzeitzugriffe* und bietet eine sehr hohe *Ausfallsicherheit*. * Für die Bereitstellung der Kartendaten wird *Nominatim* eingesetzt. Es werden hier Adressdaten dem Dienst zugeschickt, welcher diese in Längen- und Breitengrade umwandelt und zurücksendet. ## Lösungsstrategie ### Qualitätsziele Der Anspruch an *Lokalisiert* bestand darin, durch Angular im Frontend eine einfache *Austauschbarkeit* der Module zu gewährleisten und somit die *Flexibilität* hochzuhalten. Zudem soll die Quellcodebasis möglichst wenig Duplikationen enthalten und dadurch eine hohe *Wiederverwendbarkeit* ermöglichen. Das Design der Applikation soll für den Nutzer sehr ansprechend sein und durch den Einsatz des *brutalistischen* Designansatzes eine hohe *Attraktivität* bieten. Durch das Nutzen des von Google bereitgestellten Cloud-Backends *Firebase* wird eine sehr hohe *Ausfallsicherheit* gewährleistet, außerdem stellt es eine sehr hohe *Effizienz* beim Datenaustausch und bei Zugriffen auf die Datenbank sicher. ### Aufbau *Lokalisiert* ist als komponentenbasierte Singlepage Application durch den Einsatz des Angular Frameworks realisiert. Der Aufbau lässt sich grob in folgende Teile zerlegen: * Eine *Core*-Anwendung, welche grundlegende Komponenten wie *datenschutz*, *faq*, *footer* / *header*, ... enthält. * Einen *Modules*-Bereich, welcher die *Core*-Anwendung durch zusätzliche Features erweitert, aber auch die benötigte Kernfunktionalität bereitstellt. * Einen *Shared*-Bereich, welcher geteilte *Services*, *Guards* und *Interfaces* enthält. ### Anbindung Das Frontend wird durch die `@angular/fire` Bibliothek mit dem *Firebase*-Backend verknüpft. Dadurch kann bereits sehr vereinfacht mit der API von *Firebase* kommuniziert werden und die Daten der Lokale, Nutzer und Speisekarten in der *Firestore Database* persistiert werden. Außerdem wird eine Anbindung an den OpenStreetMap Dienst *Nominatim* implementiert, um eine Adresse in Längen- und Breitengrade umzuwnadeln und somit auf der Karte des Lokals anzuzeigen. ## Bausteinsicht ![Komponentendiagramm - Bausteinsicht](https://i.imgur.com/WUG05wH.png) ## Laufzeitsicht ### Suche nach Unternehmen ![Sequenzdiagramm - Ablauf Suche Unternehmen](https://i.imgur.com/VfGrfOH.png) ### Erstellung eines Benutzers ![Sequenzdiagramm - Ablauf Erstellung Benutzer](https://i.imgur.com/RteqTZ7.png) ## Verteilungssicht ![Komponentendiagramm - Verteilungssicht](https://i.imgur.com/xqaFKKC.png) ## Konzepte ### Lazy-Loading von Modulen Die Webseite *Lokalisiert* stellt sowohl für interessierte Kunden eine Möglichkeit der Information über Lokale in deren Nähe, wie auch Erweiterungen über die Möglichkeit des Erstellens von Speisekarten und die Registrierung / Editierung von Unternehmen dar. Dadurch entstehen einige verschiedene, voneinader unabhängige Bereiche, die nur für einen bestimmten Benutzerbereich erreicht werden müssen. So muss ein nicht-eingeloggter Nutzer nicht auf den Admin-Bereich für Unternehmen zugreifen und die Funktion des Einloggens braucht dieser erst, wenn er sich entscheidet auch einen Account erstellen zu wollen. Single-Page-Webanwendungen haben oft den Nachteil, dass bereits zu Beginn die gesamte Applikation geladen werden muss. Das kann unter Umständen sehr lange dauern. Außerdem müsste jeder Nutzer den Admin-Bereich, Login-Bereich mitladen, obwohl dieser möglicherweise gar nicht benötigt wird. Um dies zu umgehen wurde in *Lokalisiert* das sogenannte *Lazy-Loading von Modulen* verwendet. Das heißt es gibt einen Anwendungskern, der immer geladen wird und die einzelnen Module, die unabhängig voneinander geschrieben sind. Diese sind im Grunde wie eigene Applikationen, die an der App / Root Applikation hängen und erst geladen werden, sobald die zugehörige Route das erste Mal aufgerufen wird. So werde nicht nur die Codebereiche thematisch getrennt, sondern auch die Initial-Ladezeit erheblich gekürzt. Außerdem können spätere Erweiterungen leicht hinzugefügt werden. Im Grunde kann man sagen, dass jedes Modul einem Feature-Bereich entspricht. Für *Lokalisiert* wurden folgende thematisch abgetrennte Feature-Bereiche entwickelt: - auth (Login / Register Bereich) - home (Startseite und Suchleiste für Lokale nach Adresse) - *order (noch nicht verwendet, Feature ist in Planung)* - organization (Übersicht über die Lokale mit Speisekarte) - user (Bereich für eingeloggte Benutzer) Der Zugriff auf die Module erfolgt über Routing. Jedes Modul hat dann wiederum ein eigenes `*module name*-routing.module.ts` File, welches die darunterliegeneden Routen umfasst. Wenn man somit den Login-Bereich im *auth*-Module erreichen möchte benötigt man die Route: `auth/login`. ### Multiple-Router-Outlets Neben dem primären Router-Outlet wird noch ein weiteres Router-Outlet in dieser Applikation verwendent, das den Namen `sidebar` trägt. Router-Outlets dienen als eine Art Platzhalter, um in Single-Page Webanwendungen den jeweils in der URL / Route angegebenen Bereich dynamisch zu laden. Der Vorteil darin ist, dass die URL jederzeit in einen anderen Browser / ein neues Fenster eingegeben werden kann und an dieselbe Seite der Webanwendung zurückführt. Grundsätzlich gibt es bei Angular-Anwendungen in der Regel eine Navigationsleiste, die über den Router-Link zu den jeweiligen Unterseiten führt. In größeren Applikationen ist jedoch oft eine dynamischere Navigation auch durch Unterbereiche notwendig. Um dies flexibel umzusetzen gibt es das Konzept der benamten oder multiplen Router-Outlets. Der Vorteil darin ist der Zugriff über die URL mit einer simplen Erweiterung `(sidebar:user-nav)`, welche die zweite Navigation dynamisch füllt. Anstelle der Navigation im Benutzerbereich `user-nav` kann später beispielsweise eine neue Adminseite an selber Stelle eingefügt werden, ohne sich ein neues Konzept überlegen zu müssen. Außerdem ist beim Reload der Seite direkt der letzte Stand abgebildet, ohne mit Query-Parameter und Selektoren arbeiten zu müssen. Somit wird auch die User-Experience aufrecht erhalten. ## Entscheidungen ### Frontend Die größte Entscheidung, die Verwendung von Angular, wurde bereits durch den Kurs "Single-Page-Webanwendung mit Typescript und Angular" abgenommen. Somit stand Angular als ein auf TypeSript-basiertes Front-End-Webapplikationsframework fest, was von den Entwicklern auch gerne angenommen wurde, da alle zumindest einmal eine kleine Beispielapplikation in Angular geschrieben hatten. Da das Design möglichst ansprechend für den Nutzer sein soll und dieser sich möglichst intuitiv zurechtfinden soll, war es wichtig eine Unterstützung im Design-Aufbau und der Wiederverwendung im Frontend zu bekommen. Dafür wird tailwind.css verwendet, welches ein Utility-Framework ist, das den Entwicklern von Frontend-Webseiten die Umsetzung eines einheitlichen, responsiven Designs erleichtert. Vor allem unter dem Gesichtspunkt, dass die Applikation in Zukunft nicht nur von Desktop-Geräten, sondern auch von mobilen Endgeräten aus lauffähig sein soll, erhält man mit tailwind.css eine Unterstützung in der Erstellung von Benutzeroberflächen. Dies wird gewährleistet, da nur noch selten die Anpassung von css Files notwendig ist, da tailwind.css für alle gängigen css-Befehle ein Kürzel enthält, das lediglich in die Klasse eines html-Tags eingebaut werden muss. ### Backend Zunächst sollte ein eigenes Backend mit NoSQL-Datenbank für die Applikation geschrieben werden. Da zunächst allerdings bis auf die Authentifizierung einzig die Datenhaltung im Backend umgesetzt werden sollte, einigten sich die Entwickler darauf, die verfügbare Zeit vermehrt in die Realisierung des Frontends zu stecken und auf ein Backend-as-a-Service Produkt zurückzugreifen. Die Wahl fiel schnell auf Firebase, welches als eine Entwicklungs-Plattform für die Webanwendung dient und in diesem Fall die Infrastruktur für die NoSQL-Datenbank, die Authentifizierung wie auch das erste Hosting übernimmt. Später kann Firebase um Functions (für z. B. E-Mail-Benachrichtigungen) oder spezielle Datenbankregeln erweitert werden. Auch der Austausch in ein eigens entwickeltes Backend wäre später denkbar, der Austausch würde aufgrund der verwendeten Services relativ problemlos funktionieren. Zunächst war die Überlegung ein nx-Monorepo für die Entwicklung der Anwendung zu verwenden. Monorepositories haben vor allem dann einen entscheidenden Vorteil, wenn sowohl das Backend als auch das Frontend in einer Programmiersprache und einem ähnlichen Framework geschrieben sind. Dieser Vorschlag ist allerdings zum Zeitpunkt der Entscheidung für Firebase verworfen worden, da nur eine Webanwendung basierend auf TypeScript entwickelt wurde und so der Overhead und das Einlernen in nx-Monorepo zu aufwendig gewesen wären. Ein weiterer Vorschlag, der aufgrund der Komplexität und des Overheads bei kleineren, nicht systemkritischen Anwendungen verworfen wurde, war die Verwendung eines ngrx-Stores. Die Verwendung eine Stores und somit eines einheitlichen State-Managements hätte den Vorteil gehabt, dass zu jeder Zeit in der Applikation alle Daten auf dem selben Stand sind und somit die Datenhaltung konsistent wäre. Firebase bietet allerdings durch die spezielle Anbindung an den Firestore (NoSQL-Datenbank) bereits eine API, die den Anschein einer gewissen Konsistenz über Subscriptions bereitstellt. ## Qualitätszenarien Die wichtigsten Qualitätsmerkmale für die Seite *Lokalisiert* sind vor allem die Effizienz und Benutzbarkeit für den Nutzer der Webapplikation. So soll die Attraktiviät hoch sein, wie auch die Antwortzeit möglichst kurz. Zudem sollen natürlich alle notwendigen und abgebildeten Funktionen zur Verfügung stehen und korrekt auszuführen sein. Für die Entwickler steht vor allem die Austauschbarkeit und Wartbarkeit im Vordergrund, da Erweiterungen an der Applikation denkbar sind und somit diese Möglichkeit auch bestehen sollte. Außerdem soll aufgrund der Verwendung von Firebase schnell auf Änderungen hinsichtlich deren API oder Konzepte reagiert werden können, um eine reibungslose Verwendung der Seite für den Nutzer zu gewährleisten. ## Risiken ### Schnelllebigkeit und Kosten von Firebase Aufgrund der Abhängigkeit von einer Backend-as-a-Service Seite wie Firebase steht man natürlich immer vor dem Risiko, dass die API sich ändern könnte oder Konzepte und Bereiche abgelöst oder nicht mehr weiter unterstützt werden. Dieses Risiko muss abgewogen werden gegenüber dem Vorteil, dass eine reine Frontend-Applikation sehr einfach und schnell mit Firebase realisiert werden kann. In der reinen Entwicklungsphase ist es noch nicht problematisch Firebase zu verwenden. Im späteren Verlauf muss allerdings in Betracht gezogen werden, dass bei einer Weiterverwendung von Firebase möglicherweise schnell und flexibel auf Veränderungen der API reagiert werden muss und die Codebereiche neu angepasst werden müssen. Weiterhin ist Firebase nur bis zu einem bestimmten Punkt kostenlos. Ab einer gewissen Auslastung oder Größe der Seite fallen schnell nicht kalkulierbare Kosten an. Auch hier muss man mit diesem Risiko leben, dass die Webseite möglicherweise ab einem bestimmten Punkt nicht mehr rentabel ist. Dies muss bei Veröffentlichung beachtet werden und gegebenenfalls auf die Entwicklung einges eigenen Backends zurückgegriffen werden. ### Verwendung externer APIs Nicht nur für das Ersetzen eines Backends wurde auf eine fremde API zurückgegriffen. Auch die Umwandlung von Adressen zu Koordinaten geschieht über den *Nominatim*-Service von OpenStreetMap. Auch hier können Änderungen im http-Aufruf passieren, auf die geeignet reagiert werden müsste. Zur Risikominimierung wurden hier bereits weitere Möglichekeiten, wie beispielsweise die Verwendung von Google-Maps, statt OSM bzw. Nominatim geprüft. Dies ist aufgrund des Einsatzes von Firebase auch relativ einfach und schnell möglich. ## Stellungnahme zur Arbeitsaufteilung im Projekt *Lokalisiert* Grundsätzlich wurde das Projekt *Lokalisiert* im Rahmen der Veranstaltung Single-Page-Webanwendungen mit TypeScript und Angular in einer Abwandlung von Scrum programmiert. Das heißt es wurden wöchentlich Issues erstellt, die ähnlich zu User-Stories unter der Woche von einem Projektmitglied gepickt und bis zur nächsten Woche bearbeitet werden konnten. In wöchentlichen Meetings (Weeklies) wurde dann über den aktuellen Stand des Projektes gesprochen und bestimmte Meilensteine, also Entwicklungsschritte festgelegt. Gleichzeitig fand während des Projektes ein Treffen in Präsenz stand, in dem über alle Probleme und bestimmte Konzepte oder besondere Programmausschnitte gesprochen wurde. Dadurch lässt sich auch keine direkte Arbeitsaufteilung festlegen. Grundsätzlich können die groben Verantwortungsbereiche wie folgt genannt werden: - Übersicht über die Lokale mit Filterung, Abspeichern und Generieren der Tags, Projektinitialisierung (Aufsetzen des Git-Hub-Repos, der Angularanwendung und von Tailwind), Aufsetzen und Generieren der Dokumentation -> **Marcel Arndt** - Authentifizierung, Registrieren und Bearbeiten der Lokale mit Logo, Projektverantwortung um Abarbeitung der Issues und Erreichen der Meilensteine -> **Dennis Lex** - Benutzerbereich, alles rund um die Speisekarte mit Funktion der Speisekartenteilung, OpenStreetMap-Anbindung und Koordinatenberechnung über Nominatim, Konzept zur Öffnungszeitenspeicherung, Aufsetzen der Core-Anwendung (Ordnerstruktur etc.), Design der Webseite über tailwind.css -> **Carina Strehler** Die Dokumentation wurde wie folgt aufgeteilt: - Einführung und Ziele, Randbedingungen, Kontextabgrenzung, Lösungsstrategie -> **Marcel Arndt** - Bausteinsicht, Laufzeitsicht, Verteilungssicht -> **Dennis Lex** - Konzepte, Entscheidungen, Qualitätsszenarien, Risiken -> **Carina Strehler** Auch hier wurde die Dokumentation zwar geteilt bearbeitet, über die Grundkonzepte wurde im Voraus und auch als Kontrolle im Nachgang ausgiebig diskutiert und die jeweiligen Personen unterstützt. Insgesamt kann man sagen, dass das Projekt *Lokalisiert* in sehr guter Team-Arbeit bearbeitet wurde und jeder Projektteilnehmer je nach Vorwissen in etwa die selbe Bearbeitungszeit investiert hat. Es waren in allen Meetings, die angesetzt waren alle Projektteilnehmer anwesend und auch bei Problemen jeder Art fand auch einmal spontan am Abend ein kurzes Treffen statt, um über Probleme zu sprechen, sich gegenseitig zu helfen oder auch nur Designentscheidungen zu treffen.

    Import from clipboard

    Paste your markdown or webpage here...

    Advanced permission required

    Your current role can only read. Ask the system administrator to acquire write and comment permission.

    This team is disabled

    Sorry, this team is disabled. You can't edit this note.

    This note is locked

    Sorry, only owner can edit this note.

    Reach the limit

    Sorry, you've reached the max length this note can be.
    Please reduce the content or divide it to more notes, thank you!

    Import from Gist

    Import from Snippet

    or

    Export to Snippet

    Are you sure?

    Do you really want to delete this note?
    All users will lose their connection.

    Create a note from template

    Create a note from template

    Oops...
    This template has been removed or transferred.
    Upgrade
    All
    • All
    • Team
    No template.

    Create a template

    Upgrade

    Delete template

    Do you really want to delete this template?
    Turn this template into a regular note and keep its content, versions, and comments.

    This page need refresh

    You have an incompatible client version.
    Refresh to update.
    New version available!
    See releases notes here
    Refresh to enjoy new features.
    Your user state has changed.
    Refresh to load new user state.

    Sign in

    Forgot password

    or

    By clicking below, you agree to our terms of service.

    Sign in via Facebook Sign in via Twitter Sign in via GitHub Sign in via Dropbox Sign in with Wallet
    Wallet ( )
    Connect another wallet

    New to HackMD? Sign up

    Help

    • English
    • 中文
    • Français
    • Deutsch
    • 日本語
    • Español
    • Català
    • Ελληνικά
    • Português
    • italiano
    • Türkçe
    • Русский
    • Nederlands
    • hrvatski jezik
    • język polski
    • Українська
    • हिन्दी
    • svenska
    • Esperanto
    • dansk

    Documents

    Help & Tutorial

    How to use Book mode

    Slide Example

    API Docs

    Edit in VSCode

    Install browser extension

    Contacts

    Feedback

    Discord

    Send us email

    Resources

    Releases

    Pricing

    Blog

    Policy

    Terms

    Privacy

    Cheatsheet

    Syntax Example Reference
    # Header Header 基本排版
    - Unordered List
    • Unordered List
    1. Ordered List
    1. Ordered List
    - [ ] Todo List
    • Todo List
    > Blockquote
    Blockquote
    **Bold font** Bold font
    *Italics font* Italics font
    ~~Strikethrough~~ Strikethrough
    19^th^ 19th
    H~2~O H2O
    ++Inserted text++ Inserted text
    ==Marked text== Marked text
    [link text](https:// "title") Link
    ![image alt](https:// "title") Image
    `Code` Code 在筆記中貼入程式碼
    ```javascript
    var i = 0;
    ```
    var i = 0;
    :smile: :smile: Emoji list
    {%youtube youtube_id %} Externals
    $L^aT_eX$ LaTeX
    :::info
    This is a alert area.
    :::

    This is a alert area.

    Versions and GitHub Sync
    Get Full History Access

    • Edit version name
    • Delete

    revision author avatar     named on  

    More Less

    Note content is identical to the latest version.
    Compare
      Choose a version
      No search result
      Version not found
    Sign in to link this note to GitHub
    Learn more
    This note is not linked with GitHub
     

    Feedback

    Submission failed, please try again

    Thanks for your support.

    On a scale of 0-10, how likely is it that you would recommend HackMD to your friends, family or business associates?

    Please give us some advice and help us improve HackMD.

     

    Thanks for your feedback

    Remove version name

    Do you want to remove this version name and description?

    Transfer ownership

    Transfer to
      Warning: is a public team. If you transfer note to this team, everyone on the web can find and read this note.

        Link with GitHub

        Please authorize HackMD on GitHub
        • Please sign in to GitHub and install the HackMD app on your GitHub repo.
        • HackMD links with GitHub through a GitHub App. You can choose which repo to install our App.
        Learn more  Sign in to GitHub

        Push the note to GitHub Push to GitHub Pull a file from GitHub

          Authorize again
         

        Choose which file to push to

        Select repo
        Refresh Authorize more repos
        Select branch
        Select file
        Select branch
        Choose version(s) to push
        • Save a new version and push
        • Choose from existing versions
        Include title and tags
        Available push count

        Pull from GitHub

         
        File from GitHub
        File from HackMD

        GitHub Link Settings

        File linked

        Linked by
        File path
        Last synced branch
        Available push count

        Danger Zone

        Unlink
        You will no longer receive notification when GitHub file changes after unlink.

        Syncing

        Push failed

        Push successfully