Tiptap v2(Prosemirror)与 NextJS

问题描述 投票:0回答:1

这是我第一次在 Stack Overflow 上写作,我请求您帮助解决问题。

我要向您提交的问题与我的新初创公司的 MVP 开发有关,该公司致力于为创意产业开发 SaaS。有问题的 MVP 是一个用于编写剧本的文本编辑器,我遇到了问题。

如果您不知道剧本有很多功能,包括场景标题是这样制作的:“1. EXT. PUB - NIGHT”

请记住,我能够执行以下操作:

  • 使每个段落独立,
  • 创建一个根据一组关键字自动检测格式的函数,
  • 当算法处于活动状态时,将段落中的所有文本设为大写;
import { Extension } from '@tiptap/core';
import { Plugin } from 'prosemirror-state';
import { Fragment } from 'prosemirror-model';

let paragraphCount = 0; // Variable to track paragraph count

const SceneTitle = Extension.create({
    name: 'SceneTitle',

    addProseMirrorPlugins() {
        return [
            new Plugin({
                appendTransaction: (_, oldState, newState) => {
                    let tr = newState.tr;
                    let oldParagraphCount = 0;
                    let newParagraphCount = 0;

                    const regex = /^(INT\.|EXT\.|int\.|ext\.|INT|EXT|int|ext|EST|EST\.|est|est\.)/;

                    oldState.doc.descendants((node) => {
                        if (node.type.name === 'paragraph') {
                            oldParagraphCount++;
                        }
                    });

                    newState.doc.descendants((node, pos) => {
                        if (node.type.name === 'paragraph') {
                            newParagraphCount++;
                            if (regex.test(node.textContent)) {
                                let from = pos + 1;
                                let to = from + node.textContent.length;
                                let fragment = Fragment.from(newState.schema.text(node.textContent.toUpperCase()));
                                tr.replaceWith(from, to, fragment);
                            }
                        }
                    });

                    if (newParagraphCount > oldParagraphCount) {
                        paragraphCount++;
                        console.log(`Paragraph inserted. Total paragraphs: ${paragraphCount}`); // Print paragraph count
                    } else if (newParagraphCount < oldParagraphCount) {
                        paragraphCount = 0;
                        console.log(`Paragraphs deleted. Paragraph count reset to zero.`); // Print paragraph count reset
                    }

                    return tr;
                },
            }),
        ];
    },
});

export default SceneTitle;

接下来,当我输入场景标题的编号时,通过创建索引,我可以将段落分为三个部分,即:编号、包含关键字的文本和其余文本。只有当我去写关键字时,作为“Int”,它会让我只大写关键字,而不是它后面的文本,但是还没有写,如果我先写将要写的文本在关键字之后,然后写下关键字,如“Int”,它使我将所有内容都大写。

import { Extension } from '@tiptap/core';
import { Plugin } from 'prosemirror-state';
import { Fragment } from 'prosemirror-model';

let paragraphCount = 0; // Variable to keep track of paragraph count
let sceneCount = 1; // Variable to track scene count
let appliedPositions = new Set(); // Together to track applied positions

const SceneTitle = Extension.create({
    name: 'SceneTitle',

    addProseMirrorPlugins() {
        return [
            new Plugin({
                appendTransaction: (_, oldState, newState) => {
                    let tr = newState.tr;
                    let oldParagraphCount = 0;
                    let newParagraphCount = 0;

                    const regex = /^(INT\.|EXT\.|int\.|ext\.|INT|EXT|int|ext|EST|EST\.|est|est\.)/;

                    oldState.doc.descendants((node) => {
                        if (node.type.name === 'paragraph') {
                            oldParagraphCount++;
                        }
                    });

                    newState.doc.descendants((node, pos) => {
                        if (node.type.name === 'paragraph' && regex.test(node.textContent)) {
                            if (!appliedPositions.has(pos)) {
                                let from = pos + 1;
                                let to = from + node.textContent.length;
                                let fragment = Fragment.from(newState.schema.text(sceneCount + "." + " " + node.textContent.toUpperCase() + "." + " "));
                                tr.replaceWith(from, to, fragment);
                                appliedPositions.add(pos);
                                sceneCount++;
                            }
                        }
                        if (node.type.name === 'paragraph') {
                            newParagraphCount++;
                        }
                    });

                    if (newParagraphCount > oldParagraphCount) {
                        paragraphCount++;
                        console.log(`Paragraph inserted. Total paragraphs: ${paragraphCount}`); // Print paragraph count
                    } else if (newParagraphCount < oldParagraphCount) {
                        paragraphCount = 0;
                        sceneCount = 1; // Resets scene count only when paragraphs are deleted
                        appliedPositions.clear();
                        console.log(`Paragraphs deleted. Paragraph and scene count reset to zero.`); // Print paragraph and scene count reset
                    }

                    return tr;
                },
            }),
        ];
    },
});

export default SceneTitle;

现在我不知道如何使场景标题具有渐进编号,它不循环,并且该段落中的所有文本都是大写的。

我遇到的另一个问题是,模拟用户可能能够做的事情,记住他们是编剧、电影制作人和视频制作人,就是在现有的场景标题之上插入一个新的场景标题,只有我能够做到这一点更新但不正确,也就是说,新标题的序列号位于创建的最后一段之后,所以:

  • 第一个错误编号示例:

“1.INT....”
“1. 分机....”
“2.INT....”
“3. 分机....”

  • 错误编号的第二个示例:

