修改文字堆砌的问题
This commit is contained in:
@@ -72,6 +72,8 @@ export class LecturePaneView {
|
||||
private playMode: "overview" | "paragraph" = "paragraph";
|
||||
private readGranularity: ReadGranularity = "paragraph";
|
||||
private activeBeatId?: string;
|
||||
private selectedBeatId?: string;
|
||||
private cachedBeats: StoredBeat[] = [];
|
||||
private activeParagraphId?: string;
|
||||
private activeSentenceId?: string;
|
||||
private selectedParagraphId?: string;
|
||||
@@ -209,10 +211,7 @@ export class LecturePaneView {
|
||||
);
|
||||
this.paraListHintEl.hidden = true;
|
||||
this.beatListEl = this.el("div", "chatpapers-lecture-beat-list chatpapers-lecture-scroll-y");
|
||||
this.paragraphDetailEl = this.el(
|
||||
"div",
|
||||
"chatpapers-lecture-para-detail chatpapers-lecture-scroll-y",
|
||||
);
|
||||
this.paragraphDetailEl = this.el("div", "chatpapers-lecture-para-detail");
|
||||
this.selectionExplainEl = this.el("div", "chatpapers-lecture-selection-explain");
|
||||
this.selectionExplainEl.hidden = true;
|
||||
|
||||
@@ -551,6 +550,7 @@ export class LecturePaneView {
|
||||
? getString("lecture-sentence-list-title")
|
||||
: getString("lecture-paragraph-list-title");
|
||||
this.paraListHintEl.hidden = false;
|
||||
this.paraListHintEl.textContent = getString("lecture-para-list-hint");
|
||||
this.renderParagraphList(paragraphs);
|
||||
this.renderParagraphDetail(
|
||||
paragraphs,
|
||||
@@ -598,11 +598,13 @@ export class LecturePaneView {
|
||||
this.granularityTabsEl.classList.add("is-visible");
|
||||
} else if (hasBeats) {
|
||||
this.granularityTabsEl.classList.remove("is-visible");
|
||||
this.paraListHintEl.hidden = true;
|
||||
this.paraListHintEl.hidden = false;
|
||||
this.paraListHintEl.textContent = getString("lecture-beat-list-hint");
|
||||
this.playerTitleEl.textContent = getString("lecture-beat-list-title");
|
||||
this.beatListEl.hidden = false;
|
||||
this.paragraphDetailEl.hidden = true;
|
||||
this.cachedBeats = beats;
|
||||
this.renderBeatList(beats);
|
||||
this.renderBeatDetail(beats, this.selectedBeatId ?? this.activeBeatId);
|
||||
|
||||
if (!this.player) {
|
||||
this.player = createPlayLecture({
|
||||
@@ -610,7 +612,9 @@ export class LecturePaneView {
|
||||
beats,
|
||||
onBeatChange: (id) => {
|
||||
this.activeBeatId = id;
|
||||
this.highlightBeat(id);
|
||||
if (id) this.selectedBeatId = id;
|
||||
this.renderBeatList(this.cachedBeats);
|
||||
this.renderBeatDetail(this.cachedBeats, id);
|
||||
},
|
||||
onPlayingChange: (playing) => {
|
||||
this.playBtn.hidden = playing;
|
||||
@@ -843,7 +847,10 @@ export class LecturePaneView {
|
||||
if (!para) {
|
||||
this.paragraphDetailEl.hidden = false;
|
||||
this.paragraphDetailEl.classList.add("is-empty");
|
||||
this.paragraphDetailEl.classList.remove("chatpapers-lecture-scroll-y");
|
||||
this.paragraphDetailEl.classList.remove(
|
||||
"chatpapers-lecture-scroll-y",
|
||||
"chatpapers-lecture-beat-detail",
|
||||
);
|
||||
this.paragraphDetailEl.append(
|
||||
this.el(
|
||||
"div",
|
||||
@@ -854,7 +861,7 @@ export class LecturePaneView {
|
||||
return;
|
||||
}
|
||||
this.paragraphDetailEl.hidden = false;
|
||||
this.paragraphDetailEl.classList.remove("is-empty");
|
||||
this.paragraphDetailEl.classList.remove("is-empty", "chatpapers-lecture-beat-detail");
|
||||
this.paragraphDetailEl.classList.add("chatpapers-lecture-scroll-y");
|
||||
|
||||
const sentence = sentenceId
|
||||
@@ -896,7 +903,11 @@ export class LecturePaneView {
|
||||
}
|
||||
|
||||
const originalText = sentence?.text ?? para.originalText;
|
||||
const translationText = sentence?.translation ?? para.translation;
|
||||
const translationText = (
|
||||
sentence?.translation ||
|
||||
(!sentence ? para.translation : "") ||
|
||||
""
|
||||
).trim();
|
||||
|
||||
this.paragraphDetailEl.append(
|
||||
this.el("div", "chatpapers-lecture-para-original-label", getString("lecture-para-original")),
|
||||
@@ -905,11 +916,20 @@ export class LecturePaneView {
|
||||
`chatpapers-lecture-para-original${sentence ? " is-active-sentence" : ""}`,
|
||||
originalText,
|
||||
),
|
||||
this.el("div", "chatpapers-lecture-para-translation-label", getString("lecture-para-translation")),
|
||||
this.el("div", "chatpapers-lecture-para-translation", translationText),
|
||||
);
|
||||
|
||||
if (sentence && (para.sentences?.length ?? 0) > 1) {
|
||||
if (translationText) {
|
||||
this.paragraphDetailEl.append(
|
||||
this.el("div", "chatpapers-lecture-para-translation-label", getString("lecture-para-translation")),
|
||||
this.el("div", "chatpapers-lecture-para-translation", translationText),
|
||||
);
|
||||
}
|
||||
|
||||
if (
|
||||
sentence &&
|
||||
this.readGranularity !== "sentence" &&
|
||||
(para.sentences?.length ?? 0) > 1
|
||||
) {
|
||||
const sentenceList = this.el("div", "chatpapers-lecture-sentence-list");
|
||||
for (const s of para.sentences ?? []) {
|
||||
const item = this.doc.createElement("button");
|
||||
@@ -1097,57 +1117,143 @@ export class LecturePaneView {
|
||||
private renderBeatList(beats: StoredBeat[]): void {
|
||||
this.beatListEl.replaceChildren();
|
||||
for (const beat of beats) {
|
||||
const row = this.doc.createElement("button") as HTMLButtonElement;
|
||||
row.type = "button";
|
||||
row.className = "chatpapers-lecture-beat-item";
|
||||
row.dataset.beatId = beat.id;
|
||||
if (beat.id === this.activeBeatId) {
|
||||
row.classList.add("is-active");
|
||||
}
|
||||
if (beat.ttsStatus !== "ready") {
|
||||
row.classList.add("is-pending");
|
||||
row.disabled = true;
|
||||
}
|
||||
|
||||
const meta = this.el("div", "chatpapers-lecture-beat-meta");
|
||||
meta.append(
|
||||
this.el(
|
||||
"span",
|
||||
"chatpapers-lecture-beat-type",
|
||||
BEAT_TYPE_LABELS[beat.type] || beat.type,
|
||||
),
|
||||
this.el(
|
||||
"span",
|
||||
"chatpapers-lecture-beat-title",
|
||||
beat.title || beat.id,
|
||||
),
|
||||
);
|
||||
|
||||
const status = this.el("span", "chatpapers-lecture-beat-status");
|
||||
if (beat.ttsStatus === "ready") {
|
||||
status.textContent = "✓";
|
||||
} else if (beat.ttsStatus === "failed") {
|
||||
status.textContent = getString("lecture-beat-tts-failed");
|
||||
} else {
|
||||
status.textContent = getString("lecture-beat-tts-pending");
|
||||
}
|
||||
|
||||
row.append(meta, status);
|
||||
row.addEventListener("click", () => {
|
||||
void this.player?.jumpToBeatId(beat.id);
|
||||
});
|
||||
this.beatListEl.append(row);
|
||||
this.beatListEl.append(this.makeBeatRow(beats, beat));
|
||||
}
|
||||
}
|
||||
|
||||
private highlightBeat(beatId?: string): void {
|
||||
this.activeBeatId = beatId;
|
||||
for (const node of this.beatListEl.querySelectorAll(
|
||||
".chatpapers-lecture-beat-item",
|
||||
)) {
|
||||
const el = node as HTMLElement;
|
||||
el.classList.toggle("is-active", el.dataset.beatId === beatId);
|
||||
private isBeatSelected(beatId: string): boolean {
|
||||
return this.selectedBeatId === beatId;
|
||||
}
|
||||
|
||||
private isBeatPlaying(beatId: string): boolean {
|
||||
return this.activeBeatId === beatId;
|
||||
}
|
||||
|
||||
private selectBeat(beats: StoredBeat[], beatId: string): void {
|
||||
this.selectedBeatId = beatId;
|
||||
this.renderBeatList(beats);
|
||||
this.renderBeatDetail(beats, beatId);
|
||||
}
|
||||
|
||||
private playBeat(beat: StoredBeat): void {
|
||||
void this.player?.jumpToBeatId(beat.id);
|
||||
}
|
||||
|
||||
private makeBeatPlayButton(beat: StoredBeat): HTMLButtonElement {
|
||||
const playBtn = this.doc.createElement("button");
|
||||
playBtn.type = "button";
|
||||
playBtn.className = "chatpapers-lecture-beat-play";
|
||||
playBtn.title = getString("lecture-para-play");
|
||||
playBtn.setAttribute("aria-label", getString("lecture-para-play"));
|
||||
playBtn.append(
|
||||
createLucideIcon(this.doc, Play, {
|
||||
size: 14,
|
||||
className: "chatpapers-lecture-btn-icon",
|
||||
}),
|
||||
);
|
||||
playBtn.disabled = beat.ttsStatus !== "ready";
|
||||
playBtn.addEventListener("click", (event) => {
|
||||
event.stopPropagation();
|
||||
this.playBeat(beat);
|
||||
});
|
||||
return playBtn;
|
||||
}
|
||||
|
||||
private makeBeatRow(beats: StoredBeat[], beat: StoredBeat): HTMLElement {
|
||||
const row = this.el("div", "chatpapers-lecture-beat-item");
|
||||
row.dataset.beatId = beat.id;
|
||||
if (this.isBeatSelected(beat.id)) row.classList.add("is-selected");
|
||||
if (this.isBeatPlaying(beat.id)) row.classList.add("is-active");
|
||||
if (beat.ttsStatus !== "ready") row.classList.add("is-pending");
|
||||
|
||||
const main = this.doc.createElement("button");
|
||||
main.type = "button";
|
||||
main.className = "chatpapers-lecture-beat-main";
|
||||
|
||||
const preview = beat.title?.trim() || beat.script;
|
||||
const line = this.el("div", "chatpapers-lecture-beat-line");
|
||||
line.append(
|
||||
this.el(
|
||||
"span",
|
||||
"chatpapers-lecture-beat-label",
|
||||
getString("lecture-beat-label", { args: { n: beat.orderIndex + 1 } }),
|
||||
),
|
||||
this.el("span", "chatpapers-lecture-beat-preview", preview),
|
||||
);
|
||||
|
||||
const status = this.el("span", "chatpapers-lecture-beat-status");
|
||||
if (beat.ttsStatus === "ready") {
|
||||
status.textContent = "✓";
|
||||
} else if (beat.ttsStatus === "failed") {
|
||||
status.textContent = getString("lecture-beat-tts-failed");
|
||||
} else {
|
||||
status.textContent = getString("lecture-beat-tts-pending");
|
||||
}
|
||||
|
||||
main.append(line, status);
|
||||
main.title = preview;
|
||||
main.addEventListener("click", () => {
|
||||
this.selectBeat(beats, beat.id);
|
||||
});
|
||||
|
||||
row.append(main, this.makeBeatPlayButton(beat));
|
||||
return row;
|
||||
}
|
||||
|
||||
private renderBeatDetail(beats: StoredBeat[], beatId?: string): void {
|
||||
const beat = beatId ? beats.find((b) => b.id === beatId) : undefined;
|
||||
this.paragraphDetailEl.replaceChildren();
|
||||
if (!beat) {
|
||||
this.paragraphDetailEl.hidden = false;
|
||||
this.paragraphDetailEl.classList.add("is-empty");
|
||||
this.paragraphDetailEl.classList.remove(
|
||||
"chatpapers-lecture-scroll-y",
|
||||
"chatpapers-lecture-beat-detail",
|
||||
);
|
||||
this.paragraphDetailEl.append(
|
||||
this.el(
|
||||
"div",
|
||||
"chatpapers-lecture-para-detail-empty",
|
||||
getString("lecture-beat-detail-empty"),
|
||||
),
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
this.paragraphDetailEl.hidden = false;
|
||||
this.paragraphDetailEl.classList.remove("is-empty");
|
||||
this.paragraphDetailEl.classList.add(
|
||||
"chatpapers-lecture-scroll-y",
|
||||
"chatpapers-lecture-beat-detail",
|
||||
);
|
||||
|
||||
const playBtn = this.doc.createElement("button");
|
||||
playBtn.type = "button";
|
||||
playBtn.className =
|
||||
"chatpapers-lecture-secondary chatpapers-lecture-para-play-btn";
|
||||
playBtn.append(
|
||||
createLucideIcon(this.doc, Play, {
|
||||
size: 14,
|
||||
className: "chatpapers-lecture-btn-icon",
|
||||
}),
|
||||
this.doc.createTextNode(getString("lecture-para-play")),
|
||||
);
|
||||
playBtn.disabled = beat.ttsStatus !== "ready";
|
||||
playBtn.addEventListener("click", () => {
|
||||
this.playBeat(beat);
|
||||
});
|
||||
this.paragraphDetailEl.append(playBtn);
|
||||
|
||||
const typeLabel = BEAT_TYPE_LABELS[beat.type] || beat.type;
|
||||
const title = beat.title?.trim();
|
||||
this.paragraphDetailEl.append(
|
||||
this.el(
|
||||
"div",
|
||||
"chatpapers-lecture-beat-detail-meta",
|
||||
title ? `${typeLabel} · ${title}` : typeLabel,
|
||||
),
|
||||
this.el("div", "chatpapers-lecture-beat-script", beat.script),
|
||||
);
|
||||
}
|
||||
|
||||
private async statusMessage(
|
||||
|
||||
@@ -88,14 +88,27 @@ export function registerPrefs() {
|
||||
});
|
||||
}
|
||||
|
||||
export function ensureChatPapersStyles(doc: Document) {
|
||||
export function ensureChatPapersStyles(doc: Document): void {
|
||||
const id = "chatpapers-styles";
|
||||
if (doc.getElementById(id)) return;
|
||||
const base = `chrome://${config.addonRef}/content/chatpapers.css`;
|
||||
const href =
|
||||
typeof __env__ !== "undefined" && __env__ === "development"
|
||||
? `${base}?v=${Date.now()}`
|
||||
: base;
|
||||
|
||||
const existing = doc.getElementById(id) as HTMLLinkElement | null;
|
||||
if (existing) {
|
||||
if (typeof __env__ !== "undefined" && __env__ === "development") {
|
||||
existing.href = href;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
const link = doc.createElement("link");
|
||||
link.id = id;
|
||||
link.rel = "stylesheet";
|
||||
link.type = "text/css";
|
||||
link.href = `chrome://${config.addonRef}/content/chatpapers.css`;
|
||||
link.href = href;
|
||||
doc.documentElement?.appendChild(link);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user