zhammer / scribly

write stories together
https://scribly.ink
MIT License
2 stars 0 forks source link

script mode #24

Open zhammer opened 4 years ago

zhammer commented 4 years ago

i want to be able to enable script mode when writing, which converts my textarea into a simple, user-friendly writer with screenplay formatting.

simple ui/ux like:

ideal requirements:

impl:

zhammer commented 4 years ago

https://developer.mozilla.org/en-US/docs/Web/API/document/execCommand#Commands

zhammer commented 4 years ago

formatBlock Adds an HTML block-level element around the line containing the current selection, replacing the block element containing the line if one exists (in Firefox,

is the exception — it will wrap any containing block element). Requires a tag-name string as a value argument. Virtually all block-level elements can be used. (Internet Explorer and Edge support only heading tags H1–H6, ADDRESS, and PRE, which must be wrapped in angle brackets, such as "

".)

zhammer commented 4 years ago

https://glitch.com/~adaptable-linseed

// global createMachine

let clicks = 0;
const script = document.getElementById("script");
script.onkeydown = event => {
  if (event.key !== "Tab") {
    return;
  }
  event.preventDefault();
  document.execCommand("formatBlock", false, "div");
  let myClass;
  if (clicks % 2 === 0) {
    myClass = "character";
  }
  else {
    myClass = "title";
  }
  const quote = window.getSelection().focusNode.parentNode;
  quote.classList = myClass;

  clicks += 1;
};
zhammer commented 4 years ago

this seems to cover basic shuffling of components:

// from: http://www.writingroom.com/viewwriting/wr_how_to/How-To-Format-A-Screenplay
const screenplayComponents = [
  "heading",
  "action",
  "character",
  "dialogue",
  "parenthetical",
  "transition"
];

// get the screenplay component after the current
// next("action") -> "character"
function next(screenplayComponent) {
  const index = screenplayComponents.indexOf(screenplayComponent);
  if (index === -1) {
    return screenplayComponents[0];
  }
  return screenplayComponents[(index + 1) % screenplayComponents.length];
}

// get the screenplay component before the current
// previous("action") -> "header"
function previous(screenplayComponent) {
  const index = screenplayComponents.indexOf(screenplayComponent);
  if (index === -1) {
    return screenplayComponents[0];
  }
  return screenplayComponents[(index + screenplayComponents.length - 1) % screenplayComponents.length];
}

function getActiveElement() {
  let focused = window.getSelection().focusNode;
  return focused.nodeName === "#text" ? focused.parentNode : focused;
}

const script = document.getElementById("script");
script.onkeydown = event => {
  if (!event.shiftKey && event.key === "Tab") {
    event.preventDefault();
    nextStyle();
  }
  if (event.shiftKey && event.key == "Tab") {
    event.preventDefault();
    prevStyle();
  }
};

function nextStyle() {
  document.execCommand("formatBlock", false, "p");
  const activeElement = getActiveElement();
  const nextScreenplayComponent = next(activeElement.className); 
  activeElement.classList = nextScreenplayComponent;
}

function prevStyle() {
  document.execCommand("formatBlock", false, "p");
  const activeElement = getActiveElement();
  const nextScreenplayComponent = previous(activeElement.className); 
  activeElement.classList = nextScreenplayComponent;
}

ideally server would deserialize from submit, like:

[
  Line("i am writing a normal text here"),
  Line(""),
  Line("i am about to start my script"),
  Heading("ext. daytime. outside."),
  Action("A person walks down the road. A gust of wind blows by."),
  Character("Narrator"),
  Parenthetical("(dramatically)"),
  Dialogue("Nothing is as it seems...")
]

but would probably? still store in the db as a text blob, maybe adding back some type of

i am writing normal text here

i am about to start my script
<SCRIPT.heading>ext. daytime. outside.</SCRIPT.heading>
<SCRIPT.action>A person walks down the road. A gust of wind blows by.</SCRIPT.action>
...

then when rendering out to html, the array of line/heading/etc would be render to html p tags with classes

zhammer commented 4 years ago

the main thing for me is: most of the time we want a turn in the db to just look like:

it was early in the morning.
someone was calling jane.
but she couldn't answer the phone.

and ideally that'd be represented simply like

turn.text_written = "it was early in the morning.\nsomeone was calling jane.\nbut she couldn't answer the phone."

but i want some optional markup to be in there, at first for script writing, in a way that's not a pain in the ass

zhammer commented 4 years ago

also, obviously i want to make sure it's not a problem if someone writes in their turn, in plaintext:

i'm trying to inject some scribly markup
zhammer commented 4 years ago

v1

zhammer commented 4 years ago

need to fix:

zhammer commented 4 years ago

s/Line/Text

zhammer commented 4 years ago

i'm leaning more towards having each part in the db.

turns
-----
| id  | type       | text                 |
| 1   | text       | this is my turn text |
| 2   | components |                      | # <-- text only exists for plain text turns

turn-components
---------------
| id | turn_id | type   | value                                |
|  1 | 2       | action | jime reaches his hand into the grass |