diff --git a/composer.py b/composer.py

index 07dbf23..27d5caa 100644

--- a/composer.py

+++ b/composer.py

@@ -35,6 +35,7 @@ def edit_segment(session):

                'Move to which § number? (Other actions: "X" to remove; "." to insert text; ":" to insert long text; "S" to split text; "M" to merge text; "/" to insert a link; "P" to insert poll)'



 post = db.get_post(segment.post)

+ title = None



 if not session.is_editable(post):

     return 61, 'Cannot edit posts by other users'

@@ -47,6 +48,10 @@ def edit_segment(session):

             if req.content_mime and not req.content_mime.startswith('text/'):

                 return 50, 'Bad content format (must be text)'

             seg_text = req.content.decode('utf-8')

+ if db.is_first_segment_of_post(post, segment):

+ title, seg_text = extract_title_heading(session, seg_text)

+ if title is not None:

+ db.update_post(post, title=title)

             db.update_segment(segment, content=seg_text)

     elif segment.type == Segment.LINK:

         if session.is_gemini:

@@ -75,6 +80,10 @@ def edit_segment(session):

             seg_text = clean_text(req.content.decode('utf-8'))



         seg_text = seg_text.rstrip()

+ if db.is_first_segment_of_post(post, segment):

+ title, seg_text = extract_title_heading(session, seg_text)

+ if title is not None:

+ db.update_post(post, title=title)

         if user.flags & User.COMPOSER_SPLIT_FLAG:

             parts = split_paragraphs(seg_text)

         else:

@@ -220,12 +229,19 @@ def make_composer_page(session):

     if not is_empty_query(req) or (session.is_titan and len(req.content)):

         seg_text = clean_query(req) if session.is_gemini \

             else clean_text(req.content.decode('utf-8'))

+ # In the first text segment, always detect a heading and make it the post title.

+ if db.get_segment_count(post) == 0:

+ title, seg_text = extract_title_heading(session, seg_text)

+ else:

+ title = None

         if session.user.flags & User.COMPOSER_SPLIT_FLAG:

             parts = split_paragraphs(seg_text)

         else:

             parts = [seg_text.rstrip()]

         for part in parts:

             db.create_segment(post, Segment.TEXT, content=part)

+ if title is not None:

+ db.update_post(post, title=title)

         return 30, gemini_link

     return 10, 'Add text segment:'



diff --git a/feeds.py b/feeds.py

index 9055aa7..76e4d3c 100644

--- a/feeds.py

+++ b/feeds.py

@@ -172,15 +172,7 @@ def make_post_page_or_configure_feed(session):

             title = ''



     if not url:

- found = re.match(r'^\s*#\s*(.+)$', lines[0])

- if found:

- title = found[1]

- body = '\n'.join(lines[1:]).strip()

- elif session.is_context_tracker:

- title = lines[0]

- body = '\n'.join(lines[1:]).strip()

- else:

- title = ''

+ title, body = extract_title_heading(session, body)



     post_id = db.create_post(session.user, session.context.id, title=title)

     post = db.get_post(post_id, draft=True)

diff --git a/model.py b/model.py

index cf3802a..e710cdb 100644

--- a/model.py

+++ b/model.py

@@ -505,7 +505,7 @@ class Post:



 SORT_CREATED, SORT_ACTIVE, SORT_HOTNESS = range(3)



- def init(self, id, subspace, parent, user, issueid, title, flags, is_draft, is_pinned,

+ def init(self, id, subspace, parent, user, issueid, title, flags, is_draft, is_pinned,

              num_cmts, num_likes, tags, ts_created, ts_edited, summary,

              sub_name=None, sub_owner=None, poster_avatar=None, poster_name=None,

              poster_flair=None, num_notifs=0, num_per_day=None, ts_comment=None):

@@ -1777,6 +1777,22 @@ class Database:



     return segments



+ def get_segment_count(self, post):

+ cur = self.conn.cursor()

+ cur.execute("SELECT COUNT(id) FROM segments WHERE post=? AND type!=?",

+ (post.id, Segment.POLL))

+ for (count,) in cur:

+ return count

+ return 0

+

+ def is_first_segment_of_post(self, post, segment):

+ cur = self.conn.cursor()

+ cur.execute("SELECT MIN(pos) FROM segments WHERE post=? AND type!=?",

+ (post.id, Segment.POLL))

+ for (min_pos,) in cur:

+ return min_pos == segment.pos

+ return False

+

 def get_segments_of_similar_type(self, post, is_poll):

     return list(filter(lambda s:

                        (is_poll     and s.type == Segment.POLL or

@@ -1885,7 +1901,7 @@ class Database:

                            self.get_segments_of_similar_type(post, is_poll)))

     segments = segments[:new_pos] + [moved_segment] + segments[new_pos:]

     cur = self.conn.cursor()

- pos = 0

+ pos = 1

     for seg in segments:

         cur.execute('UPDATE segments SET pos=? WHERE id=?', (pos, seg.id))

         pos += 1

diff --git a/settings.py b/settings.py

index b7143d6..d8efa51 100644

--- a/settings.py

+++ b/settings.py

@@ -898,7 +898,7 @@ def make_settings_page(session):

     page += f'=> /settings/omit-feed {CHECKS[nonzero(user_space.flags & Subspace.OMIT_FROM_FEED_BY_DEFAULT)]} ' + \

             'Omit posts from Gemini/Atom feed by default\n'

     page += 'Individual posts can be included or excluded from All Posts and Gemini/Atom feeds using the composer.\n'

- page += f'=> /settings/autosplit {CHECKS[nonzero(user.flags & User.COMPOSER_SPLIT_FLAG)]} Auto-split paragraphs in composer\n'

+ page += f'\n=> /settings/autosplit {CHECKS[nonzero(user.flags & User.COMPOSER_SPLIT_FLAG)]} Auto-split paragraphs in composer\n'

     page += f'=> /settings/titan-edit {CHECKS[nonzero(user.flags & User.COMPOSER_TITAN_EDIT_FLAG)]} Edit text with Titan\nYour client must support the Titan "edit" parameter.\n'



     page += '\n=> /settings/profile ⚙️ Profile\n'

diff --git a/utils.py b/utils.py

index c7b9722..dbb8588 100644

--- a/utils.py

+++ b/utils.py

@@ -241,6 +241,19 @@ def shorten_text(text, n):

 return text.strip()





+def extract_title_heading(session, body):

+ lines = body.split('\n')

+ title = None

+ found = re.match(r'^\s*#\s*(.+)$', lines[0])

+ if found:

+ title = found[1]

+ body = '\n'.join(lines[1:]).strip()

+ elif session.is_context_tracker:

+ title = lines[0]

+ body = '\n'.join(lines[1:]).strip()

+ return title, body

+

+

def time_delta_text(sec, date_ts, suffix='ago', now='Now',

                 date_prefix='',

                 date_fmt='%Y-%m-%d',

Proxy Information
Original URL
gemini://git.skyjake.fi/bubble/main/pcdiff/25daafcf5fa2626d03107d510c744ee15d575749
Status Code
Success (20)
Meta
text/plain
Capsule Response Time
65.485802 milliseconds
Gemini-to-HTML Time
3.83098 milliseconds

This content has been proxied by September (ba2dc).