gnolang / gno

Gno: An interpreted, stack-based Go virtual machine to build succinct and composable apps + Gno.land: a blockchain for timeless code and fair open-source
https://gno.land/
Other
842 stars 343 forks source link

Proposal: extend markdown rendering #439

Open moul opened 1 year ago

moul commented 1 year ago

Context

Currently, we embrace markdown, which is the default way to consume a smart contract using the Render function and the official website.

I propose extending markdown, so we preserve its main advantage -> being human-readable. But making it easier to programmatically interact with the contracts in richer ways.


Current state

Right now we can have rich rendering for humans with standard markdown.

# Title

Some content.

```json
{
  "lorem": "ipsum"
}

Dolor sit amet.


--- 

# New feature examples

## Using frontmatter

Not a big fan of this approach, but it's interesting to show how other systems extended markdown.

On most markdown-driven static website generators, the engines are using frontmatter to allow writing context as yaml.

````markdown
---
title: Lorem
words: 42
keywords: [a, b, c]
---

Hello [world](link).

Adding metadata to JSON

We can add additional parameters to the language selector after the triple-backticks. We could use this to help machines understand the goal of each JSON entries.

Blah blah.

```json,form
{
  "input": [
    {"title": "firstname", "name": "fn", "default": "Satoshi"},
    {"title": "lastname", "name": "ln", "default": "Nakamoto"},
  ]
}

Which would give valid markdown on a standard editor, but would allow the default gnoland website to generate a dynamic HTML form, and would allow a wallet to make dynamic sections too.

We could imagine things like:

````markdown
`warning`: to have visual indicators on the main website, and maybe confirmation boxes on wallets.
```text,warning
Please, make sure to have enough tokens before calling.

API: to indicate which part of the markdown is the JSON API. Then we could add optional parameters like ?format=JSON to just extract this part.

{...}

collapsed: as a way to indicate that the content is not important by default, but people can choose to expand it.

{...}

error: like HTTP errors, allow to give more details about the error, and suggest people to redirect, retry, etc.

{
  "text": "the content moved.",
  "redirect": "/new/location",


---

# Conclusion

My proposal is to extend markdown in a way that stays compatible with standard markdown renderer, and remains human-readable-first, but provides more context to machines and systems.

If we decide a system like this, then we could:
* write helper libraries for writing contracts that return "typed errors" or important helpers like the current `p/demo/dom`.
* update the gnoland website to support them out of the box.
* write guidelines to continue extending it when it makes sense, but also try to keep this minimalist, so people won't have to learn something new.

Related with #903 
tbruyelle commented 1 year ago

Some interpreters cannot read the lang properly if it's not immediately followed by a space or a new line, so eventually I would suggest to use attributes, which is more widely accepted and in addition gives more flexibility. For instance :

```json type="form"

About warnings and errors, the triple colons is also something widely used to create colored containers/boxes around labels. For instance :

:::warning
This is a warning
:::
moul commented 1 year ago

About your comment on the other PR: https://github.com/gnolang/gno/pull/463#issuecomment-1397124866

We'll do some server-side parsing, but no HTML server-side rendering.

The goal is to make something that:

actuallymentor commented 1 year ago

What is the context of applying markdown? I'm trying to understand what Render function you are referring to, or how markdown relates to smart contracts and gnolang.

May I recommend taking a more async approach in issues that are (excessively) expressive in their context so that outside collaborators can be more helpful?

moul commented 1 year ago

I'm trying to understand what Render function you are referring to, or how markdown relates to smart contracts and gnolang.

Looks like a good fit for #408 :)

moul commented 1 year ago

Can be useful for testing: https://staging.gno.land/r/demo/markdown_test

alexiscolin commented 1 year ago

Considering the fact the rendering is done by the client, I would opt for a javascript approach. So, it would be possible to generate the UI without need of any backend templating system. The ideal solution should also let the content to be readable when the javascript engine is disabled in the browser.

Two kind of (related) approches come in my mind to enhance the markdown:

1. Markdown shortcodes

