webwriter-app / webwriter Goto Github PK
View Code? Open in Web Editor NEWAuthoring tool for interactive content
Home Page: https://webwriter.app
License: Other
Authoring tool for interactive content
Home Page: https://webwriter.app
License: Other
More complex widgets may need to contain children.
There are multiple use cases:
Specification by developers
Slots are specified with the <slot>
element. A name
attribute may be provided which will also be used as an input hint by the editor. Additionally, a data-type
attribute may be provided. If it is provided, the editor narrows the possible slot children to this value. Possible values are: "inline"
for inline widgets, "block"
for block widgets, any valid element name (e.g. "other-widget"
), or a space-separated list of those previous three (e. g. "other-widget another-widget"
or "inline other-widget"
). The <slot>
element should be filled with a fallback input so the widget remains fully usable outside of a WebWriter context. The fallback input should store its state as widget attributes.
my-widget/index.ts
// Imports omitted
@customElement("my-widget")
class MyWidget extends LitElementWw {
render() {
return html`
<img src="..." />
<slot name="caption" data-type="inline">
<input />
</slot>
`
}
}
Editing by teachers
When the widget is rendered by the editor, each slot is first filled with a wrapper element. When the wrapper element is selected, it becomes the actively edited slot. Any editor input is now placed into the wrapper element. Optionally, the possible input is limited by the slot's data-type
attribute. Furthermore, the name
attribute is used as an input hint.
Usage by students
How the element is updated and saved depends on whether there is a slot child present. If there is not slot content, the state is updated and saved as attributes. If there is slot content, the children are updated and saved.
A) Widget without slot content
<my-widget caption="great image"></my-widget>
B) Widget with slot content
<my-widget>
<span slot="caption"><b>great</b> image</span>
</my-widget>
Describe the bug
Changing attributes while using the widget results in a reload of the widget and it gets deleted.
Only happens in the editor, not in the exported file.
To Reproduce
Add a simple Attribute e.g.
@property({ type: String, attribute: true, reflect: true }) public testString: string = '';
Change the Attribute e.g. with a button
<button @click=${() => this.testString = 'test'}>Test</button>
Desktop (please complete the following information):
Additional context
Tested with a Local Widget
The editor should be available in German.
WebWriter opens all explorables in iframes, which provides some isolation between the explorable document and the editor. While the default iframe configuration is enough to avoid unintentional leakage of styles and scripts into the editor, intentional attacks using scripts remain a potential issue.
Scenarios:
Mitigations:
csp
property (https://developer.mozilla.org/en-US/docs/Web/API/HTMLIFrameElement/csp)Some nodes in the model are containers, for example the built-in lists and headings. Furthermore, widgets can also work as containers with slots.
Authors should be able to switch between (compatible) types of containers.
How can this be implemented?
Fundamentally, there is a target node and zero or more original nodes which need to be fit inside the target node.
While @webwriter/core/view
is difficult to test automatically (many platforms), other parts of @webwriter/core
could be tested automatically:
state
: Provide an example state for the store and dispatch actions on itmarshal
: Serialize/unserialize example documentsutility
: Straightforward testing for expected outputsOn print, the editor UI should be fully hidden and only the document itself should be printed.
Add automatic tests for @webwriter/core/connect
.
Some widgets could provide advanced editing capabilities, such as a video widget allowing video editing or a H5P widget that can install H5P content types locally.
Specification by developers
The mechanism to specify advanced editing capabilities is to create a second entry point file edit.ts
, additional to the standard entry point index.ts
. Using the "exports"
field, the developer can map the edit.ts
file to my-widget/edit
such that it can be imported with import MyWidget from "my-widget/edit"
.
my-widget/package.json
{
"main": "index.ts",
"exports": {
".": "index.ts",
"./edit": "edit.ts"
}
}
The main entrypoint should contain the normal widget with no dependency on the editing entry point.
my-widget/index.ts
// Imports omitted
@customElement("my-widget")
export class MyWidget extends LitElementWw {
...
}
The "edit" entrypoint can depend on the main entrypoint.
my-widget/edit.ts
import MyWidget from "."
...
Editing by teachers
The editor automatically imports the editing widget from the edit entrypoint.
Usage by students
None
Widgets are distributed as NPM packages, and WebWriter installs these packages on the user's machine using a package manager. As such, all scenarios and mitigations of NPM packages in general apply. https://cheatsheetseries.owasp.org/cheatsheets/NPM_Security_Cheat_Sheet.html
Scenarios:
webwriter-widget
keyword is used.Mitigations:
--ignore-scripts
) -> This would allow malicious package to execute arbitrary code. (mitigates 1, 2)--frozen-lockfile
) -> This makes builds deterministic and avoids security issues introduced by updates anywhere in the dependency tree (mitigates 2) BUT introduces the burden of consistent lockfiles on developersAdd automatic tests for @webwriter/core/view
Text
Block
Allowing widgets to be addressed with an ID would enable some useful features:
URL-based approach
example.com/myexplorable.html
. For HTTP/S, this can be expected to be unique. For local files, the username needs to be inserted into the file
URL.Editing by teachers
The editor automatically assigns IDs when creating widgets. The ID is a Base 36 ASCII string encoded number, preceded by ww_
. For example, the first widget ID would be ww_0
, while "last" widget ID would be ww_2gosa7pa2gv
, based on Number.MAX_SAFE_INTEGER.toString(36)
. On creation, possible widget IDs are checked one by one, until an unassigned one is found. This theoretically allows for a stable set of Number.MAX_SAFE_INTEGER
widgets in the document, while checking for available IDs is performant.
Describe the bug
When adding a widget the default values of attributes are not set.
To Reproduce
Add Property:
@property({ type: String, attribute: true, reflect: true }) public testString: string = 'Test';
Try to render Property:
render(){
return html`${this.testString}`;
}
Screenshots
Desktop (please complete the following information):
Additional context
Only happens in Editor not in exported File.
Attributes are correct in the firstUpdated
but are lost in the render
Function
The editor should be able to support different languages.
While WebWriter can function mostly decentralized, some features need a central instance:
Storage of analytics and explorables could be combined into one LRS using the xAPI State API, but dependencies should be stored separately as an optimization.
Describe the bug
Widget code is not bundled on saving.
To Reproduce
Steps to reproduce the behavior:
Screenshots
If applicable, add screenshots to help explain your problem.
Desktop (please complete the following information):
Code Signing on Windows has no effect, SmartScreen still blocks the installer.
External data (document formats, packages, etc.) should be parsed so data in the system is uniform.
"main": "theme.css"
font
: L1 Pickerfont-synthesis
: L2 Formfont-variant
: L2 Formfont-kerning
: L2 Formfont-optical-sizing
: L2 Formfont-stretch
: L1 Pickercolor
: L1 Pickertext-decoration
: L1 Pickertext-emphasis
: L1 Pickertext-transform
: L1 Pickertext-shadow
: L1 PickerBox Model, Backgrounds and Borders
display
: L1 Pickerbackground
: L1 Pickermin-height
: L1 Pickerheight
: L1 Pickermax-height
: L1 Pickermin-width
: L1 Pickerwidth
: L1 Pickermax-width
: L1 Pickerpadding
: L1 Pickerborder
: L1 Pickerborder-left
: L1 Pickerborder-right
: L1 Pickerborder-top
: L1 Pickerborder-bottom
: L1 Pickermargin
: L1 Pickerbox-shadow
: L1 Pickeroverflow
: L2 Formoverflow-clip-margin
: L2 Formoverscroll-behavior
: L2 Formscroll-behavior
: L2 Formscrollbar-gutter
: L2 Formtext-overflow
: L2 Formletter-spacing
: L2 Formtext-align
: L1 Pickertext-align-last
: L2 Formtext-indent
: L2 Formtext-justify
: L2 Formtext-size-adjust
: L2 Formvertical-align
: L2 Formwhite-space
: L2 Formword-spacing
: L2 Formword-break
: L2 Formline-break
: L2 Formoverflow-wrap
: L2 Formtab-size
: L2 Formquotes
: L2 Formfloat
: L2 Formclip
: L2 Formposition
: L2 Forminset
: L2 Formz-index
: L2 Formcolumns
: L1 Pickercolumn-span
: L2 Formcolumn-gap
: L2 Formcolumn-fill
: L2 Formcolumn-rule
: L2 Formbreak-after
: L2 Formbreak-before
: L2 Formbreak-inside
: L2 Formcursor
: L2 Formuser-select
: L2 Formaccent-color
: L2 Formappearance
: L2 Formcaret-color
: L2 Formoutline
: L2 Formoutline-offset
: L2 Formpointer-events
: L2 Formresize
: L2 Formanimation
: L3 Literaloffset
: L3 Literalbackground-blend-mode
: L2 Formisolation
: L2 Formmix-blend-mode
: L2 Formbackdrop-filter
: L2 Formfilter
: L2 Formtransform
: L3 Literaltransform-box
: L3 Literaltransform-origin
: L3 Literaltransform-style
: L3 Literaltranslate
: L3 Literalrotate
: L3 Literalscale
: L3 Literalperspective
: L3 Literalperspective-origin
: L3 Literalbackface-visibility
: L3 Literalbox-decoration-break
: L2 Formbreak-before
, break-after
, break-inside
: L2 Formorphans
, widows
: L2 Formclip-path
: L2 Formmask
: L2 Formmask-border
: L2 Formmask-type
: L2 FormRuby, [Writing modes]
ruby-align
: L3 Literalruby-position
: L3 Literaldirection
: L2 Formtext-orientation
: L2 Formunicode-bidi
: L2 Formwriting-mode
: L2 Formshape-image-threshold
: L3 Literalshape-margin
: L3 Literalshape-outside
: L3 Literaltransition
: L3 Literaldisplay: table
Table (Node attribute <table>
)
border-collapse
: L1 Pickerborder-spacing
: L1 Pickercaption-side
: L1 Pickerempty-cells
: L1 Pickertable-layout
: L1 Pickervertical-align
: L1 Pickerdisplay: flex
Flexible Box Layout, Grid layout
flex-flow
: L1 Pickergrid-template
: L1 Pickergrid-auto-flow
: L1 Pickergap
: L1 Pickerplace-items
: L1 Pickerplace-content
: L1 Pickerflex
: L1 Pickergrid-area
: L1 Pickerplace-self
: L1 Pickerorder
: L1 Pickerdisplay: list-item
Lists (Node attribute <ul>|<ol>
)
list-style
: L1 Pickerimage-orientation
: L3 Literalimage-rendering
: L3 Literalimage-resolution
: L3 Literalobject-fit
: L3 Literalobject-position
: L3 LiteralWidgets should be able to specify that they can handle certain media types. This allows advanced drag n' drop handling in the editor, such as automatically creating a ww-figure
widget out of an image dragged into the editor.
Specification by developers
A static property mediaTypes
on the widget's constructor is used to specify which media types the widget can handle. This is an array of valid MIME type strings.
// Imports are omitted
@customElement("my-widget")
class MyWidget extends LitElementWw {
static mediaTypes = ["image/png", "image/svg"]
}
Editing by teachers
When something is dropped or pasted into the editor, the media type should be checked. If the media type has no registered handler, a warning should be shown. If the media type has exactly one registered handler, a new handler element is created at the drop location and a drop
or paste
event is triggered synthetically on the newly created element. If the media type has two or more registered handlers, a dialog should be shown where the user can pick a handler.
Usage by students
None
A package is a set of elements to be used in WebWriter, implemented by a developer. It contains several parts with a package.json
file serving as the entrypoint. It should follow NPM's conventions to provide metadata for the widget package. Specifically, these rules apply:
name
MUST be present. The name MUST be scoped (in the form @{SCOPE}/{NAME}
).version
MUST be present. It MUST follow the syntax of SemVer and SHOULD follow the conventions laid out with SemVer.exports
MUST be present. It MUST contain a record mapping identifiers of the pattern {TYPE}/{ID}
to relative paths of source files.
{TYPE}
MUST be one of themes
, utilities
, snippets
or widgets
.{ID}
MUST be a sequence of lowercase, URL-safe characters.{TYPE}
is widgets
, {ID}
MUST follow the form {SCOPE}-{ELEMENT}
, where {SCOPE}
is the package scope and {ELEMENT}
is a string such that {SCOPE}-{ELEMENT}
is a valid custom element identifier. The file referenced MUST be a JavaScript or TypeScript file that registers a custom element of the same name {SCOPE}-{ELEMENT}
with window.customElements.define
{TYPE}
is snippets
, {ID}
MAY be a string such that widgets/{ID}
is also defined in exports
.{TYPE}
is themes
, the file referenced MUST be a CSS file.editingConfig
MAY be present. It MUST be an object where each key is also present in exports
, and each value is an object providing editing settings depending on {TYPE}
Widgets are interactive elements visible to both authors and users.
Custom Elements Manifest (CEM) is a way to describe custom elements, adding metadata. For WebWriter, this could serve several functions.The @custom-elements-manifest/analyzer
library is useful for this - During widget development, this can be run when neccessary.
Attributes represent the permanent state of the widget. WebWriter tracks changes in attributes and is able to undo/redo them. For non-permanent state, properties should be used.
Attributes of the widget and events that may be emitted can be annotated for purposes of documentation.
class
- State of the editorww-beforeprint
: Before printing, this class is set, allowing widgets to change their presentation to be print-ready.contenteditable
- Differentiate author and user behaviorThe value contenteditable="true"
is set when editing, allowing widgets to provide affordances to authors change their state.
lang
- Language for localizationWebWriter sets the user's chosen document language on each widget during editing as the lang
attribute. The widget should process the set locale and change its presentation accordingly.
@experience
- Custom events for xAPI statementsWidget may emit custom experience
events that report interactions with the widget.
Slots are elements inside the widget's shadow tree in which content can be placed. This allows for a nesting of elements in WebWriter. Developers can specify the the allowed content of the slot with a content expression. The content expression is added as a custom JSDoc annotation on the component.
Parts and CSS variables enable styling the internals of the widget by WebWriter or developed stylesheets. Developers can annotate CSS variables with a CSS type that enables WebWriter to generate a widget allowing the user to manipulate the CSS variable. For example, the widget may have a --canvas-color
variable that is of the CSS type <color>
, allowing authors to use a color picker to choose a color.
Themes are stylesheets intended to be applied to the whole explorable.
Snippets are arbitrary HTML fragments to be inserted into the explorable.
Add tests for the whole app working together.
Add automatic tests for @webwriter/core/state
.
Add automatic testing for @webwriter/core/state
.
Right now, any document content can affect the whole app. This is both a security and a usability issue: Attackers can gain control over the app and faulty widgets can break the whole app.
Sandboxing on the document level seems to be the best choice. Widgets having access to the whole document can be useful, and the damage attackers can cause is limited to an acceptable level. Performance loss is also kept limited, since each open document has its own iframe, but the user most likely won't have many open documents (<10).
The document would need to exist in an iframe. The editor needs to be modified so all content exists in the iframe. Bundles need to be loaded into the iframe. The editor toolbox with its widget previews needs to be reconsidered, as well.
Undoing/redoing changes is desirable for any stateful widget. It's better for UX to implement this on the editor level instead of the widget level.
To implement this on the widget level, the widget needs to interface with the editor. A simple approach would be running a MutationObserver on every element - When the element's attributes or children change, an event is triggered to change the editor state accordingly. This adds a small performance overhead, but integrates fluently with the existing implementation and doesn't need any changes on the widget level.
Analytics data about user interaction with widgets can be useful both to enable insights into learner behavior for teachers and to generate research data.
Specification by developers
Editing by teachers
Usage by students
Some widget metrics can be recorded automatically and instantly.
Metrics
Usage Testing
Validation
There are some general features which make sense for a large subset of widgets.
Some features should be available on the editing level, some on the usage level, some on both levels.
Use cases
Specification by developers
Editing by teachers
Usage by students
webwriter-slides
webwriter-slides
webwriter-slides
webwriter-slides
webwriter-slides
webwriter-grid
webwriter-grid
webwriter-grid
webwriter-slides
webwriter-flashcards
webwriter-flashcards
webwriter-branching-scenario
webwriter-interactive-video
webwriter-timeline
<iframe>
webwriter-chart
webwriter-image360
webwriter-word-puzzle
webwriter-word-puzzle
Supporting most of the HTML spec to be able to write/read any HTML document
<html>
, <head>
, <body>
(automatically as <html><head></head><body></body></html>
)<link>
<meta>
)<meta>
(Metadata Form)
<title>
(mirror <meta>
)<base>
(literal only)<address>
(Person Container)<article>
(not applicable)<aside>
(Aside Container)<header>
, <footer>
(?)<hgroup>
(automatic with headings if neccessary)<main>
(literal only)<nav>
(literal only)<section>
(automatic with headings by default?)<search>
(literal only)<blockquote>
(Blockquote Container)<dl>
, <dd>
, <dt>
(Glossary Container)<figure>
, <figcaption>
(Figure Container)<hr>
(Separator Leaf)<ol>
, <ul>
, <li>
(List Container)<menu>
(literal only)<p>
(Paragraph Container)<pre>
(literal only, fulfilled by visible <script>)<a>
(Link Mark)<abbr>
(Acronym Mark)<b>
, <strong>
(Bold Mark)<i>
, <em>
(Italic Mark)<u>
(Underline Mark)<s>
(Strikethrough Mark)<cite>
(Reference Mark)<code>
(Code Mark)<data>
, <time>
(Data Mark)<dfn>
(literal only)<q>
(Quote Mark)<samp>
(Output Mark)<small>
(literal only)<var>
(Inline Math Mark)<br>
, <wbr>
(automatic)<bdi>
(BDI Mark)<bdo>
(BDO Mark)<ruby>
(Ruby Mark)<span>
(Span Mark)<rp>
, <rt>
(literal only)<area>
, <map>
(Widget-supported editor on <img>
, <picture>
, <audio>
, <video>
, <track>
(Media Element)<embed>
, <object>
(Media Element - PDF)<iframe>
, <portal>
, <fencedframe>
(Media Element - Website)<source>
(automatic)<svg>
(Vector Drawing Container)<math>
(Formula Container)<script>
(Media Element - Textual and other types)<noscript>
(automatic)<canvas>
(literal only)<del>
, <ins>
(literal only)<caption>
, <col>
, <colgroup>
, <table>
, <tbody>
, <td>
, <tfoot>
, <th>
, <thead>
, <tr>
(Table Container)<button>
, <input>
, <select>
, <meter>
, <datalist>
, <fieldset>
, <form>
, <label>
, <legend>
, <optgroup>
, <option>
(Quiz Container Elements)<output>
, <progress>
(literal only)<details>
, <summary>
(Drawer Container)<dialog>
(Dialog Container) <my-element>
, <slot>
, <template>
(Widget System)
Fallback: HTML literal
It would be nice to allow developers to use WebWriter directly as the development preview. This would also remove the need to set up a development environment for widget developers, so only WebWriter, npm and a text editor are needed to develop widgets.
Basic
Advanced
Problems:
Add a feature widget that can serialize and save the parent tree.
Add automatic tests for @webwriter/core/utility
.
Tokenize typed text live with nlp.js, then
use nspell and dictionaries to generate corrections. Interface with the editor as a NodeView, where each token is a span that can be styled.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.