Carry underlines to the slides; keep Bible names in the typo check (v1.3.1)

- Underlined words in the notes are underlined on every slide type, as the
  operator does by hand (Part 8: "saved", "saves"); segments are now
  (text, bold, italic, underline); TextEdit edit format uses __word__
- Typo check never changes capitalised words inside scripture, or names
  the notes' scripture uses ("Gilead was the father..." became "Gilled")
- Tests: Part 8 + Together Tuesday notes, hand-fixed Part 8 deck

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-19 12:11:30 -04:00
co-authored by Claude Opus 5.5
parent d41c3d33ef
commit f2306f781e
9 changed files with 606 additions and 60 deletions
+1 -1
View File
@@ -33,7 +33,7 @@ import spelling
import themes
import youtube
VERSION = "1.3.0"
VERSION = "1.3.1"
API = None # ProPresenter's network API on this Mac - set in run() from its preferences
SUPPORT = os.path.expanduser("~/Library/Application Support/MessageNotes")
LOG = os.path.expanduser("~/Library/Logs/message-notes.log")
+23 -21
View File
@@ -264,21 +264,23 @@ def scaled(state, scale):
return s
def with_emphasis(state, bold, italic):
def with_emphasis(state, bold, italic, underline=False):
s = dict(state)
if bold:
s["b"] = None
if italic:
s["i"] = None
if underline:
s["ul"] = None
return s
def make_rtf(style, paras, scale=1.0):
"""paras: [(template paragraph index, [(text, bold, italic), ...]), ...]"""
"""paras: [(template paragraph index, [(text, bold, italic, underline), ...]), ...]"""
out = []
for k, (pi, segs) in enumerate(paras):
codes, first, end, _ = style.para(pi)
runs = [(t, scaled(with_emphasis(first, b, i), scale)) for t, b, i in segs if t]
runs = [(t, scaled(with_emphasis(first, b, i, u), scale)) for t, b, i, u in segs if t]
if not runs:
runs = [("", scaled(first, scale))]
out.append((codes, runs))
@@ -316,14 +318,14 @@ def slide_texts(tpl, s):
if s.kind == "sentence":
st = tpl.style("sentence", "sermoninasentence")
label = st.paras[0][3].strip() if st and len(st.paras) > 1 else ""
out = [(0, [(label, False, False)])] if label else []
out = [(0, [(label, False, False, False)])] if label else []
return {"sermoninasentence": out + [(-1, ln.segs_plain()) for ln in L]}
if s.kind == "scripture":
# the template's sample reference ends with a space ("Exodus 8:1 ESV "); keep that habit
ref_st = tpl.style("scripture", "reference")
tail = " " if ref_st and ref_st.paras[0][3].endswith(" ") and not s.ref.endswith(" ") else ""
return {"reference": [(0, [(s.ref + tail, False, False)])],
"scripture": [(0, ln.segs) for ln in L] or [(0, [("", False, False)])]}
return {"reference": [(0, [(s.ref + tail, False, False, False)])],
"scripture": [(0, ln.segs) for ln in L] or [(0, [("", False, False, False)])]}
return {}
@@ -370,29 +372,29 @@ def _as_point(tpl, s):
def effective(st, paras):
"""Paragraph runs as they really look on the slide: [[(text, bold, italic)]]."""
"""Paragraph runs as they really look on the slide: [[(text, bold, italic, underline)]]."""
out = []
for pi, segs in paras:
_, first, _, _ = st.para(pi)
base_b = "b" in first
base_i = "i" in first
out.append([(t, base_b or b, base_i or i) for t, b, i in segs if t])
out.append([(t, base_b or b, base_i or i, u) for t, b, i, u in segs if t])
return out
def output_rtf(tpl, kind, name, st_out, paras):
"""The same text as a themed screen draws it: the theme's font and size, with only the
'special' words (styled differently from the rest of the box) keeping bold/italic."""
'special' words (styled differently from the rest of the box) keeping bold/italic; underlines stay."""
st_slide = tpl.style(kind, name)
eff = effective(st_slide, paras) if st_slide is not None else [[(t, b, i) for t, b, i in segs] for _, segs in paras]
eff = effective(st_slide, paras) if st_slide is not None else [list(segs) for _, segs in paras]
count = {}
for para in eff:
for t, b, i in para:
for t, b, i, u in para:
count[(b, i)] = count.get((b, i), 0) + len(t)
base = max(count, key=count.get) if count else (False, False)
out = []
for para in eff:
segs = [(t, b, i) if (b, i) != base else (t, False, False) for t, b, i in para]
segs = [(t, b, i, u) if (b, i) != base else (t, False, False, u) for t, b, i, u in para]
out.append((0, segs))
return make_rtf(st_out, out)
@@ -438,7 +440,7 @@ def measure_all(tpl, plan, outputs, measure):
def line_height(st, measure_cache, measure):
key = id(st)
if key not in measure_cache:
r = measure([{"rtf": make_rtf(st, [(0, [("Xg", False, False)])]), "w": st.w}])[0]
r = measure([{"rtf": make_rtf(st, [(0, [("Xg", False, False, False)])]), "w": st.w}])[0]
measure_cache[key] = max(1.0, r["h"])
return measure_cache[key]
@@ -516,12 +518,12 @@ _CLAUSE = re.compile(r"(?<=[,\u2014])\s*")
def split_scripture(tpl, s, measure, outputs=None):
"""One long scripture -> as few slides as possible that fit everywhere, cut at verse
numbers if possible (then sentences, then clauses), pieces as even as possible."""
flat = [] # (text, bold, italic) with "\n" between paragraphs
flat = [] # (text, bold, italic, underline) with "\n" between paragraphs
for k, ln in enumerate(s.lines):
if k:
flat.append(("\n", False, False))
flat.append(("\n", False, False, False))
flat.extend(ln.segs)
text = "".join(t for t, _, _ in flat)
text = "".join(seg[0] for seg in flat)
for level in (_VERSE_START, _SENT_END, _CLAUSE):
bounds = {0, len(text)} | {i + 1 for i, ch in enumerate(text) if ch == "\n"}
bounds |= {m.start() if level is _VERSE_START else m.end() for m in level.finditer(text)}
@@ -572,12 +574,12 @@ def split_scripture(tpl, s, measure, outputs=None):
def slice_segs(flat, a, b):
out, pos = [], 0
for t, bo, it in flat:
for t, bo, it, ul in flat:
x, y = pos, pos + len(t)
pos = y
lo, hi = max(a, x), min(b, y)
if lo < hi:
out.append((t[lo - x:hi - x], bo, it))
out.append((t[lo - x:hi - x], bo, it, ul))
while out and not out[0][0].strip():
out.pop(0)
while out and not out[-1][0].strip():
@@ -590,14 +592,14 @@ def slice_segs(flat, a, b):
def split_lines(segs):
lines, cur = [], []
for t, b, i in segs:
for t, b, i, u in segs:
parts = t.split("\n")
for k, part in enumerate(parts):
if k:
lines.append(cur)
cur = []
if part:
cur.append((part, b, i))
cur.append((part, b, i, u))
lines.append(cur)
return [l for l in lines if l]
@@ -860,7 +862,7 @@ def speaker_rtfs(tpl, role, preacher):
for nm, text in (("role", role), ("preacher", preacher)):
st = tpl.style("speaker", nm)
if st is not None and text:
out.append((nm, st, make_rtf(st, [(0, [(text, False, False)])])))
out.append((nm, st, make_rtf(st, [(0, [(text, False, False, False)])])))
return out
+70 -14
View File
@@ -76,11 +76,11 @@ QUOTE_OPEN = "\u201c\"\u2018'"
class Line:
"""One paragraph of slide text: segments of (text, bold, italic); lead = lead-in style."""
"""One paragraph of slide text: segments of (text, bold, italic, underline); lead = lead-in style."""
__slots__ = ("segs", "lead")
def __init__(self, segs, lead=False):
self.segs = [s for s in segs if s[0]]
self.segs = [tuple(s) + (False,) * (4 - len(s)) for s in segs if s[0]]
self.lead = lead
@property
@@ -88,7 +88,14 @@ class Line:
return "".join(s[0] for s in self.segs)
def segs_plain(self):
return [(self.text, False, False)]
"""The text in the box's own style (bold/italic come from the theme); underlines stay."""
out = []
for t, _, _, u in self.segs:
if out and out[-1][3] == u:
out[-1] = (out[-1][0] + t, False, False, u)
else:
out.append((t, False, False, u))
return out or [(self.text, False, False, False)]
def __repr__(self):
return "Line(%r%s)" % (self.text, ", lead" if self.lead else "")
@@ -181,9 +188,14 @@ class P:
col = doc.colors[ci] if ci < len(doc.colors) else None
if col in ((0, 0, 0), (255, 255, 255)):
col = None
self.runs.append((r.text, bold, ital, (r.state.get("fs", 24) or 24) / 2.0, col))
ul = "ul" in r.state
self.runs.append((r.text, bold, ital, (r.state.get("fs", 24) or 24) / 2.0, col, ul))
self.raw = "".join(r[0] for r in self.runs)
self.text = clean(self.raw)
# which characters of self.text are underlined in the notes
mask = [u for r in self.runs for u in [r[5]] * len(r[0])]
t, m = clean_mask(self.raw, mask)
self.ul = m if (t == self.text and any(m)) else None
vis = [r for r in self.runs if r[0].strip()]
self.size = max([r[3] for r in vis] or [12])
self.bold = bool(vis) and all(r[1] for r in vis)
@@ -204,10 +216,51 @@ def clean(text):
return t.strip()
def clean_mask(raw, mask):
"""clean(), carrying a per-character flag along: (text, flags)."""
chars = []
for ch, f in zip(raw, mask):
ch = "\n" if ch in "\u2028\u2029" else " " if ch == "\u00a0" else ch
chars.extend([(".", f)] * 3 if ch == "\u2026" else [(ch, f)])
out = []
for ch, f in chars:
if ch == "\n":
while out and out[-1][0] in " \t":
out.pop()
out.append((ch, f))
while out and out[0][0].isspace():
out.pop(0)
while out and out[-1][0].isspace():
out.pop()
return "".join(c for c, _ in out), [f for _, f in out]
def underlined(text, p, part=None, offset=0):
"""Segments for text (taken from paragraph p's text, or from part of it starting at offset),
underlined where the notes underline it."""
if p is None or not p.ul:
return [(text, False, False, False)]
src = p.text if part is None else part
flags = p.ul[offset:offset + len(src)]
k, skip = src.find(text), 0
if k < 0: # "1) Faith" became "1. Faith": match the end of it
while skip < len(text) and src.find(text[skip:]) < 0:
skip += 1
k = src.find(text[skip:]) if skip < len(text) else 0
mask = [False] * skip + flags[k:k + len(text) - skip]
out = []
for ch, u in zip(text, mask + [False] * (len(text) - len(mask))):
if out and out[-1][3] == u:
out[-1][0] += ch
else:
out.append([ch, False, False, u])
return [tuple(s) for s in out]
def segments(runs, start_char=0, emphasis=True):
"""Runs -> [(text, bold, italic)] starting at character offset start_char."""
"""Runs -> [(text, bold, italic, underline)] starting at character offset start_char."""
out, pos = [], 0
for text, bold, ital, size, col in runs:
for text, bold, ital, size, col, ul in runs:
a, b = pos, pos + len(text)
pos = b
if b <= start_char:
@@ -215,7 +268,7 @@ def segments(runs, start_char=0, emphasis=True):
piece = text[max(0, start_char - a):]
if not emphasis:
bold = ital = False
out.append([piece, bold, ital])
out.append([piece, bold, ital, ul])
# verse numbers are never emphasis
for s in out:
if s[1] and re.fullmatch(r"\s*\d{1,3}\s*", s[0]):
@@ -227,7 +280,7 @@ def segments(runs, start_char=0, emphasis=True):
merged = []
for s in out:
s[0] = s[0].replace("\u2026", "...").replace("\u2028", " ").replace("\u00a0", " ")
if merged and merged[-1][1] == s[1] and merged[-1][2] == s[2]:
if merged and merged[-1][1:] == s[1:]:
merged[-1][0] += s[0]
else:
merged.append(s)
@@ -369,7 +422,7 @@ def parse(data):
rest = p.text[m.end():].strip()
lines = []
if rest:
lines.append(Line([(rest, False, False)]))
lines.append(Line(underlined(rest, p)))
i += 1
if not lines:
# following paragraphs up to the next blank line
@@ -377,7 +430,8 @@ def parse(data):
i += 1
while i < n and not body[i].blank and not parse_ref(body[i].text):
if not is_annotation(body[i]):
lines.append(Line([(body[i].text.replace("\n", " "), False, False)]))
q = body[i]
lines.append(Line(underlined(q.text.replace("\n", " "), q, q.text.replace("\n", " "))))
i += 1
s = Slide("sentence", lines, src=p.idx)
if not lines:
@@ -479,11 +533,13 @@ def statements_to_slides(group, force_build=False):
"""Consecutive statement paragraphs -> Point / Build slides."""
lines = []
for p in group:
pos = 0
for part in p.text.split("\n"):
start, pos = pos, pos + len(part) + 1
if not part.strip():
continue
text, mark = strip_list_mark(part)
lines.append((text, p, mark))
lines.append((text, p, mark, part, start))
if not lines:
return []
src = group[0].idx
@@ -506,8 +562,8 @@ def statements_to_slides(group, force_build=False):
else:
kind = "build"
out_lines = []
for k, (text, p, mark) in enumerate(lines):
out_lines.append(Line([(text, False, False)], lead=(k == 0 and lead and kind == "point")))
for k, (text, p, mark, part, start) in enumerate(lines):
out_lines.append(Line(underlined(text, p, part, start), lead=(k == 0 and lead and kind == "point")))
s = Slide(kind, out_lines, src=src)
plain = [p for p in group if not p.bold and not p.italic]
if plain and all(p.size <= 15 for p in plain):
@@ -533,5 +589,5 @@ def quote_slide(lines, src):
attribution = [l for l in lines[1:] if re.match(r"^\s*[-\u2013\u2014~]\s*\S", l[0])]
if not (first[1].italic or attribution):
return None
out = [Line([(l[0], False, False)]) for l in lines]
out = [Line(underlined(l[0], l[1], l[3], l[4])) for l in lines]
return Slide("quote", out, src=src)
+30 -17
View File
@@ -12,6 +12,7 @@ HEADER = """# Slide list for: {name}
# The lines under it are the slide's lines (one line = one paragraph; in a Build, one step).
# In a Point, start a line with > to make it the italic lead-in line.
# In a Scripture, put **double asterisks** around bold words and *single ones* around italic words.
# On any slide, __double underscores__ around words underline them.
# Delete a slide by deleting its whole block. Blank lines and lines starting with "# " are ignored.
# Lines starting with #! are notes that were left off the slides (shown for reference).
"""
@@ -44,38 +45,48 @@ def dump(plan, name=""):
if s.kind == "scripture":
out.append("[Scripture] %s" % s.ref)
for ln in s.lines:
out.append("".join(mark(t, b, i) for t, b, i in ln.segs))
out.append("".join(mark(t, b, i, u) for t, b, i, u in ln.segs))
else:
out.append("[%s]" % NAMES.get(s.kind, "Point"))
for ln in s.lines:
out.append(("> " if (ln.lead and s.kind == "point") else "") + ln.text)
text = "".join(mark(t, False, False, u) for t, _, _, u in ln.segs)
out.append(("> " if (ln.lead and s.kind == "point") else "") + text)
for item in left.get(len(plan.slides), []):
out.append("#! %s" % item[1].replace("\n", " "))
return "\n".join(out) + "\n"
def mark(t, b, i):
def mark(t, b, i, u=False):
m = "***" if (b and i) else "**" if b else "*" if i else ""
if not m or not t.strip():
if not (m or u) or not t.strip():
return t
lead, core, trail = re.match(r"^(\s*)(.*?)(\s*)$", t, re.S).groups()
return "%s%s%s%s%s" % (lead, m, core, m, trail)
core = "%s%s%s" % (m, core, m)
return "%s%s%s" % (lead, "__%s__" % core if u else core, trail)
def parse_bold(text):
segs, pos = [], 0
for m in re.finditer(r"\*\*\*(.+?)\*\*\*|\*\*(.+?)\*\*|\*(.+?)\*", text):
def parse_bold(text, styles=True):
"""'a **b** __c__' -> [(text, bold, italic, underline)]; styles=False keeps only underline."""
segs = []
# "__" + text that doesn't start or end with _ or a space + "__" (so "______" blanks stay)
for k, piece in enumerate(re.split(r"__([^_\s](?:.*?[^_\s])??)__", text)):
u = k % 2 == 1
if not styles:
segs.append((piece.replace("**", ""), False, False, u))
continue
pos = 0
for m in re.finditer(r"\*\*\*(.+?)\*\*\*|\*\*(.+?)\*\*|\*(.+?)\*", piece):
if m.start() > pos:
segs.append((text[pos:m.start()], False, False))
segs.append((piece[pos:m.start()], False, False, u))
if m.group(1):
segs.append((m.group(1), True, True))
segs.append((m.group(1), True, True, u))
elif m.group(2):
segs.append((m.group(2), True, False))
segs.append((m.group(2), True, False, u))
else:
segs.append((m.group(3), False, True))
segs.append((m.group(3), False, True, u))
pos = m.end()
if pos < len(text):
segs.append((text[pos:], False, False))
if pos < len(piece):
segs.append((piece[pos:], False, False, u))
return [s for s in segs if s[0]]
@@ -105,7 +116,9 @@ def load(text):
"e.g. [Scripture] John 3:16 ESV" % no)
cur.ref = ref
elif m.group(2).strip():
cur.lines.append(notesmod.Line([(m.group(2).strip(), False, False)]))
t = m.group(2).strip()
cur.lines.append(notesmod.Line([(t, False, False)] if kind in ("picture", "clearpicture")
else parse_bold(t, styles=False)))
continue
if cur is None:
raise ParseError("line %d: text before the first [Slide type] line: %r" % (no, line.strip()[:60]))
@@ -113,9 +126,9 @@ def load(text):
if cur.kind == "scripture":
cur.lines.append(notesmod.Line(parse_bold(t)))
elif cur.kind == "point" and t.startswith(">"):
cur.lines.append(notesmod.Line([(t[1:].strip(), False, False)], lead=True))
cur.lines.append(notesmod.Line(parse_bold(t[1:].strip(), styles=False), lead=True))
else:
cur.lines.append(notesmod.Line([(t.replace("**", ""), False, False)]))
cur.lines.append(notesmod.Line(parse_bold(t, styles=False)))
if cur is not None:
plan.slides.append(cur)
titles = [s for s in plan.slides if s.kind == "title"]
+25 -1
View File
@@ -2,7 +2,8 @@
A word is fixed only when macOS has a confident correction for it (the same one it would
auto-correct while typing). Names it doesn't know (Rahab, Chniouli) have none, so they are
left alone. ALL-CAPS emphasis words are checked too (macOS normally skips those), and
left alone, and so are capitalised words inside scripture and names the notes' scripture
uses (Gilead). ALL-CAPS emphasis words are checked too (macOS normally skips those), and
doubled small words ("in in") are fixed. Every fix is reported. To undo one, the word goes
into allowed-words.txt (never touched again) and the check is simply run again on the
text from before the fixes.
@@ -150,6 +151,24 @@ def _starts_sentence(text, loc):
return not before or before[-1] in ".!?:\u201c\"(" or before.endswith("\u2014")
_WORD = re.compile(r"[A-Za-z\u00c0-\u024f]+(?:['\u2019][A-Za-z]+)?")
def names_in(plan):
"""Words the notes themselves use as names (lowercase): capitalised inside a scripture
passage (Gilead, Jephthah), or capitalised in the middle of a sentence anywhere."""
out = set()
for s, _, _, text in _segments(plan):
for m in _WORD.finditer(text):
w = m.group(0)
if not w[:1].isupper() or w.isupper():
continue
if s.kind == "scripture" or not _starts_sentence(text, m.start()):
out.add(w.lower())
out.add(re.split(r"['\u2019]", w)[0].lower()) # Gilead's -> gilead
return out
def check_and_fix(plan, skip=None):
"""Fix confident typos in the plan's slide text. Returns a Report.
skip: words (lowercase) never to change, on top of allowed-words.txt."""
@@ -159,6 +178,7 @@ def check_and_fix(plan, skip=None):
return rep
results = _run_jxa([t for _, _, _, t in segs])
ok = allowed_words() | set(skip or ()) | SERMON_WORDS
names = names_in(plan)
for (s, li, si, text), found in zip(segs, results):
edits = [] # (start, end, replacement, word)
for f in found:
@@ -166,6 +186,10 @@ def check_and_fix(plan, skip=None):
word = text[loc:loc + ln]
if not word or word.lower() in ok or any(ch.isdigit() for ch in word):
continue
# Bible text is pasted, and its capitalised words are names and places ("Gilead was
# the father of Jephthah" starts a sentence); a name the notes use is never a typo
if word[:1].isupper() and (s.kind == "scripture" or word.lower() in names):
continue
corr = f["corr"]
name_like = word[:1].isupper() and not word.isupper() and not _starts_sentence(text, loc)
# "lampstand" -> "lamp stand", "firstfruits" -> "first fruits": Bible words, not typos
@@ -0,0 +1,204 @@
{\rtf1\ansi\ansicpg1252\cocoartf2870
\cocoatextscaling0\cocoaplatform0{\fonttbl\f0\fswiss\fcharset0 Helvetica-Bold;\f1\fswiss\fcharset0 Helvetica-BoldOblique;\f2\fswiss\fcharset0 Helvetica;
\f3\fnil\fcharset0 Verdana-Bold;\f4\fnil\fcharset0 Verdana;}
{\colortbl;\red255\green255\blue255;\red0\green0\blue0;\red0\green0\blue241;\red0\green23\blue255;
\red0\green0\blue255;}
{\*\expandedcolortbl;;\cssrgb\c0\c0\c0;\cspthree\c0\c4769\c91973;\cssrgb\c1229\c23499\c100000;
\cssrgb\c1680\c19835\c100000;}
\vieww9260\viewh20100\viewkind0
\deftab720
\pard\pardeftab720\partightenfactor0
\f0\b\fs54 \cf2 Together Tuesday - October 2026\
\pard\pardeftab720\partightenfactor0
\f1\i\fs32 \cf2 A Vision for His Mission\
\pard\pardeftab720\partightenfactor0
\f0\i0\fs28 \cf3 Proverbs 29:18\
\
\
\cf2 Two key ideas:\cf3 \
\pard\pardeftab720\partightenfactor0
\fs34 \cf2 1. God's Word gives me VISION.
\f2\b0 \uc0\u8232
\f0\b 2. VISION changes how I LIVE.\
\
\
\
\pard\pardeftab720\partightenfactor0
\fs36 \cf2 1. I must SEE what God is DOING.\
\pard\pardeftab720\partightenfactor0
\f3\fs28 \cf4 Proverbs 29:18 MSG
\f4\b0 If people can\'92t see what God is doing, they stumble all over themselves; But when they attend to what he reveals, they are most blessed.\
\
\
\
\
\f3\b Proverbs 29:18 ESV
\f4\b0 Where there is no
\f3\b prophetic vision
\f4\b0 the people cast off restraint\'85\
\
\
\
\f3\b 1 Samuel 3:1 ESV
\f4\b0 Now the boy Samuel was ministering to the LORD in the presence of Eli. And the word of the LORD was
\f3\b rare in those days
\f4\b0 ; there was no
\f3\b frequent vision.
\f4\b0 \cf2 \
\
\
\
\f3\b \cf4 Exodus 32:25 ESV
\f4\b0 \'93And when Moses saw that the people
\f3\b had broken loose
\f4\b0 (for Aaron had let them break loose\'85)\'94\
\
\
\
\f3\b 2 Kings 6:17 ESV
\f4\b0 \'93O LORD, please open his eyes that he may see.\'94\
\
\
\
\pard\pardeftab720\partightenfactor0
\f0\b\fs36 \cf2 2. I must PERCEIVE what God is SAYING.\
\pard\pardeftab720\partightenfactor0
\f3\fs28 \cf4 Proverbs 29:18b ESV
\f4\b0 but blessed is he who keeps
\f3\b the law.
\f4\b0 \cf2 \
\
\
\
\pard\pardeftab720\partightenfactor0
\f0\b\fs34 \cf2 A. I PERCEIVE through TEACHING in the Word.
\f4\b0\fs28 \
\pard\pardeftab560\partightenfactor0
\f3\b \cf5 Ephesians 4:11\'9614 ESV
\f4\b0 And he gave the apostles, the prophets, the evangelists, the shepherds and teachers.\
\
\
\
\pard\pardeftab720\partightenfactor0
\f0\b\fs34 \cf2 B. I PERCEIVE through PROPHETIC gifts in the Church.\
\pard\pardeftab720\partightenfactor0
\f3\fs28 \cf4 1 Corinthians 14:1 ESV
\f4\b0 Pursue love, and earnestly desire the spiritual gifts, especially that you may
\f3\b prophesy
\f4\b0 .\
\f3\b 1 Corinthians 14:3 ESV
\f4\b0 the one who prophesies speaks to people for their
\f3\b upbuilding
\f4\b0 and
\f3\b encouragement
\f4\b0 and
\f3\b consolation
\f4\b0 .\cf2 \
\
\
\
\pard\pardeftab720\partightenfactor0
\f0\b\fs34 \cf2 \ul \ulc2 Prophesy,\ulnone always based on God\'92s Word, brings IMPARTATION to a specific PERSON in a specific SITUATION for a specific REASON.\
\
\
\pard\pardeftab720\partightenfactor0
\f3\fs28 \cf4 1 Corinthians 14:24\'9625 ESV
\f4\b0 But if all prophesy, and an unbeliever or outsider enters, he is convicted by all, he is called to account by all, 25 the secrets of his heart are disclosed, and so, falling on his face, he will worship God and declare that God is really among you.\
\pard\pardeftab720\partightenfactor0
\f0\b\fs34 \cf2 \
\
\pard\pardeftab720\partightenfactor0
\f3\fs28 \cf4 1 Thessalonians 5:20\'9621 ESV
\f4\b0 Do not despise prophecies, 21 but test everything; hold fast what is good.\cf2 \
\
\
\
\
\pard\pardeftab720\partightenfactor0
\f0\b\fs36 \cf2 3. I must be REFORMED by what God REVEALS.\
\pard\pardeftab720\partightenfactor0
\f3\fs28 \cf4 Proverbs 29:18b MSG
\f4\b0 But when they
\f3\b attend to what He reveals
\f4\b0 , they are most blessed.
\f3\b \
\f4\b0 \
\
\
\f3\b Romans 8:29 MSG
\f4\b0 God knew what he was doing from the very beginning. He decided from the outset to shape the lives of those who love him along the same lines as the life of his Son. The Son stands first in the line of humanity he restored.
\f3\b We see the original and intended shape of our lives there in him.
\f4\b0 \
\
\
\
\
\pard\pardeftab720\partightenfactor0
\f0\b\fs34 \cf2 What do we do with what God reveals?\
\
A. PRAY it through.
\f2\b0 \
\pard\pardeftab720\partightenfactor0
\f3\b\fs28 \cf4 Psalm 119:18 ESV
\f4\b0 Open my eyes, that I may behold wondrous things out of your law.\cf2 \
\f3\b \cf4 Psalm 139:23\'9624 ESV
\f4\b0 Search me, O God, and know my heart! Try me and know my thoughts! 24 And see if there be any grievous way in me, and lead me in the way everlasting!\
\
\
\
\pard\pardeftab720\partightenfactor0
\f0\b\fs34 \cf2 B. READ it out.
\f2\b0 \
\pard\pardeftab720\partightenfactor0
\f3\b\fs28 \cf4 Joshua 1:8 ESV
\f4\b0 This Book of the Law shall not depart from your
\f3\b mouth
\f4\b0 , but you shall meditate on it day and night, so that you may be careful to do according to all that is written in it. For then you will make your way prosperous, and then you will have good success.\cf2 \
\
\
\
\pard\pardeftab720\partightenfactor0
\f0\b\fs34 \cf2 C. WRITE it down.
\f2\b0 \
\pard\pardeftab720\partightenfactor0
\f3\b\fs28 \cf4 Habakkuk 2:2\'963 ESV
\f4\b0 And the LORD answered me: \'93Write the vision; make it plain on tablets, so he may run who reads it. 3 For still the vision awaits its appointed time; it hastens to the end\'97it will not lie. If it seems slow, wait for it; it will surely come; it will not delay.\'94\
\
\
\pard\pardeftab720\partightenfactor0
\f0\b\fs34 \cf2 D. WALK it out.
\f2\b0 \
}
@@ -0,0 +1,215 @@
{\rtf1\ansi\ansicpg1252\cocoartf2870
\cocoatextscaling0\cocoaplatform0{\fonttbl\f0\fswiss\fcharset0 Helvetica-Bold;\f1\fswiss\fcharset0 Helvetica-BoldOblique;\f2\fnil\fcharset0 Verdana;
\f3\fnil\fcharset0 Verdana-Bold;\f4\fswiss\fcharset0 Helvetica-Oblique;\f5\fswiss\fcharset0 Helvetica;
\f6\fnil\fcharset0 Verdana-Italic;}
{\colortbl;\red255\green255\blue255;\red0\green0\blue0;\red0\green0\blue255;\red251\green2\blue255;
\red253\green128\blue8;\red128\green0\blue2;\red0\green23\blue255;\red128\green0\blue23;}
{\*\expandedcolortbl;;\cssrgb\c0\c0\c0;\cssrgb\c0\c12614\c100000;\cssrgb\c100000\c25279\c100000;
\cssrgb\c100000\c57811\c0;\cssrgb\c58072\c6673\c0;\cssrgb\c1229\c23499\c100000;\cssrgb\c58074\c7053\c11604;}
\margl1440\margr1440\vieww13140\viewh12560\viewkind0
\deftab720
\pard\pardeftab720\partightenfactor0
\f0\b\fs54 \cf2 Where Is Your Faith - Part 8\
\pard\pardeftab720\partightenfactor0
\f1\i\fs32 \cf2 Faith to Fight\
\pard\pardeftab720\partightenfactor0
\f0\i0\fs28 \cf3 Hebrews 11:32-34\
\
\
\f1\i \cf4 Tesla stunt video on screen behind me.
\f0\i0 \cf3 \
\pard\pardeftab720\sl280\partightenfactor0
\f2\b0 \cf5 <Waters Creative/What's Next/SERIES/2608_Where is Your Faith>\cf6 \
\
\
\
\
\pard\pardeftab720\partightenfactor0
\f3\b \cf7 Hebrews 11:32 ESV
\f2\b0 And what more shall I say? For time would fail me to tell of Gideon, Barak, Samson, Jephthah, of David and Samuel and the prophets\'97\
\
\
\
\pard\pardeftab720\partightenfactor0
\f0\b\fs32 \cf2 Faith isn't passive DEPENDENCE on God; it's ACTIVE PARTICIPATION in what God calls us to do.\
\
\
\
\pard\pardeftab720\partightenfactor0
\f3\fs28 \cf7 Hebrews 10:38\'9639 ESV
\f2\b0 but my righteous one shall live by faith, and if he shrinks back, my soul has no pleasure in him.\'94 39 But
\f3\b we are not of those who shrink back
\f2\b0 and are destroyed, but of
\f3\b those who have faith and preserve their souls.
\f2\b0 \
\
\
\
\pard\pardeftab720\partightenfactor0
\f0\b\fs36 \cf2 1. Faith FIGHTS despite our FLAWS.\
\pard\pardeftab720\partightenfactor0
\f3\fs28 \cf7 Hebrews 11:32\'9633 ESV
\f2\b0 And what more shall I say? For time would fail me to tell of Gideon, Barak, Samson, Jephthah, of David and Samuel and the prophets\'97 33 who through faith\'85\
\
\
\pard\pardeftab720\partightenfactor0
\f4\i \cf4 <Come up one at a time:
\f5\i0 >
\f2 \cf7 \
\pard\pardeftab720\partightenfactor0
\f0\b\fs32 \cf2 What is your FLAW?
\f5\b0 \
\f0\b 1. FEARFUL INSECURITY - Gideon and Barak\
2. FLESHLY IMPULSES - David and Samson\
3. FAMILY DYSFUNCTION - David and Jephthah
\f5\b0 \
\f0\b \
\
\pard\pardeftab720\partightenfactor0
\f3\fs28 \cf7 Galatians 5:17 ESV
\f2\b0 For the desires of the flesh are against the Spirit, and the desires of the Spirit are against the flesh,
\f3\b for these are opposed to each other
\f2\b0 , to keep you from doing the things you want to do.\
\pard\pardeftab720\partightenfactor0
\f0\b\fs32 \cf2 \
\
\pard\pardeftab720\partightenfactor0
\f1\i\fs26 \cf4 (Slides only)\
\pard\pardeftab720\partightenfactor0
\f3\i0\fs28 \cf7 Judges 11:1\'963 ESV
\f2\b0 Now Jephthah the Gileadite was a mighty warrior, but he was the son of a prostitute. Gilead was the father of Jephthah. 2 And Gilead\'92s wife also bore him sons. And when his wife\'92s sons grew up, they drove Jephthah out and said to him, \'93You shall not have an inheritance in our father\'92s house, for you are the son of another woman.\'94 3 Then Jephthah fled from his brothers and lived in the land of Tob, and worthless fellows collected around Jephthah and went out with him.\
\pard\pardeftab720\partightenfactor0
\f0\b\fs32 \cf2 \
\
\
\
\pard\pardeftab720\partightenfactor0
\f3\fs28 \cf7 Psalm 139:2\'964 ESV
\f2\b0 You know when I sit down and when I rise up;
\f3\b you discern my thoughts from afar.
\f2\b0 3 You search out my path and my lying down and are acquainted with all my ways. 4 Even
\f3\b before a word is on my tongue
\f2\b0 , behold, O LORD,
\f3\b you know it altogether.\
\pard\pardeftab720\partightenfactor0
\f0\fs32 \cf2 \
\
\
\
\pard\pardeftab720\partightenfactor0
\fs36 \cf2 2. Faith FIGHTS despite the ODDS.\
\pard\pardeftab720\partightenfactor0
\f3\fs28 \cf7 Hebrews 11:33 ESV
\f2\b0 who through faith conquered kingdoms, enforced justice, obtained promises, stopped the mouths of lions,\
\
\
\
\
\f3\b Judges 7:2 ESV
\f2\b0 The LORD said to Gideon, \'93The people with you are too many for me to give the Midianites into their hand, lest Israel boast over me, saying, \'91My own hand has
\f3\b \cf7 \ul \ulc7 saved
\f2\b0 \ulnone me.\'92\
\
\
\pard\pardeftab720\partightenfactor0
\f1\i\b\fs26 \cf4 (Slides only)\
\pard\pardeftab720\partightenfactor0
\f3\i0\fs28 \cf7 1 Samuel 17:24 ESV
\f2\b0 All the men of Israel, when they saw the man, fled from him and were much afraid.\
\
\
\
\f3\b 1 Samuel 17:45\'9647 ESV
\f2\b0 Then David said to the Philistine, \'93You come to me with a sword and with a spear and with a javelin,
\f3\b but I come to you in the name of the LORD of hosts,
\f2\b0 \'85 47 and
\f3\b that all this assembly may know that the LORD \ul saves\ulnone not with sword and spear.
\f2\b0 For the battle is the LORD\'92s, and he will give you into our hand.\'94\
\
\
\
\f3\b 2 Corinthians 10:3\'965 ESV
\f2\b0 For though we walk in the flesh, we are not waging war according to the flesh. 4 For the weapons of our warfare are not of the flesh
\f3\b but have divine power
\f2\b0 to destroy strongholds. 5 We destroy arguments and every lofty opinion raised against the knowledge of God, and take every thought captive to obey Christ,\
\
\
\pard\pardeftab720\partightenfactor0
\f4\i \cf4 <Come up one at a time:
\f5\i0 >
\f2 \cf7 \
\pard\pardeftab720\partightenfactor0
\f0\b\fs32 \cf2 Our weapons have DIVINE power. \
Weapon #1: The NAME OF JESUS - Luke 10:17
\f5\b0 \
\f0\b Weapon #2: The WORD OF GOD - Ephesians 6:17/Hebrews 4:12\
Weapon #3: The FAMILY OF FAITH - Philippians 1:27
\f5\b0 \
\
\
\
\
\pard\pardeftab720\partightenfactor0
\f0\b\fs36 \cf2 3. Faith FIGHTS with STRENGTH God SUPPLIES.
\f5\b0 \
\pard\pardeftab720\partightenfactor0
\f3\b\fs28 \cf7 Hebrews 11:34 ESV
\f2\b0 were
\f3\b made strong out of weakness,
\f2\b0 became mighty in war, put foreign armies to flight.\cf2 \
\
\
\
\f3\b \cf7 Ephesians 6:10 ESV
\f2\b0 Finally, be strong in the Lord and in the strength of his might.\
\
\
\
\pard\pardeftab720\partightenfactor0
\cf4 On my TV and available by TAP:\
\pard\pardeftab720\sl360\partightenfactor0
\f6\i \cf8 Prayer: Father, I believe in spite of my flaws and failures that you are working in me right now to overcome the world, the flesh and the devil. I depend on your strength and not my own, your grace and not my works. Empower your weakened servant with DIVINE strength to fight in Jesus\'92 Name.\
\
\
\
\pard\pardeftab720\sl280\partightenfactor0
\f3\i0\b \cf2 Sermon in a sentence:\
\pard\pardeftab720\partightenfactor0
\f0\fs32 \cf2 Faith is not passive DEPENDENCE; it\'92s active ENGAGEMENT in the BATTLES of life.}
Binary file not shown.
+33 -1
View File
@@ -112,7 +112,39 @@ def main():
except spelling.Unavailable as e:
print("skip typo checks (%s)" % e)
# 7. other campuses: their template names, and who preaches there according to the email
# 7. underlined words in the notes are underlined on the slides (as the operator does by hand)
def underlined_runs(data):
p = deck.C.decode(data, "rv.data.Presentation")
out = []
for c in deck.C.all(p, "cues"):
a = deck.slide_action(c)
for el in (deck.slide_elements(a) if a is not None else []):
t = deck.element_text(el)
d = deck.C.get(t, "rtf_data") if t is not None else None
for para in (rtf.read(d).paras if d else []):
out += [r.text.strip() for r in para.runs if "ul" in r.state and r.text.strip()]
return sorted(out)
p8 = notes.parse(open(os.path.join(NOTES, "Slides - Where is Your Faith - Part 8.rtf"), "rb").read())
got = underlined_runs(deck.build(tpl, p8, "TEST", tpl.speaker()))
hand = underlined_runs(open(os.path.join(PRO, "261018 Where Is Your Faith Part 8.pro"), "rb").read())
check("part 8 underlines match the hand-fixed deck", got == hand == ["saved", "saves"], "%s vs %s" % (got, hand))
p7l = notes.parse(open(os.path.join(NOTES, "Slides - 7 Letters - Part 2.rtf"), "rb").read())
got = underlined_runs(deck.build(tpl, p7l, "TEST", tpl.speaker()))
hand = underlined_runs(open(os.path.join(PRO, "260628 The 7 Letters Part 2.pro"), "rb").read())
check("7 Letters 2 underlines match the hand-made deck", got == hand, "%s vs %s" % (got, hand))
tt = notes.parse(open(os.path.join(NOTES, "Slides - Together Tuesday - October 2026.rtf"), "rb").read())
pt = [s for s in tt.slides if s.kind == "point" and s.lines[0].text.startswith("Prophesy")]
check("underline kept in a point", pt and [x[0] for x in pt[0].lines[0].segs_plain() if x[3]] == ["Prophesy,"],
"%s" % (pt[0].lines[0].segs_plain() if pt else "no point"))
rt = plantext.load(plantext.dump(p8, "x"))
styled = lambda plan: [[(ch,) + tuple(seg[1:]) for seg in l.segs for ch in seg[0] if not ch.isspace()]
for s in plan.slides for l in s.lines] # spaces at bold edges may move
check("underline survives the TextEdit round trip", styled(p8) == styled(rt))
check("fill-in blanks are not underline marks", plantext.parse_bold("Faith is ______.") ==
[("Faith is ______.", False, False, False)])
# 8. other campuses: their template names, and who preaches there according to the email
import app
import settings
for n, want in (("_TEMPLATE - DO NOT DELETE 2608 Lower Thirds.pro", True),