Shortcodes could be used like in SSG solutions (let's say Hugo) in order to add more complexes UIs to the classic markdown. It’s also a good solution to avoid getting too much HTML/CSS in Markdown files and improve the DRY principles by reusing piece of code. Here is an example from an old project in Hugo:

<!-- SHORTCODE - BUTTON -->

{{ if .IsNamedParams }}

  <!-- Get button type -->
  <!-- DDL type -->
  {{ with (eq (.Get "type") "ddl") }}
    {{ $.Scratch.Add "icoClass" "button__ico--ddl"}}
    {{ $.Scratch.Add "typeLabel" "Download"}}
  {{end }}

   [...]

  <!-- Get icon -->
  {{ $icon := .Get "type"}}
  {{ $icon_template := printf "icons/ico-%s.html" $icon }}

  <!-- rendu html -->
    <a class="button-action button-action--aside" href="{{ with .Get "url"}}{{.}}{{ end }}"><span class=" button__ico">{{ partial $icon_template . }}</span>
      <div class="button__text">
        <span class="button__text__label">{{ with .Get "title"}}{{.}}{{ end }}</span>
        <span class="button__text__action">{{ $.Scratch.Get "typeLabel" }}{{ if (eq (.Get "type") "target")}}{{ .Get "site" }}{{end}}</span>
      </div>
    </a>

{{ end }}
<!-- MD FILE -->
## So What Should I Do About Cookies Right Now?

I gave you an update on everything that's happened with GDPR since 2018. (TL;DR: A lot has changed.) In this article, we'll look at cookie consent: specifically, the paradox where marketers are heavily reliant on Google Analytics cookie data ...

>  In this article, we'll look at cookie consent: specifically, the paradox where marketers are heavily reliant on Google Analytics ...

[...]

{{< button title="Get the data" type="ddl" site="Github" url="https://github.com/alexiscolin" >}}

The last part {{< button title="Get the data" type="ddl" site="Github" url="https://github.com/alexiscolin" >}} is a shortcode used to render a button in the md file from various variables (title, type, site (for the icon), url). It’s rendered from a Hugo templating system in this example but could be rendered from a JS templating system if using Hugo’s one (or other Go templating system) is impossible. In this case, we could imagine use/code a JS rendering lib to render predefined shortcodes or allow users to create their own shortcodes trough a dedicated syntax.

2. MDX rendering

Very similar to shortcodes, Mdx is an extended md syntax that improves the markdown creation by adding JSX components and variables to it. For example, adding components, such as interactive charts or alerts, and embed them within the content. It become possible to add JS expressions and render the content trough any JSX runtime (vue, react, emotion, custom...). Here is an mdx example from mdx doc website:

import {Chart} from './snowfall.js'
export const year = 2018

# Last year's snowfall

In {year}, the snowfall was above average.
It was followed by a warm spring which caused
flood conditions in many of the nearby rivers.

<Chart year={year} color="#fcb32c" />

Update

@tbruyelle reminds me that marked.js, the library we use may offer many ways to create or develop these kinds of system above (by using or writing extensions). Using what we currently have is a good choice for a minimalist approach and we should avoid to reinvent the wheel when it's possible.

moul commented 1 year ago

Finding the right balance between readability and staying standard can be a challenge. To ensure the best user experience, it is important to explore alternative ways to manage HTML forms that are both easy to read and adhere to standards. For instance, a multi-column support could be implemented using <div class=""> elements, which are small enough to not cause any issues.

To further improve readability, I suggest focusing on defining the minimal CSS necessary to create the most essential new widgets. Additionally, I propose continuing my initiative of creating a p/demo/ui (#527) library to make the .gno part more idiomatic.

Once we have made the initial modifications, we can move on to more advanced alterations to finish the task.

alexiscolin commented 1 year ago

With the goal of creating a small library of pure CSS components. Here is a list of basic components we should incorporate. They are highly inspired by Notion components (meaning we they are simple inline block components), from Bootstrap classes and from the Gno.land 2.0 website Figma file (clean design). Of course we will be able to more later.

In addition to these components, others style config could be added such as: breakpoints (media queries) or reboot css (reset style). Other design systems such as composition and utility style can be achieved by using CubeCSS Principles : Simplicity, Progressive Enhancement, Agnostic patterns.

moul commented 1 year ago

Great, I suggest providing a quickstart in your preferred format so that we can review and discuss the approach. Please let me know if you require assistance setting up your environment, and feel free to reach out for early review. My goal is to facilitate your work so that we can move in the right direction with ease.

Edit: In addition to widgets, some common rules may also be necessary, such as:

tbruyelle commented 1 year ago

@moul We had a discussion with @alexiscolin and we decided to work on this next week. Thanks for your the additional rules, I think they are important ones and they shouldn't be hard to implement, so it's probably a good start.