Package: django-highlights
django-highlightsAdd a generic relationHighlightto arbitrary models.Selecting the highlight is done with custom javascript.Saving the highlightted snippet (without page refresh) is done withhtmxwith a dash ofhyperscriptfor dealing with the return trigger.SetupInstall.venv>poetryadddjango-highlights# pip3 install django-highlightsAdd app to project settings# in project_folder/settings.pyINSTALLED_APPS=[...,'highlights']Add highlight model to database.venv>pythonmanage.pymigrateConfigurationInitialize modelEnsure model, e.g.Sentinel, with:auniqueSlugField namedslug- this will be used for creating thehighlight urla TextField, e.g.content/description- this is the field that will behighlightableAdd mixinMake the initialized model inherit from theAbstractHighlightableabstract base model :fromdjango_extensions.db.modelsimportTitleSlugDescriptionModelfromhighlights.modelsimportAbstractHighlightable# importclassSentinel(TitleSlugDescriptionModel,AbstractHighlightable):# addpassEachSentinelinstance, i.e. pk=1, pk=2, etc., will now have generic relations to aHighlightmodel and have access to a pre-named,slug-basedhighlight_url. TheSentinelclass will now have a@highlight_pathproperty to be used inurlpatternsso that each instanceshighlight_urlis recognized by the project.Setup url# sentinels/urls.pyfrom.appsimportSentinelsConfig# already built when you previously created `sentinels` via python manage.py startapp sentinelsfrom.modelsimportSentinelapp_name=SentinelsConfig.name# newurlpatterns=[Sentinel.highlight_path,# new...]Use article id with highighter and notice<!-- sentinels/templates/sentinel_detail.html --><!-- Note the `object` as the context_object_name --><main class="container"><h1>Title:{{object.title}}</h1><!-- 1. article id will be the scope monitored for user highlights --><article id="highlightable">{{object.description}}</article></main>{%ifuser.is_authenticated%}<!-- 2. a toast box will be triggered on a successful highlight --><div class="position-fixed top-0 end-0 p-3" style="z-index: 11"><div id="highlight-notice" class="toast hide" role="alert" aria-live="assertive" aria-atomic="true"><div class="toast-body"><span class="me-auto">Highlight added!</span><button type="button" class="btn-close float-end" data-bs-dismiss="toast" aria-label="Close"></button></div></div></div><!-- 3. the footer is unhidden when text within the scope is selected, and will contain a button to highlight -->{%loadhighlighter%}{%highlight_btn_showurl=object.highlight_url%}{%endif%}FlowThe<article>tag will contain thescopeor the highlightable text field.The<footer>will contain theurlor the submission of highlights to the server.The specific DOM nodes have event listeners found intextSelector.js.Any text selection inside the scoped<article>will reflect in the<footer>'s hidden<input>.When highlightmakeris ready with text selection, click on footer<button>submits highlight stored in<input>.The submission is done throughhtmx'shx-postwithout refreshing or swapping content, i.e. a POST request is sent to thesave_highlightview c/o the passedhighlight_url.The request adds a newHighlight(from an authenticated highlightmaker) to the highlightable model instance, e.g. Sentinel pk=2.The successful POST request sends a header trigger to the client to alert themaker.