“4.INT....”
“1. 分机....”
“2.INT....”
“3. 分机....”

我使用的技术栈如下:

  • NextJS v14.1.0,
  • TailwindCSS,
  • TipTap v2,
  • 打字稿,
  • NodeJS v20.8.0

下面是几个示例:

  • 场景标题应如何编号的第一个示例:
  1. INT。酒吧 - 夜晚
  • 具有不同场景标题的编号序列的示例:
  1. INT。酒吧 - 夜晚
    我们发现肖恩正和一个女人莉兹坐在一起。他们是
    两人都二十多岁了。 LZ看起来有点担心,
    肖恩看上去有些困惑。他们正在喝酒。
  2. INT。公寓大楼 - 早上
  3. INT。公寓 - 早上
  4. INT。客厅 - 早上
regex typescript next.js pagination tiptap
1个回答
0
投票

我以前从未做过一些NextJS,但我只会给你一些 提示一个最小的普通 JS 示例,然后您可以实现 你自己在你的应用程序中。

第一步:正则表达式

正如我在评论中提到的,你的正则表达式应该 被重写以处理您的所有输入。我已经做到了 处理:

  • 带点或不带点的可选数字。这个可能是 正如您提到的,这是错误的,所以我们将放弃它。

  • 一种地点(

    INT
    EXT
    EST
    ),带有可选 点,并且不区分大小写,以便匹配“INT”,“Int” 或“int”等

  • 地名(例如:“卧室”)。

  • 连字符分隔符,周围有强制空格。

  • 什么时候(例如:“早上”)。

基于我通过评论发布的正则表达式,附加 在开始时处理最终不需要的数字:

/^(\d+\.?\s*)?(?<place_type>int|ext|est)\.?\s*(?<place>.*?)\s+-\s+(?<when>.*?)\s*$/i

如您所见,我使用命名组是因为我发现它们比 索引组,特别是如果您稍后更改正则表达式。团体 名称不会改变,但索引组会改变。

在此处测试详细信息:https://regex101.com/r/qDPvYK/1

第2步:将段落转换为标题

正如我在评论中也提到的,不这样做可能要简单得多 自己计算标题,只需使用 CSS 计数器即可。

您说您无法将段落转换为标题。 是的你是对的。但只要用谷歌搜索一下,你很快就会 发现你只需要创建一个新的

<h2>
标签并将其放入 而不是
<p>
标签。这可以通过很多方式来完成。这 我发现最简单的是获取父节点,然后使用
.replaceChild()
方法。

我在 HTML 演示中添加了一个按钮,以便您可以看到 运行转换算法之前和之后的内容。

const regexTitle = /^(\d+\.?\s*)?(?<place_type>int|ext|est)\.?\s*(?<place>.*?)\s+-\s+(?<when>.*?)\s*$/gmi;

document.addEventListener('DOMContentLoaded', function() {

    function convertToTitles() {
    // Loop over all paragraphs to try and convert some to headings.
    document.querySelectorAll('p').forEach((p, i) => {
      const match = regexTitle.exec(p.innerText);

      if (match) {
        const placeType = match.groups.place_type.toUpperCase(),
              place = match.groups.place,
              when = match.groups.when;

        const h2 = document.createElement('h2');
        h2.innerText = placeType + '. ' + place + ' - ' + when;
        p.parentNode.replaceChild(h2, p);
      }
    });
  }
  
  const convertButton = document.getElementById('convert-to-titles');
  
  convertButton.addEventListener('click', convertToTitles);
});
body {
  background: silver;
  padding: 2em;
}

.scenario {
  min-width: 15em;
  max-width: 30em;
  background: white;
  /* More padding on the left to pull out the scene title number. */
  padding: 2em 2em 2em 4em;
  margin-bottom: 2em;
  box-shadow: 0 0 .5em rgba(0, 0, 0, 0.2);
  font-family: "Courrier New", Courrier, monospace;
  counter-reset: scene-title-nbr;
}

h1 {
  margin-top: 0;
}

h2 {
  margin: 1em 0 .5em 0;
  font-size: 1em;
  text-transform: uppercase;
  position: relative; /* For absolute ::before pseudo-element. */
}

h2::before {
  content: counter(scene-title-nbr);
  counter-increment: scene-title-nbr;
  position: absolute;
  left: -2em;
}
<article class="scenario">
  <header>
    <h1>Scenario</h1>
  </header>
  
  <p>INT. Pub - night</p>
  <p>
    We reveal that SHAUN is sitting with a woman, LIZ. They are both in their
    late twenties. LIZ looks slightly concerned, SHAUN looks slightly confused.
    They are having a drink.
  </p>

  <p>INT. Apartment complex - morning</p>
  <p>
    Lorem ipsum dolor sit amet consectetur adipisicing elit. Quasi
    maxime quisquam itaque numquam quas tempore quo, ex reprehenderit
    ea dicta sequi voluptatem ipsum id cupiditate autem perspiciatis
    obcaecati consectetur quod.
  </p>

  <p>1. INT. Apartment - morning</p>
  <p>
    Text should come here...
  </p>

  <p>INT. Living room - morning</p>
  <p>
    Text should come here...
  </p>

  <p>3. Ext Swimming-pool - midday</p>
  <p>SHAUN dives into the pool while LIZ is sunbathing...</p>
</article>

<button id="convert-to-titles">Convert to titles</button>

© www.soinside.com 2019 - 2024. All rights reserved.