mirror of
https://github.com/ParkerTenBroeck/automata.git
synced 2026-06-06 21:24:06 -04:00
highlight everything
This commit is contained in:
parent
794ac8cdd8
commit
bffa67069d
10 changed files with 155 additions and 159 deletions
|
|
@ -5,7 +5,7 @@ import type { Example } from "./examples.ts";
|
|||
import type { Sim, SimStepResult } from "./simulation.ts";
|
||||
import type wasm from "./wasm.ts";
|
||||
import type { Text } from "npm:@codemirror/state";
|
||||
import type { Highlight } from "./highlight.ts";
|
||||
import type { Highlight, HighlightKind } from "./highlight.ts";
|
||||
|
||||
type Unsubscribe = () => void;
|
||||
|
||||
|
|
@ -68,7 +68,7 @@ type AppEvents = {
|
|||
"begin": void;
|
||||
|
||||
"editor/change": {text: string, doc: Text};
|
||||
"compiled": {log: wasm.CompileLog[], ansi_log: string, machine: string|undefined};
|
||||
"compiled": {log: wasm.CompileLog[], ansi_log: string, machine: Machine|undefined};
|
||||
|
||||
"automata/sim/update": Sim|null;
|
||||
"automata/sim/before_step": { simulation: Sim };
|
||||
|
|
@ -88,9 +88,8 @@ type AppEvents = {
|
|||
|
||||
"highlight/one/add": Highlight;
|
||||
"highlight/one/remove": Highlight;
|
||||
"highlight/all/remove": void;
|
||||
|
||||
"highlight/update": void;
|
||||
"highlight/update": {span: Span, kind: HighlightKind, repr: string, remove: boolean};
|
||||
|
||||
"theme/update": void;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -25,7 +25,8 @@ import wasm from "./wasm.ts";
|
|||
import { Share } from "./share.ts";
|
||||
import { examples } from "./examples.ts";
|
||||
import { bus } from "./bus.ts";
|
||||
import { current, Highlight, HighlightKind } from "./highlight.ts";
|
||||
import { current, Highlight, highlight_span_attr, HighlightKind } from "./highlight.ts";
|
||||
import { Machine, parse_machine_from_json, Span } from "./automata.ts";
|
||||
|
||||
function tokenize(text: string): wasm.Tok[] {
|
||||
try {
|
||||
|
|
@ -38,57 +39,17 @@ function tokenize(text: string): wasm.Tok[] {
|
|||
|
||||
function compile(
|
||||
text: string,
|
||||
): { log: wasm.CompileLog[]; ansi_log: string; machine: string | undefined } {
|
||||
): { log: wasm.CompileLog[]; ansi_log: string; machine: Machine | undefined } {
|
||||
try {
|
||||
return wasm.compile(text);
|
||||
const res = wasm.compile(text);
|
||||
return {machine: res.machine ? parse_machine_from_json(res.machine):undefined, log: res.log, ansi_log: res.ansi_log};
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
return { log: [], ansi_log: "", machine: "" };
|
||||
return { log: [], ansi_log: "", machine: undefined };
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function decoForKind(kind: HighlightKind) {
|
||||
// Use a class per kind so each gets a distinct color via CSS
|
||||
return Decoration.mark({ class: `cm-highlight cm-highlight-${kind}` });
|
||||
}
|
||||
|
||||
bus.on("highlight/update", _ => {
|
||||
const arr = current.values().toArray().sort((a, b) => a.span[0]-b.span[0]);
|
||||
editor.dispatch({ effects: setHighlights.of(arr) });
|
||||
});
|
||||
export const setHighlights = StateEffect.define<Highlight[]>();
|
||||
export const highlightsField = StateField.define<DecorationSet>({
|
||||
create() {
|
||||
return Decoration.none;
|
||||
},
|
||||
|
||||
update(highlights, tr) {
|
||||
// Keep highlights aligned with document edits
|
||||
highlights = highlights.map(tr.changes);
|
||||
|
||||
for (const e of tr.effects) {
|
||||
if (e.is(setHighlights)) {
|
||||
const spans = e.value;
|
||||
|
||||
const builder = new RangeSetBuilder<Decoration>();
|
||||
for (const s of spans) {
|
||||
|
||||
const from = Math.max(0, Math.min(s.span[0], tr.state.doc.length));
|
||||
const to = Math.max(0, Math.min(s.span[1], tr.state.doc.length));
|
||||
if (to > from) builder.add(from, to, decoForKind(s.kind));
|
||||
}
|
||||
highlights = builder.finish();
|
||||
}
|
||||
}
|
||||
|
||||
return highlights;
|
||||
},
|
||||
|
||||
provide: (f) => EditorView.decorations.from(f),
|
||||
});
|
||||
|
||||
|
||||
const eventBusConnection = StateField.define({
|
||||
create(state) {
|
||||
const text = state.doc.toString();
|
||||
|
|
@ -142,6 +103,28 @@ function buildAnalysis(text: string, doc: Text) {
|
|||
}
|
||||
}
|
||||
|
||||
const addDeco = (kind: HighlightKind, highlight: Span, location?: Span) => {
|
||||
if(!location) location = highlight;
|
||||
marks.push(Decoration.mark({attributes: {"highlight-kind": kind, "highlight-span": highlight_span_attr(highlight)}}).range(location[0], location[1]));
|
||||
};
|
||||
|
||||
for (const transitions of machine?.transitions ?? []){
|
||||
for(const transition of transitions[1]){
|
||||
addDeco("focus", transition.function);
|
||||
addDeco("warning", transition.transition);
|
||||
}
|
||||
}
|
||||
|
||||
for (const state of machine?.states.values() ?? []){
|
||||
addDeco("success", state.definition);
|
||||
}
|
||||
|
||||
for (const [state, info] of machine?.final_states?.entries() ?? []){
|
||||
try{
|
||||
addDeco("success", machine?.states.get(state)!.definition!, info.definition);
|
||||
}catch(e){}
|
||||
}
|
||||
|
||||
const deco = Decoration.set(marks, true);
|
||||
return { tokens, log, ansi_log, deco };
|
||||
}
|
||||
|
|
@ -242,7 +225,6 @@ const state = EditorState.create({
|
|||
keymap.of([...defaultKeymap, ...historyKeymap]),
|
||||
|
||||
eventBusConnection,
|
||||
highlightsField,
|
||||
diagHover,
|
||||
|
||||
EditorView.lineWrapping,
|
||||
|
|
|
|||
|
|
@ -392,34 +392,34 @@ d(q3,Y)=(q3,y,R)
|
|||
d(q3,B)=(q4,B,R)
|
||||
`),
|
||||
|
||||
new Example("CFG", "definition",
|
||||
`// CFG's aren't supported yet, and this definition is not complete.
|
||||
// This is the definition for the grammar the definition has itself
|
||||
// new Example("CFG", "definition",
|
||||
// `// CFG's aren't supported yet, and this definition is not complete.
|
||||
// // This is the definition for the grammar the definition has itself
|
||||
|
||||
type=CFG
|
||||
// type=CFG
|
||||
|
||||
S -> TopLevel | TopLevel S
|
||||
// S -> TopLevel | TopLevel S
|
||||
|
||||
TopLevel -> Ident "=" Item // Item
|
||||
TopLevel -> Ident Tuple "=" Item // Transition Functions
|
||||
TopLevel -> Production | Table
|
||||
// TopLevel -> Ident "=" Item // Item
|
||||
// TopLevel -> Ident Tuple "=" Item // Transition Functions
|
||||
// TopLevel -> Production | Table
|
||||
|
||||
Item -> Symbol | String | Tuple | List
|
||||
// Item -> Symbol | String | Tuple | List
|
||||
|
||||
Symbol -> Ident | "~"
|
||||
String -> "\"" "\""
|
||||
Tuple -> "(" ItemList ")"
|
||||
List -> "{" ItemList "}" | "[" ItemList "]"
|
||||
// Symbol -> Ident | "~"
|
||||
// String -> "\"" "\""
|
||||
// Tuple -> "(" ItemList ")"
|
||||
// List -> "{" ItemList "}" | "[" ItemList "]"
|
||||
|
||||
ItemList -> ~ | Item ItemList | Item "," ItemList
|
||||
// ItemList -> ~ | Item ItemList | Item "," ItemList
|
||||
|
||||
Production -> ProductionGroup "->" ProductionGroupList
|
||||
ProductionGroupList -> ProductionGroup | ProductionGroupList "|" ProductionGroup
|
||||
ProductionGroup -> ProductionUnit | ProductionGroup ProductionUnit
|
||||
ProductionUnit -> Ident | "~" | String
|
||||
// Production -> ProductionGroup "->" ProductionGroupList
|
||||
// ProductionGroupList -> ProductionGroup | ProductionGroupList "|" ProductionGroup
|
||||
// ProductionGroup -> ProductionUnit | ProductionGroup ProductionUnit
|
||||
// ProductionUnit -> Ident | "~" | String
|
||||
|
||||
|
||||
`)
|
||||
// `)
|
||||
];
|
||||
|
||||
const CATEGORY_ORDER: Category[] = [
|
||||
|
|
|
|||
|
|
@ -15,7 +15,6 @@ export type Highlight = {
|
|||
type HighlightEntry = {
|
||||
span: Span,
|
||||
kind: HighlightKind,
|
||||
count: number;
|
||||
}
|
||||
|
||||
export const current: Map<string, HighlightEntry> = new Map();
|
||||
|
|
@ -53,68 +52,46 @@ export function dehighlight_from_edge_id(node_id: string) {
|
|||
}
|
||||
}
|
||||
|
||||
bus.on("automata/update", _ => {
|
||||
bus.emit("highlight/all/remove", undefined);
|
||||
})
|
||||
|
||||
function decoForKind(kind: HighlightKind): string {
|
||||
return `cm-highlight-${kind}`;
|
||||
}
|
||||
|
||||
bus.on("highlight/one/add", (highlight) => {
|
||||
const key = asKey(highlight);
|
||||
if (current.has(key)) {
|
||||
current.get(key)!.count += 1;
|
||||
} else {
|
||||
current.set(key, { count: 1, ...highlight });
|
||||
if (!current.has(key)) {
|
||||
current.set(key, {...highlight });
|
||||
|
||||
const cname = decoForKind(highlight.kind);
|
||||
globalThis.document.querySelectorAll(`[highlight-span="${highlight.span[0]}:${highlight.span[1]}"]`).forEach(el => el.classList.add(cname))
|
||||
const repr = `${highlight.span[0]}:${highlight.span[1]}`;
|
||||
globalThis.document.querySelectorAll(`[highlight-span="${repr}"]`).forEach(el => el.classList.add(cname))
|
||||
|
||||
bus.emit("highlight/update", undefined);
|
||||
bus.emit("highlight/update", {repr, remove: false, ...highlight});
|
||||
}
|
||||
});
|
||||
bus.on("highlight/one/remove", (highlight) => {
|
||||
const key = asKey(highlight);
|
||||
if (current.has(key)) {
|
||||
const value = current.get(key)!
|
||||
value.count -= 1;
|
||||
if (value.count === 0) {
|
||||
current.delete(key);
|
||||
if (current.delete(key)) {
|
||||
const cname = decoForKind(highlight.kind);
|
||||
const repr = `${highlight.span[0]}:${highlight.span[1]}`;
|
||||
globalThis.document.querySelectorAll(`[highlight-span="${repr}"]`).forEach(el => el.classList.remove(cname))
|
||||
|
||||
const cname = decoForKind(highlight.kind);
|
||||
globalThis.document.querySelectorAll(`[highlight-span="${highlight.span[0]}:${highlight.span[1]}"]`).forEach(el => el.classList.remove(cname))
|
||||
|
||||
bus.emit("highlight/update", undefined);
|
||||
}
|
||||
bus.emit("highlight/update", {repr, remove: true, ...highlight});
|
||||
}
|
||||
});
|
||||
bus.on("highlight/all/remove", (_) => {
|
||||
if (current.size !== 0) {
|
||||
current.clear();
|
||||
|
||||
const warning = decoForKind("warning");
|
||||
const focus = decoForKind("focus");
|
||||
const success = decoForKind("success");
|
||||
const error = decoForKind("error");
|
||||
globalThis.document.querySelectorAll(`[highlight-span"]`).forEach(el => {
|
||||
el.classList.remove(warning)
|
||||
el.classList.remove(focus)
|
||||
el.classList.remove(success)
|
||||
el.classList.remove(error)
|
||||
})
|
||||
|
||||
|
||||
bus.emit("highlight/update", undefined);
|
||||
}
|
||||
});
|
||||
|
||||
globalThis.document.addEventListener("mouseover", (e) => {
|
||||
const target = (e.target instanceof Element)
|
||||
? e.target.closest("[highlight-span]")
|
||||
if (!(e.target instanceof Element)) return;
|
||||
|
||||
const target = e.target.closest("[highlight-span]");
|
||||
if (!target) return;
|
||||
|
||||
const related = e.relatedTarget instanceof Element
|
||||
? e.relatedTarget.closest("[highlight-span]")
|
||||
: null;
|
||||
|
||||
if (!target) return;
|
||||
// Mouse is still inside the same highlight span → ignore
|
||||
if (related === target) return;
|
||||
|
||||
const kind = (target.getAttribute("highlight-kind") ?? "focus") as unknown as HighlightKind;
|
||||
const span = target.getAttribute("highlight-span")!.split(":").map(Number) as unknown as Span;
|
||||
|
|
@ -134,10 +111,14 @@ document.addEventListener("mouseout", (e) => {
|
|||
|
||||
const kind = (from.getAttribute("highlight-kind") ?? "focus") as unknown as HighlightKind;
|
||||
const span = from.getAttribute("highlight-span")!.split(":").map(Number) as unknown as Span;
|
||||
|
||||
|
||||
bus.emit("highlight/one/remove", {span, kind});
|
||||
});
|
||||
|
||||
export function highlightable(span: Span, text: string, kind?: HighlightKind): string{
|
||||
return `<span class = "cm-highlight" ${kind ? `highlight-kind="${kind}"`:""} highlight-span="${span[0]}:${span[1]}">${text}</span>`
|
||||
}
|
||||
|
||||
export function highlight_span_attr(span: Span): string{
|
||||
return `${span[0]}:${span[1]}`
|
||||
}
|
||||
|
|
@ -100,7 +100,7 @@ function renderTmPath(state: TmState, index: number) {
|
|||
+ highlightable(step.function, `${DELTA}(${step.from_state}, ${step.from_symbol})`, "focus")
|
||||
+ " = "
|
||||
+ highlightable(step.transition, `(${step.state}, ${step.symbol}, ${step.direction})`, "warning");
|
||||
console.log(div.innerHTML);
|
||||
|
||||
steps.appendChild(div);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@ import type {
|
|||
Pda,
|
||||
Tm,
|
||||
} from "./automata.ts";
|
||||
import {parse_machine_from_json} from "./automata.ts";
|
||||
|
||||
import { FaSim } from "./simulation/fa.ts";
|
||||
export { FaSim } from "./simulation/fa.ts";
|
||||
|
|
@ -34,7 +33,7 @@ export let automaton: Machine = {
|
|||
bus.on("compiled", ({ machine }) => {
|
||||
if (machine) {
|
||||
try {
|
||||
automaton = parse_machine_from_json(machine);
|
||||
automaton = machine;
|
||||
bus.emit("automata/update", automaton);
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
|
|
|
|||
|
|
@ -98,7 +98,6 @@ function enableFlexSplitters() {
|
|||
{
|
||||
const r = parent.getBoundingClientRect();
|
||||
const px = clamp((defPct / 100) * r.height, minA, r.height - gap - minB);
|
||||
console.log(r.height, px)
|
||||
setFixedSize(a, "y", px);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -8,8 +8,7 @@ import { dehighlight_from_edge_id, dehighlight_from_node_id, highlight_from_edge
|
|||
|
||||
|
||||
bus.on("controls/vis/physics", ({ enabled }) => {
|
||||
network.setOptions({ physics: { enabled } });
|
||||
network.setOptions({ edges: { smooth: enabled } });
|
||||
network.setOptions({nodes: {physics: enabled}});
|
||||
});
|
||||
bus.on("controls/vis/reset_network", _ => {
|
||||
try {
|
||||
|
|
@ -34,10 +33,12 @@ bus.on("automata/sim/update", _ => {
|
|||
});
|
||||
|
||||
bus.on("automata/update", automaton => {
|
||||
spanEdgeMap.clear();
|
||||
spanNodeMap.clear();
|
||||
|
||||
// Populate nodes
|
||||
for (const state of automaton.states.keys()) {
|
||||
|
||||
for (const [state, value] of automaton.states.entries()) {
|
||||
spanNodeMap.set(`${value.definition[0]}:${value.definition[1]}`, state);
|
||||
const size = measureTextWidth(state, getGraphTheme().node_font) / 2 + 10
|
||||
if (nodes.get(state)) {
|
||||
nodes.update({
|
||||
|
|
@ -65,6 +66,10 @@ bus.on("automata/update", automaton => {
|
|||
vadjust
|
||||
}
|
||||
};
|
||||
transitions.forEach(edge => {
|
||||
spanEdgeMap.set(`${edge.function[0]}:${edge.function[1]}`, edge_id);
|
||||
spanEdgeMap.set(`${edge.transition[0]}:${edge.transition[1]}`, edge_id);
|
||||
})
|
||||
if (edges.get(edge_id)) {
|
||||
edges.update({
|
||||
id: edge_id,
|
||||
|
|
@ -99,6 +104,29 @@ bus.on("automata/update", automaton => {
|
|||
}
|
||||
});
|
||||
|
||||
bus.on("highlight/update", ({repr, remove}) => {
|
||||
if(spanNodeMap.has(repr)){
|
||||
const id = spanNodeMap.get(repr)!;
|
||||
if(remove){
|
||||
// @ts-expect-error bad library
|
||||
nodes.update({id, color: null});
|
||||
}else{
|
||||
nodes.update({id, color: getGraphTheme().current_node_border});
|
||||
}
|
||||
}
|
||||
if(spanEdgeMap.has(repr)){
|
||||
const id = spanEdgeMap.get(repr)!;
|
||||
if(remove){
|
||||
// @ts-expect-error bad library
|
||||
edges.update({id, font: null});
|
||||
}else{
|
||||
edges.update({id, font: {color: getGraphTheme().node_anchor}});
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
const spanEdgeMap: Map<string, string> = new Map()
|
||||
const spanNodeMap: Map<string, string> = new Map()
|
||||
|
||||
const nodes = new vis.DataSet<vis.Node>();
|
||||
const edges = new vis.DataSet<vis.Edge>();
|
||||
|
|
@ -184,6 +212,7 @@ function updateGraphTheme() {
|
|||
network.setOptions({
|
||||
nodes: {
|
||||
labelHighlightBold: false,
|
||||
color: gt.fg_0,
|
||||
font: {
|
||||
color: gt.fg_0,
|
||||
bold: {
|
||||
|
|
@ -378,7 +407,7 @@ function renderNode({
|
|||
}
|
||||
|
||||
ctx.lineWidth = 2;
|
||||
ctx.fillStyle = t.fg_0;
|
||||
ctx.fillStyle = (style.color ?? t.fg_0) as string;
|
||||
ctx.strokeStyle = t.bg_0;
|
||||
ctx.strokeText(label, x, y);
|
||||
ctx.fillText(label, x, y);
|
||||
|
|
@ -487,7 +516,6 @@ function drawInitialArrow(
|
|||
|
||||
ctx.restore();
|
||||
}
|
||||
|
||||
function drawPinIndicator(
|
||||
ctx: CanvasRenderingContext2D,
|
||||
x: number,
|
||||
|
|
@ -495,55 +523,56 @@ function drawPinIndicator(
|
|||
r: number,
|
||||
color: string,
|
||||
) {
|
||||
const size = Math.max(7, Math.round(r * 0.28));
|
||||
const ox = x + r - size * 0.55;
|
||||
const oy = y + r - size * 0.55;
|
||||
const size = Math.max(7, Math.round(r * 0.28));
|
||||
|
||||
const stroke = color;
|
||||
const fill = "rgba(0,0,0,0)";
|
||||
// Position near bottom-right of node
|
||||
const cx = x + r - size * 0.6;
|
||||
const cy = y + r - size * 0.55;
|
||||
|
||||
const headRadius = size * 0.45;
|
||||
const rimRadius = headRadius * 0.85;
|
||||
const needleLength = size * 1.1;
|
||||
|
||||
ctx.save();
|
||||
|
||||
|
||||
const strokeWidth = Math.max(1.25, Math.round(r * 0.06));
|
||||
ctx.lineWidth = strokeWidth;
|
||||
ctx.strokeStyle = color;
|
||||
ctx.fillStyle = "rgba(0,0,0,0)";
|
||||
|
||||
ctx.shadowColor = "rgba(0,0,0,0)";
|
||||
ctx.shadowBlur = 6;
|
||||
ctx.shadowOffsetX = 0;
|
||||
ctx.shadowOffsetY = 2;
|
||||
ctx.shadowBlur = 4;
|
||||
ctx.shadowOffsetY = 1;
|
||||
|
||||
// Pin head (circle)
|
||||
// ---- Head (top disc)
|
||||
ctx.beginPath();
|
||||
ctx.arc(ox, oy, size * 0.55, 0, Math.PI * 2);
|
||||
ctx.fillStyle = fill;
|
||||
ctx.fill();
|
||||
|
||||
// Pin stem (triangle-ish)
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(ox, oy + size * 0.25);
|
||||
ctx.lineTo(ox - size * 0.35, oy + size * 0.95);
|
||||
ctx.lineTo(ox + size * 0.35, oy + size * 0.95);
|
||||
ctx.closePath();
|
||||
ctx.fillStyle = fill;
|
||||
ctx.fill();
|
||||
|
||||
// Outline
|
||||
ctx.shadowBlur = 0;
|
||||
ctx.lineWidth = Math.max(1.25, Math.round(r * 0.06));
|
||||
ctx.strokeStyle = stroke;
|
||||
|
||||
ctx.beginPath();
|
||||
ctx.arc(ox, oy, size * 0.55, 0, Math.PI * 2);
|
||||
ctx.arc(cx, cy, headRadius, 0, Math.PI * 2);
|
||||
ctx.stroke();
|
||||
|
||||
// ---- Rim (inner ring)
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(ox, oy + size * 0.25);
|
||||
ctx.lineTo(ox - size * 0.35, oy + size * 0.95);
|
||||
ctx.lineTo(ox + size * 0.35, oy + size * 0.95);
|
||||
ctx.closePath();
|
||||
ctx.arc(cx, cy, rimRadius, 0, Math.PI * 2);
|
||||
ctx.stroke();
|
||||
|
||||
// Inner dot
|
||||
// ---- Needle
|
||||
ctx.beginPath();
|
||||
ctx.arc(ox, oy, size * 0.18, 0, Math.PI * 2);
|
||||
ctx.fillStyle = stroke;
|
||||
ctx.moveTo(cx, cy + rimRadius * 0.9);
|
||||
ctx.lineTo(cx, cy + rimRadius * 0.9 + needleLength);
|
||||
ctx.stroke();
|
||||
|
||||
// ---- Needle tip
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(cx - strokeWidth * 0.6, cy + rimRadius * 0.9 + needleLength);
|
||||
ctx.lineTo(cx, cy + rimRadius * 0.9 + needleLength + strokeWidth * 1.6);
|
||||
ctx.lineTo(cx + strokeWidth * 0.6, cy + rimRadius * 0.9 + needleLength);
|
||||
ctx.closePath();
|
||||
ctx.fillStyle = color;
|
||||
ctx.fill();
|
||||
|
||||
// ---- Center dot (plastic reflection)
|
||||
ctx.beginPath();
|
||||
ctx.arc(cx, cy, headRadius * 0.18, 0, Math.PI * 2);
|
||||
ctx.fill();
|
||||
|
||||
ctx.restore();
|
||||
|
|
|
|||
|
|
@ -5,6 +5,14 @@
|
|||
.editor {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
|
||||
}
|
||||
|
||||
.cm-lineWrapping{
|
||||
word-break: keep-all!important;
|
||||
word-wrap: normal!important;
|
||||
white-space: nowrap!important;
|
||||
overflow-wrap: normal!important;
|
||||
}
|
||||
|
||||
.cm-scroller {
|
||||
|
|
|
|||
|
|
@ -8,7 +8,6 @@
|
|||
white-space: pre-wrap;
|
||||
word-break: break-word;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
overflow-y: auto;
|
||||
overflow-x: auto;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue