p = 'frontend/src/components/MarkdownToolbar.jsx'
s = open(p).read()

s = s.replace("""const LinkPicker = lazy(() => import('./LinkPicker'))""",
"""const LinkPicker = lazy(() => import('./LinkPicker'))
// Same reasoning: a list of glossary terms and a search box, wanted by a
// minority of edits.
const GlossaryPicker = lazy(() => import('./GlossaryPicker'))""")

s = s.replace("""//: What a tip reads as before anything has been typed into it.
const TIP_PHRASE = 'phrase'
const TIP_TEXT = 'the teaching point'""",
"""//: What a tip reads as before anything has been typed into it.
const TIP_PHRASE = 'phrase'
const TIP_TEXT = 'the teaching point'

//: A tip's kind, which decides its mark and its colour. `define` is what an
//: unmarked tip has always been, so it is written without the third field.
const TIP_KINDS = [
  { key: 'define', mark: 'ⓘ', label: 'Definition' },
  { key: 'fact', mark: '★', label: 'Worth knowing' },
  { key: 'why', mark: '⚕', label: 'Why it happens' },
  { key: 'caution', mark: '!', label: 'Catches people out' },
]""")

s = s.replace("""    } else if (action.kind === TIP) {
      // With words selected, they become the underlined phrase and the caret
      // waits where the tip goes. With nothing selected there is a placeholder
      // to type over, selected so the first keystroke replaces it.
      const selected = text.slice(start, end)
      const phrase = selected || TIP_PHRASE
      const tip = selected ? '' : TIP_TEXT
      next = `${text.slice(0, start)}{{${phrase}|${tip}}}${text.slice(end)}`
      const tipAt = start + 2 + phrase.length + 1
      select = selected ? [tipAt, tipAt] : [start + 2, start + 2 + phrase.length]
      caret = tipAt""",
"""    } else if (action.kind === TIP) {
      // With words selected, they become the underlined phrase and the caret
      // waits where the tip goes. With nothing selected there is a placeholder
      // to type over, selected so the first keystroke replaces it.
      //
      // A `bare` tip has no phrase at all: a chip at the end of a sentence,
      // for an aside that is not about any one word in it.
      const selected = text.slice(start, end)
      const phrase = action.bare ? '' : (selected || TIP_PHRASE)
      const tip = (selected && !action.bare) ? '' : TIP_TEXT
      const tail = action.tipKind && action.tipKind !== 'define' ? `|${action.tipKind}` : ''
      next = `${text.slice(0, start)}{{${phrase}|${tip}${tail}}}${text.slice(end)}`
      const tipAt = start + 2 + phrase.length + 1
      select = (selected && !action.bare)
        ? [tipAt, tipAt]
        : (action.bare ? [tipAt, tipAt + TIP_TEXT.length] : [start + 2, start + 2 + phrase.length])
      caret = tipAt""")

s = s.replace("""  { key: 'math', label: '∑', title: 'Inline maths', kind: WRAP, before: '$', after: '$' },
  // Select the words first; the tip is what you type next.
  { key: 'tip', label: '⚕ Tip', title: 'Turn the selected words into a tip', kind: TIP },
]""",
"""  { key: 'math', label: '∑', title: 'Inline maths', kind: WRAP, before: '$', after: '$' },
]""")

s = s.replace("""  const [linking, setLinking] = useState(false)""",
"""  const [linking, setLinking] = useState(false)
  //: The two menus that hang off the Tip button: which kind of card, and the
  //: shared glossary for a word that is already defined somewhere.
  const [tipping, setTipping] = useState(false)
  const [glossing, setGlossing] = useState(false)""")

s = s.replace("""      <span className="mdbar-link">
        <button type="button" title="Link to an article" aria-label="Link to an article"
          aria-expanded={linking} onClick={() => setLinking(v => !v)}>
          🔗 Link
        </button>
        {linking && (
          <Suspense fallback={null}>
            <LinkPicker onClose={() => setLinking(false)} onInsert={insert} />
          </Suspense>
        )}
      </span>""",
"""      <span className="mdbar-link">
        {/* One button, four kinds, plus the glossary. A tip is written far
            less often than bold is, and four buttons in the bar for it would
            crowd out the formatting somebody actually reaches for. */}
        <button type="button" title="Add a tip" aria-label="Add a tip"
          aria-expanded={tipping} onClick={() => { setTipping(v => !v); setGlossing(false) }}>
          ⚕ Tip
        </button>
        {tipping && (
          <span className="mdbar-menu" role="menu">
            {TIP_KINDS.map(kind => (
              <button type="button" key={kind.key} role="menuitem"
                onClick={() => { setTipping(false); apply({ kind: TIP, tipKind: kind.key }) }}>
                <span className={`mdbar-mark is-${kind.key}`} aria-hidden="true">{kind.mark}</span>
                {kind.label}
              </button>
            ))}
            <span className="mdbar-menu-rule" />
            <button type="button" role="menuitem"
              onClick={() => { setTipping(false); apply({ kind: TIP, tipKind: 'fact', bare: true }) }}>
              <span className="mdbar-mark" aria-hidden="true">◦</span>
              At the end of a sentence
            </button>
            <button type="button" role="menuitem"
              onClick={() => { setTipping(false); setGlossing(true) }}>
              <span className="mdbar-mark" aria-hidden="true">≡</span>
              From the glossary
            </button>
          </span>
        )}
        {glossing && (
          <Suspense fallback={null}>
            <GlossaryPicker onClose={() => setGlossing(false)} onInsert={insert} />
          </Suspense>
        )}
      </span>
      <span className="mdbar-link">
        <button type="button" title="Link to an article" aria-label="Link to an article"
          aria-expanded={linking} onClick={() => setLinking(v => !v)}>
          🔗 Link
        </button>
        {linking && (
          <Suspense fallback={null}>
            <LinkPicker onClose={() => setLinking(false)} onInsert={insert} />
          </Suspense>
        )}
      </span>""")
open(p, 'w').write(s)
print('written')
