packages feed

emanote 1.0.0.0 → 1.0.2.0

raw patch · 48 files changed

+676/−544 lines, 48 filesdep +deriving-aesonPVP: major bump suggested

API removals or changes: PVP suggests a major version bump

Dependencies added: deriving-aeson

API changes (from Hackage documentation)

- Emanote.Model.Stork.Index: newtype Input
+ Emanote.Model.Stork.Index: Config :: Input -> Config
+ Emanote.Model.Stork.Index: data Handling
+ Emanote.Model.Stork.Index: data Input
+ Emanote.Model.Stork.Index: instance Data.Aeson.Types.FromJSON.FromJSON Emanote.Model.Stork.Index.Handling
+ Emanote.Model.Stork.Index: instance Data.Default.Class.Default Emanote.Model.Stork.Index.Handling
+ Emanote.Model.Stork.Index: instance GHC.Classes.Eq Emanote.Model.Stork.Index.Config
+ Emanote.Model.Stork.Index: instance GHC.Classes.Eq Emanote.Model.Stork.Index.Handling
+ Emanote.Model.Stork.Index: instance GHC.Generics.Generic Emanote.Model.Stork.Index.Handling
+ Emanote.Model.Stork.Index: instance GHC.Show.Show Emanote.Model.Stork.Index.Config
+ Emanote.Model.Stork.Index: instance GHC.Show.Show Emanote.Model.Stork.Index.Handling
+ Emanote.Model.Stork.Index: newtype Config
- Emanote.Model.Stork.Index: Input :: [File] -> Input
+ Emanote.Model.Stork.Index: Input :: [File] -> Handling -> Input
- Emanote.Model.Stork.Index: readOrBuildStorkIndex :: (MonadIO m, MonadLoggerIO m) => IndexVar -> Input -> m LByteString
+ Emanote.Model.Stork.Index: readOrBuildStorkIndex :: (MonadIO m, MonadLoggerIO m) => IndexVar -> Config -> m LByteString
- Emanote.Model.Type: modelCompileTailwind :: forall encF_a2sEA. Lens' (ModelT encF_a2sEA) Bool
+ Emanote.Model.Type: modelCompileTailwind :: forall encF_a1yQJ. Lens' (ModelT encF_a1yQJ) Bool
- Emanote.Model.Type: modelEmaCLIAction :: forall encF_a2sEA. Lens' (ModelT encF_a2sEA) (Some Action)
+ Emanote.Model.Type: modelEmaCLIAction :: forall encF_a1yQJ. Lens' (ModelT encF_a1yQJ) (Some Action)
- Emanote.Model.Type: modelHeistTemplate :: forall encF_a2sEA. Lens' (ModelT encF_a2sEA) TemplateState
+ Emanote.Model.Type: modelHeistTemplate :: forall encF_a1yQJ. Lens' (ModelT encF_a1yQJ) TemplateState
- Emanote.Model.Type: modelInstanceID :: forall encF_a2sEA. Lens' (ModelT encF_a2sEA) UUID
+ Emanote.Model.Type: modelInstanceID :: forall encF_a1yQJ. Lens' (ModelT encF_a1yQJ) UUID
- Emanote.Model.Type: modelLayers :: forall encF_a2sEA. Lens' (ModelT encF_a2sEA) (Set Loc)
+ Emanote.Model.Type: modelLayers :: forall encF_a1yQJ. Lens' (ModelT encF_a1yQJ) (Set Loc)
- Emanote.Model.Type: modelNav :: forall encF_a2sEA. Lens' (ModelT encF_a2sEA) [Tree Slug]
+ Emanote.Model.Type: modelNav :: forall encF_a1yQJ. Lens' (ModelT encF_a1yQJ) [Tree Slug]
- Emanote.Model.Type: modelNotes :: forall encF_a2sEA. Lens' (ModelT encF_a2sEA) IxNote
+ Emanote.Model.Type: modelNotes :: forall encF_a1yQJ. Lens' (ModelT encF_a1yQJ) IxNote
- Emanote.Model.Type: modelPandocRenderers :: forall encF_a2sEA. Lens' (ModelT encF_a2sEA) (EmanotePandocRenderers Model LMLRoute)
+ Emanote.Model.Type: modelPandocRenderers :: forall encF_a1yQJ. Lens' (ModelT encF_a1yQJ) (EmanotePandocRenderers Model LMLRoute)
- Emanote.Model.Type: modelRels :: forall encF_a2sEA. Lens' (ModelT encF_a2sEA) IxRel
+ Emanote.Model.Type: modelRels :: forall encF_a1yQJ. Lens' (ModelT encF_a1yQJ) IxRel
- Emanote.Model.Type: modelRoutePrism :: forall encF_a2sEA encF_a2thF. Lens (ModelT encF_a2sEA) (ModelT encF_a2thF) (encF_a2sEA (Prism' FilePath SiteRoute)) (encF_a2thF (Prism' FilePath SiteRoute))
+ Emanote.Model.Type: modelRoutePrism :: forall encF_a1yQJ encF_a1ztS. Lens (ModelT encF_a1yQJ) (ModelT encF_a1ztS) (encF_a1yQJ (Prism' FilePath SiteRoute)) (encF_a1ztS (Prism' FilePath SiteRoute))
- Emanote.Model.Type: modelSData :: forall encF_a2sEA. Lens' (ModelT encF_a2sEA) IxSData
+ Emanote.Model.Type: modelSData :: forall encF_a1yQJ. Lens' (ModelT encF_a1yQJ) IxSData
- Emanote.Model.Type: modelStaticFiles :: forall encF_a2sEA. Lens' (ModelT encF_a2sEA) IxStaticFile
+ Emanote.Model.Type: modelStaticFiles :: forall encF_a1yQJ. Lens' (ModelT encF_a1yQJ) IxStaticFile
- Emanote.Model.Type: modelStatus :: forall encF_a2sEA. Lens' (ModelT encF_a2sEA) Status
+ Emanote.Model.Type: modelStatus :: forall encF_a1yQJ. Lens' (ModelT encF_a1yQJ) Status
- Emanote.Model.Type: modelStorkIndex :: forall encF_a2sEA. Lens' (ModelT encF_a2sEA) IndexVar
+ Emanote.Model.Type: modelStorkIndex :: forall encF_a1yQJ. Lens' (ModelT encF_a1yQJ) IndexVar
- Emanote.Model.Type: modelTasks :: forall encF_a2sEA. Lens' (ModelT encF_a2sEA) IxTask
+ Emanote.Model.Type: modelTasks :: forall encF_a1yQJ. Lens' (ModelT encF_a1yQJ) IxTask

Files

CHANGELOG.md view
@@ -1,10 +1,26 @@ # Revision history for emanote +## 1.0.2.0 (2023-01-29)++- Nix+  - Reduce Emanote's Nix runtime closure size+- UI+  - Add source map for Stork [\#391](https://github.com/srid/emanote/pull/391)+  - Workaround for Prism.js and Tailwind CSS both using `table` class [\#320](https://github.com/srid/emanote/pull/396)+  - Add option to include YAML frontmatter in the Stork index [\#398](https://github.com/srid/emanote/pull/398)+- Features+  - Timeline backlinks recognize flexible daily notes suffixed with arbitrary string [\#395](https://github.com/srid/emanote/issues/395)+- Performance+  - Address client-side memory leak due to Stork search in live server [\#411](https://github.com/srid/emanote/issues/411#issuecomment-1402056235)+- Misc+  - Ignore toplevel `flake.{nix,lock}` by default.+  - Remove deprecated `_emanote-bin/compile-css` script+ ## 1.0.0.0 (2022-12-04)  - UI   - Index pages are no longer marked as 'experimental'-  - Add external link icon to external links (this behaviour is customizable). [\#189](https://github.com/EmaApps/emanote/pull/189)+  - Add external link icon to external links (this behaviour is customizable). [\#189](https://github.com/srid/emanote/pull/189)   - `js.mermaid` snippet uses the "module" script tag approach, which is added to end of `<body>`. See [here](https://mermaid-js.github.io/mermaid/#/n00b-gettingStarted?id=_3-calling-the-javascript-api).    - Align dates in timeline queries - Configuration
README.md view
@@ -29,6 +29,10 @@  See [architecture.md](docs/architecture.md) for a high-level overview of the codebase. +### PR contribution guidelines++Run `nix build .#check -L` when opening a PR.+ ## Discussion  To discuss the emanote project, [join Matrix][matrix] or post in [GitHub Discussions][ghdiscuss].@@ -36,11 +40,8 @@ [matrix]: https://matrix.to/#/#ema:matrix.org [ghdiscuss]: https://github.com/srid/emanote/discussions -## Related projects--- https://github.com/squidfunk/mkdocs-material- [^licenses]: Emanote uses software and resources that are licensed differently, viz.:     - [Logo](https://www.svgrepo.com/svg/267765/paper-plane)     - [Stork search](https://github.com/jameslittle230/stork/blob/master/license.txt)-    - Various SVG icons are from [Heroicons](https://heroicons.com/)+    - [Tailwind CSS](https://github.com/tailwindlabs/tailwindcss/blob/master/LICENSE)+    - Various SVG icons are from [Heroicons](https://github.com/tailwindlabs/heroicons/blob/master/LICENSE)
− default/_emanote-bin/compile-css
@@ -1,4 +0,0 @@-#!/usr/bin/env bash-set -xe--echo "Deprecation Warning: this script should not be used anymore"
+ default/_emanote-static/stork/stork.js.map view
@@ -0,0 +1,1 @@+{"version":3,"file":"stork.js","mappings":"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","sources":["webpack://stork/./node_modules/stork-search/stork.js","webpack://stork/./js/config.ts","webpack://stork/./js/dom.ts","webpack://stork/./js/entity.ts","webpack://stork/./js/entityDom.ts","webpack://stork/./js/entityManager.ts","webpack://stork/./js/loaders/indexLoader.ts","webpack://stork/./js/main.ts","webpack://stork/./js/pencil.ts","webpack://stork/./js/resultToListItem.ts","webpack://stork/./js/searchData.ts","webpack://stork/./js/storkError.ts","webpack://stork/./js/util.ts","webpack://stork/./js/validators/indexParamValidator.ts","webpack://stork/./js/wasmManager.ts","webpack://stork/webpack/bootstrap","webpack://stork/webpack/runtime/define property getters","webpack://stork/webpack/runtime/global","webpack://stork/webpack/runtime/hasOwnProperty shorthand","webpack://stork/webpack/runtime/make namespace object","webpack://stork/webpack/runtime/publicPath","webpack://stork/webpack/startup"],"sourcesContent":["\n      function getAbsoluteUrl(relativeUrl) {\n        const publicPath = __webpack_public_path__;\n\n        let url = '';\n\n        if (!publicPath || publicPath.indexOf('://') < 0) {\n          url += window.location.protocol + '//' + window.location.host;\n        }\n\n        if (publicPath) {\n          url += publicPath;\n        } else {\n          url += '/';\n        }\n\n        return url + relativeUrl;\n      }\n\nlet wasm;\n\nconst heap = new Array(32).fill(undefined);\n\nheap.push(undefined, null, true, false);\n\nfunction getObject(idx) { return heap[idx]; }\n\nlet heap_next = heap.length;\n\nfunction dropObject(idx) {\n    if (idx < 36) return;\n    heap[idx] = heap_next;\n    heap_next = idx;\n}\n\nfunction takeObject(idx) {\n    const ret = getObject(idx);\n    dropObject(idx);\n    return ret;\n}\n\nlet WASM_VECTOR_LEN = 0;\n\nlet cachegetUint8Memory0 = null;\nfunction getUint8Memory0() {\n    if (cachegetUint8Memory0 === null || cachegetUint8Memory0.buffer !== wasm.memory.buffer) {\n        cachegetUint8Memory0 = new Uint8Array(wasm.memory.buffer);\n    }\n    return cachegetUint8Memory0;\n}\n\nlet cachedTextEncoder = new TextEncoder('utf-8');\n\nconst encodeString = (typeof cachedTextEncoder.encodeInto === 'function'\n    ? function (arg, view) {\n    return cachedTextEncoder.encodeInto(arg, view);\n}\n    : function (arg, view) {\n    const buf = cachedTextEncoder.encode(arg);\n    view.set(buf);\n    return {\n        read: arg.length,\n        written: buf.length\n    };\n});\n\nfunction passStringToWasm0(arg, malloc, realloc) {\n\n    if (realloc === undefined) {\n        const buf = cachedTextEncoder.encode(arg);\n        const ptr = malloc(buf.length);\n        getUint8Memory0().subarray(ptr, ptr + buf.length).set(buf);\n        WASM_VECTOR_LEN = buf.length;\n        return ptr;\n    }\n\n    let len = arg.length;\n    let ptr = malloc(len);\n\n    const mem = getUint8Memory0();\n\n    let offset = 0;\n\n    for (; offset < len; offset++) {\n        const code = arg.charCodeAt(offset);\n        if (code > 0x7F) break;\n        mem[ptr + offset] = code;\n    }\n\n    if (offset !== len) {\n        if (offset !== 0) {\n            arg = arg.slice(offset);\n        }\n        ptr = realloc(ptr, len, len = offset + arg.length * 3);\n        const view = getUint8Memory0().subarray(ptr + offset, ptr + len);\n        const ret = encodeString(arg, view);\n\n        offset += ret.written;\n    }\n\n    WASM_VECTOR_LEN = offset;\n    return ptr;\n}\n\nfunction passArray8ToWasm0(arg, malloc) {\n    const ptr = malloc(arg.length * 1);\n    getUint8Memory0().set(arg, ptr / 1);\n    WASM_VECTOR_LEN = arg.length;\n    return ptr;\n}\n\nlet cachegetInt32Memory0 = null;\nfunction getInt32Memory0() {\n    if (cachegetInt32Memory0 === null || cachegetInt32Memory0.buffer !== wasm.memory.buffer) {\n        cachegetInt32Memory0 = new Int32Array(wasm.memory.buffer);\n    }\n    return cachegetInt32Memory0;\n}\n\nlet cachedTextDecoder = new TextDecoder('utf-8', { ignoreBOM: true, fatal: true });\n\ncachedTextDecoder.decode();\n\nfunction getStringFromWasm0(ptr, len) {\n    return cachedTextDecoder.decode(getUint8Memory0().subarray(ptr, ptr + len));\n}\n/**\n* @param {string} name\n* @param {Uint8Array} data\n* @returns {string}\n*/\nexport function wasm_register_index(name, data) {\n    try {\n        const retptr = wasm.__wbindgen_add_to_stack_pointer(-16);\n        var ptr0 = passStringToWasm0(name, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);\n        var len0 = WASM_VECTOR_LEN;\n        var ptr1 = passArray8ToWasm0(data, wasm.__wbindgen_malloc);\n        var len1 = WASM_VECTOR_LEN;\n        wasm.wasm_register_index(retptr, ptr0, len0, ptr1, len1);\n        var r0 = getInt32Memory0()[retptr / 4 + 0];\n        var r1 = getInt32Memory0()[retptr / 4 + 1];\n        return getStringFromWasm0(r0, r1);\n    } finally {\n        wasm.__wbindgen_add_to_stack_pointer(16);\n        wasm.__wbindgen_free(r0, r1);\n    }\n}\n\n/**\n* @param {string} name\n* @param {string} query\n* @returns {string}\n*/\nexport function wasm_search(name, query) {\n    try {\n        const retptr = wasm.__wbindgen_add_to_stack_pointer(-16);\n        var ptr0 = passStringToWasm0(name, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);\n        var len0 = WASM_VECTOR_LEN;\n        var ptr1 = passStringToWasm0(query, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);\n        var len1 = WASM_VECTOR_LEN;\n        wasm.wasm_search(retptr, ptr0, len0, ptr1, len1);\n        var r0 = getInt32Memory0()[retptr / 4 + 0];\n        var r1 = getInt32Memory0()[retptr / 4 + 1];\n        return getStringFromWasm0(r0, r1);\n    } finally {\n        wasm.__wbindgen_add_to_stack_pointer(16);\n        wasm.__wbindgen_free(r0, r1);\n    }\n}\n\n/**\n* @returns {string}\n*/\nexport function wasm_stork_version() {\n    try {\n        const retptr = wasm.__wbindgen_add_to_stack_pointer(-16);\n        wasm.wasm_stork_version(retptr);\n        var r0 = getInt32Memory0()[retptr / 4 + 0];\n        var r1 = getInt32Memory0()[retptr / 4 + 1];\n        return getStringFromWasm0(r0, r1);\n    } finally {\n        wasm.__wbindgen_add_to_stack_pointer(16);\n        wasm.__wbindgen_free(r0, r1);\n    }\n}\n\nfunction addHeapObject(obj) {\n    if (heap_next === heap.length) heap.push(heap.length + 1);\n    const idx = heap_next;\n    heap_next = heap[idx];\n\n    heap[idx] = obj;\n    return idx;\n}\n\nasync function load(module, imports) {\n    if (typeof Response === 'function' && module instanceof Response) {\n        if (typeof WebAssembly.instantiateStreaming === 'function') {\n            try {\n                return await WebAssembly.instantiateStreaming(module, imports);\n\n            } catch (e) {\n                if (module.headers.get('Content-Type') != 'application/wasm') {\n                    console.warn(\"`WebAssembly.instantiateStreaming` failed because your server does not serve wasm with `application/wasm` MIME type. Falling back to `WebAssembly.instantiate` which is slower. Original error:\\n\", e);\n\n                } else {\n                    throw e;\n                }\n            }\n        }\n\n        const bytes = await module.arrayBuffer();\n        return await WebAssembly.instantiate(bytes, imports);\n\n    } else {\n        const instance = await WebAssembly.instantiate(module, imports);\n\n        if (instance instanceof WebAssembly.Instance) {\n            return { instance, module };\n\n        } else {\n            return instance;\n        }\n    }\n}\n\nasync function init(input) {\n    if (typeof input === 'undefined') {\n        input = new URL('stork_bg.wasm', ({ url: getAbsoluteUrl('node_modules/stork-search/stork.js') }).url);\n    }\n    const imports = {};\n    imports.wbg = {};\n    imports.wbg.__wbg_new_693216e109162396 = function() {\n        var ret = new Error();\n        return addHeapObject(ret);\n    };\n    imports.wbg.__wbg_stack_0ddaca5d1abfb52f = function(arg0, arg1) {\n        var ret = getObject(arg1).stack;\n        var ptr0 = passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);\n        var len0 = WASM_VECTOR_LEN;\n        getInt32Memory0()[arg0 / 4 + 1] = len0;\n        getInt32Memory0()[arg0 / 4 + 0] = ptr0;\n    };\n    imports.wbg.__wbg_error_09919627ac0992f5 = function(arg0, arg1) {\n        try {\n            console.error(getStringFromWasm0(arg0, arg1));\n        } finally {\n            wasm.__wbindgen_free(arg0, arg1);\n        }\n    };\n    imports.wbg.__wbindgen_object_drop_ref = function(arg0) {\n        takeObject(arg0);\n    };\n\n    if (typeof input === 'string' || (typeof Request === 'function' && input instanceof Request) || (typeof URL === 'function' && input instanceof URL)) {\n        input = fetch(input);\n    }\n\n\n\n    const { instance, module } = await load(await input, imports);\n\n    wasm = instance.exports;\n    init.__wbindgen_wasm_module = module;\n\n    return wasm;\n}\n\nexport default init;\n\n","import StorkError from \"./storkError\";\nimport { difference, plural } from \"./util\";\n\nexport interface Configuration {\n  showProgress: boolean;\n  printIndexInfo: boolean;\n  showScores: boolean;\n  showCloseButton: boolean;\n  minimumQueryLength: number;\n  forceOverwrite: boolean;\n  resultNoun: { singular: string; plural: string };\n  onQueryUpdate?: (query: string, results: unknown) => unknown;\n  onResultSelected?: (query: string, result: unknown) => unknown;\n  onResultsHidden?: () => unknown;\n  onInputCleared?: () => unknown;\n  transformResultUrl: (url: string) => string;\n}\n\nexport const defaultConfig: Readonly<Configuration> = {\n  showProgress: true,\n  printIndexInfo: false,\n  showScores: false,\n  showCloseButton: true,\n  minimumQueryLength: 3,\n  forceOverwrite: false,\n  resultNoun: { singular: \"file\", plural: \"files\" },\n  onQueryUpdate: undefined,\n  onResultSelected: undefined,\n  onResultsHidden: undefined,\n  onInputCleared: undefined,\n  transformResultUrl: url => url,\n};\n\nexport function calculateOverriddenConfig(\n  overrides: Partial<Configuration>\n): Configuration | StorkError {\n  const configKeyDiff = difference(\n    Object.keys(overrides),\n    Object.keys(defaultConfig)\n  );\n\n  if (configKeyDiff.length > 0) {\n    const keys = plural(configKeyDiff.length, \"key\", \"keys\");\n    const invalidKeys = JSON.stringify(configKeyDiff);\n    return new StorkError(`Invalid ${keys} in config object: ${invalidKeys}`);\n  }\n\n  const output: Configuration = Object.assign({}, defaultConfig);\n\n  for (const key of Object.keys(defaultConfig) as Array<keyof Configuration>) {\n    const overrideVal = overrides[key];\n    if (overrideVal !== undefined) {\n      // eslint-disable-next-line @typescript-eslint/ban-ts-comment\n      // @ts-ignore\n      output[key] = overrideVal;\n    }\n  }\n\n  return output;\n}\n","export function create(\n  name: string,\n  attributes: Record<string, Array<string>>\n): HTMLElement {\n  const elem = document.createElement(name);\n  if (attributes.classNames) {\n    elem.setAttribute(\"class\", attributes.classNames.join(\" \"));\n  }\n  return elem;\n}\n\nexport function add(\n  elem: HTMLElement,\n  location: InsertPosition,\n  reference: HTMLElement\n): void {\n  reference.insertAdjacentElement(location, elem);\n}\n\nexport function clear(elem: HTMLElement | null): void {\n  while (elem && elem.firstChild) {\n    elem.removeChild(elem.firstChild);\n  }\n}\n\nexport function setText(elem: HTMLElement | null, text: string): void {\n  const textNode = document.createTextNode(text);\n  if (elem && elem.firstChild) {\n    elem.replaceChild(textNode, elem.firstChild);\n  } else if (elem) {\n    elem.appendChild(textNode);\n  }\n}\n\nexport function existsBeyondContainerBounds(\n  elem: HTMLElement,\n  container: HTMLElement\n): boolean {\n  const elemBoundingBox = elem.getBoundingClientRect();\n  const containerBoundingBox = container.getBoundingClientRect();\n\n  return (\n    elemBoundingBox.bottom > containerBoundingBox.bottom ||\n    elemBoundingBox.top < containerBoundingBox.top\n  );\n}\n","import { Configuration } from \"./config\";\nimport { Result, SearchData, resolveSearch } from \"./searchData\";\nimport { EntityDom, RenderState } from \"./entityDom\";\nimport { wasm_register_index } from \"stork-search\";\nimport StorkError from \"./storkError\";\n\nexport type EntityState = \"initialized\" | \"loading\" | \"ready\" | \"error\";\n\nexport class Entity {\n  readonly name: string;\n  readonly url: string;\n  readonly config: Configuration;\n\n  private _state: EntityState = \"initialized\";\n\n  downloadProgress = 0;\n\n  index: Uint8Array;\n  results: Array<Result> = [];\n  totalResultCount = 0;\n\n  domManager: EntityDom | null;\n  eventListenerFunctions: Record<string, (e: Event) => void> = {};\n  highlightedResult = 0;\n  resultsVisible = false;\n  hoverSelectEnabled = true;\n\n  constructor(name: string, url: string, config: Configuration) {\n    this.name = name;\n    this.url = url;\n    this.config = config;\n  }\n\n  public get state(): EntityState {\n    return this._state;\n  }\n\n  public set state(value: EntityState) {\n    this._state = value;\n    this.render();\n  }\n\n  private getCurrentMessage(): string | null {\n    if (!this.domManager) return null;\n    const query = this.domManager.getQuery();\n    if (this.state === \"error\") {\n      return \"Error! Check the browser console.\";\n    } else if (this.state != \"ready\") {\n      return \"Loading...\";\n    } else if (query?.length < this.config.minimumQueryLength) {\n      return \"Filtering...\";\n    } else if (this.results) {\n      if (this.totalResultCount === 0) {\n        return `No ${this.config.resultNoun.plural} found.`;\n      } else if (this.totalResultCount === 1) {\n        return `1 ${this.config.resultNoun.singular} found.`;\n      } else {\n        return `${this.totalResultCount} ${this.config.resultNoun.plural} found.`;\n      }\n    }\n\n    return null;\n  }\n\n  private generateRenderConfig(): RenderState {\n    return {\n      results: this.results,\n      resultsVisible: true,\n      showScores: this.config.showScores,\n      message: this.getCurrentMessage(),\n      showProgress: this.config.showProgress,\n      progress: this.downloadProgress,\n      state: this.state\n    };\n  }\n\n  private render() {\n    if (!this.domManager) return;\n    this.domManager.render(this.generateRenderConfig());\n  }\n\n  registerIndex(data: Uint8Array): Promise<void> {\n    return new Promise((resolve, reject) => {\n      const indexInfo = JSON.parse(wasm_register_index(this.name, data));\n      if (indexInfo.error) {\n        reject(new StorkError(indexInfo.error));\n      } else {\n        if (this.config.printIndexInfo) {\n          console.log(indexInfo);\n        }\n\n        this.state = \"ready\";\n        resolve(indexInfo);\n      }\n    });\n  }\n\n  attachToDom(): void {\n    this.domManager = new EntityDom(this.name, this);\n    this.render();\n  }\n\n  injestSearchData(data: SearchData): void {\n    this.results = data.results;\n    this.totalResultCount = data.total_hit_count;\n    this.highlightedResult = 0;\n\n    // Mutate the result URL, like we do when there's a url prefix or suffix\n    const urlPrefix = data.url_prefix || \"\";\n    this.results.map(r => {\n      let urlSuffix = \"\";\n\n      const firstInternalAnnotations = r.excerpts\n        .map(e => e.internal_annotations)\n        .filter(ia => !!ia)[0];\n\n      if (firstInternalAnnotations && firstInternalAnnotations[0]) {\n        const annotationMap = firstInternalAnnotations[0];\n        if (typeof annotationMap[\"a\"] === \"string\") {\n          urlSuffix += annotationMap[\"a\"];\n        }\n      }\n\n      // oof\n      if (\n        r.excerpts &&\n        r.excerpts[0] &&\n        r.excerpts[0].internal_annotations &&\n        r.excerpts[0].internal_annotations[0] &&\n        r.excerpts[0].internal_annotations[0][\"a\"] &&\n        typeof r.excerpts[0].internal_annotations[0][\"a\"] === \"string\"\n      ) {\n        urlSuffix = r.excerpts[0].internal_annotations[0][\"a\"];\n      }\n      r.entry.url = this.config.transformResultUrl(`${urlPrefix}${r.entry.url}${urlSuffix}`);\n    });\n\n    this.render();\n  }\n\n  private getSanitizedResults() {\n    const results = this.results;\n    results.map(result => {\n      delete result.title_highlight_ranges;\n      result.excerpts.map(excerpt => {\n        delete excerpt.highlight_ranges;\n        delete excerpt.internal_annotations;\n      });\n    });\n    return results;\n  }\n\n  setDownloadProgress = (percentage: number): void => {\n    this.state = \"loading\";\n    this.downloadProgress = percentage;\n    if (this.config.showProgress) {\n      this.render();\n    }\n  };\n\n  setDownloadError(): void {\n    this.state = \"error\";\n  }\n\n  performSearch(query: string): void {\n    if (this.state !== \"ready\") {\n      this.render();\n      return;\n    }\n\n    if (query.length < this.config.minimumQueryLength) {\n      this.results = [];\n      this.render();\n      return;\n    }\n\n    try {\n      const data = resolveSearch(this.name, query);\n      if (!data) return;\n\n      this.injestSearchData(data);\n\n      if (this.config.onQueryUpdate) {\n        this.config.onQueryUpdate(query, this.getSanitizedResults());\n      }\n    } catch (error) {\n      console.error(error);\n    }\n  }\n}\n","import { Result } from \"./searchData\";\n\nimport {\n  create,\n  add,\n  clear,\n  setText,\n  existsBeyondContainerBounds\n} from \"./dom\";\nimport { Entity, EntityState } from \"./entity\";\nimport { ListItemDisplayOptions, resultToListItem } from \"./resultToListItem\";\n\ninterface ElementMap {\n  input: HTMLInputElement;\n  output: HTMLDivElement;\n  progress: HTMLElement;\n  list: HTMLElement;\n  message: HTMLElement;\n  attribution: HTMLElement;\n  closeButton: HTMLElement;\n}\n\nexport interface RenderState {\n  results: Array<Result>;\n  resultsVisible: boolean;\n  showScores: boolean;\n  message: string | null;\n  showProgress: boolean;\n  progress: number;\n  state: EntityState;\n}\n\nconst hiddenInterfaceRenderState: RenderState = {\n  results: [],\n  resultsVisible: false,\n  showScores: false,\n  message: null,\n  showProgress: false,\n  progress: 1,\n  state: \"ready\"\n};\n\nexport class EntityDom {\n  readonly elements: ElementMap;\n  readonly entity: Entity;\n\n  highlightedResult?: number;\n  hoverSelectEnabled: boolean;\n  lastRenderState: RenderState;\n\n  scrollAnchorPoint: \"start\" | \"end\" = \"end\";\n\n  constructor(name: string, entity: Entity) {\n    this.entity = entity;\n\n    const data = [\n      {\n        selector: `input[data-stork=\"${name}\"]`,\n        elementName: \"input\"\n      },\n      {\n        selector: `div[data-stork=\"${name}-output\"]`,\n        elementName: \"output\"\n      }\n    ];\n\n    const [input, output] = data.map(d => {\n      const element = document.querySelector(d.selector);\n      if (!element) {\n        throw new Error(\n          `Could not register search box \"${name}\": ${d.elementName} element not found. Make sure an element matches the query selector \\`${d.selector}\\``\n        );\n      }\n\n      return element;\n    }) as [HTMLInputElement, HTMLDivElement];\n\n    this.elements = {\n      input: input,\n      output: output,\n      list: create(\"ul\", { classNames: [\"stork-results\"] }),\n      attribution: create(\"div\", {\n        classNames: [\"stork-attribution\"]\n      }),\n      progress: create(\"div\", { classNames: [\"stork-progress\"] }),\n      message: create(\"div\", { classNames: [\"stork-message\"] }),\n      closeButton: create(\"button\", {\n        classNames: [\"stork-close-button\"]\n      })\n    };\n\n    // First, remove saved event listener functions from the element, if they exist.\n    // This makes the EntityDom constructor safe to call multiple times, even if\n    // the elements on the page haven't changed.\n    this.elements.input.removeEventListener(\n      \"input\",\n      this.entity.eventListenerFunctions.inputInputEvent\n    );\n\n    this.elements.input.removeEventListener(\n      \"keydown\",\n      this.entity.eventListenerFunctions.inputKeydownEvent\n    );\n\n    // Then, save new event listener functions to the entity so that we can\n    // delete those listeners from the corresponding elements when the\n    // EntityDom object is recreated.\n    this.entity.eventListenerFunctions = {\n      inputInputEvent: (e: InputEvent) => {\n        this.handleInputEvent(e as InputEvent);\n      },\n\n      inputKeydownEvent: (e: KeyboardEvent) => {\n        this.handleKeyDownEvent(e as KeyboardEvent);\n      }\n    };\n\n    // Then, add those newly saved functions as event listeners on the elements.\n    this.elements.input.addEventListener(\n      \"input\",\n      this.entity.eventListenerFunctions.inputInputEvent\n    );\n\n    this.elements.input.addEventListener(\n      \"keydown\",\n      this.entity.eventListenerFunctions.inputKeydownEvent\n    );\n\n    // We didn't have to do the remove/add dance with this one because\n    // this listener behavior is already idempotent.\n    this.elements.list?.addEventListener(\"mousemove\", () => {\n      this.hoverSelectEnabled = true;\n    });\n\n    this.elements.attribution.innerHTML =\n      'Powered by <a href=\"https://stork-search.net\">Stork</a>';\n\n    this.elements.closeButton.innerHTML = `\n<svg height=\"0.8em\" viewBox=\"0 0 23 24\" xmlns=\"http://www.w3.org/2000/svg\">\n<g fill=\"none\" fill-rule=\"evenodd\" stroke-linecap=\"round\">\n<g transform=\"translate(-700 -149)\" stroke=\"currentcolor\" stroke-width=\"4\">\n<line id=\"a\" x1=\"702.5\" x2=\"720\" y1=\"152.5\" y2=\"170\"/>\n<line transform=\"translate(711 161) rotate(-90) translate(-711 -161)\" x1=\"702.5\" x2=\"720\" y1=\"152.5\" y2=\"170\"/>\n</g>\n</g>\n</svg>`;\n\n    if (this.entity.config.showProgress) {\n      add(this.elements.progress, \"afterend\", this.elements.input);\n    }\n\n    this.elements.closeButton?.addEventListener(\"click\", () => {\n      this.elements.input.value = \"\";\n      this.elements.input.focus();\n      this.render(hiddenInterfaceRenderState);\n      const [m, n] = [\n        this.entity.config.onInputCleared,\n        this.entity.config.onResultsHidden\n      ];\n      m ? m() : null;\n      n ? n() : null;\n    });\n  }\n\n  private clearDom() {\n    clear(this.elements.output);\n    clear(this.elements.list);\n    this.elements.closeButton?.remove();\n    this.elements.output.classList.remove(\"stork-output-visible\");\n  }\n\n  render(state: RenderState): void {\n    const query = (this.elements.input as HTMLInputElement).value;\n    this.clearDom();\n    this.lastRenderState = state;\n\n    if (state.showProgress) {\n      const getFakeProgress = (): number => {\n        switch (state.state) {\n          case \"ready\":\n          case \"error\":\n            return 1;\n          case \"initialized\":\n          case \"loading\":\n            return state.progress * 0.9 + 0.05;\n        }\n      };\n\n      const progress = getFakeProgress();\n\n      if (progress < 1) {\n        this.elements.progress.style.width = `${progress * 100}%`;\n        this.elements.progress.style.opacity = \"1\";\n      } else {\n        this.elements.progress.style.width = `100%`;\n        this.elements.progress.style.opacity = \"0\";\n      }\n    }\n\n    if (state.state === \"error\") {\n      this.elements.input.classList.add(\"stork-error\");\n    }\n\n    if (this.getQuery().length > 0 && state.resultsVisible) {\n      this.elements.output.classList.add(\"stork-output-visible\");\n      add(this.elements.message, \"beforeend\", this.elements.output);\n    }\n\n    if (state.message) {\n      setText(this.elements.message, state.message);\n    }\n\n    if (state.results?.length > 0 && state.resultsVisible) {\n      add(this.elements.list, \"beforeend\", this.elements.output);\n\n      for (let i = 0; i < state.results.length; i++) {\n        const result = state.results[i];\n        const generateOptions: ListItemDisplayOptions = {\n          selected: i === this.highlightedResult,\n          showScores: state.showScores\n        };\n\n        const listItem = resultToListItem(result, generateOptions);\n        add(listItem as HTMLElement, \"beforeend\", this.elements.list);\n\n        listItem.addEventListener(\"mousemove\", () => {\n          if (this.hoverSelectEnabled) {\n            if (i !== this.highlightedResult) {\n              this.changeHighlightedResult({ to: i, shouldScrollTo: false });\n            }\n          }\n        });\n\n        listItem.addEventListener(\"mouseleave\", () => {\n          if (this.hoverSelectEnabled) {\n            if (i === this.highlightedResult) {\n              this.changeHighlightedResult({ to: -1, shouldScrollTo: false });\n            }\n          }\n        });\n\n        listItem.addEventListener(\"click\", e => {\n          e.preventDefault();\n          this.selectResult();\n        });\n      }\n\n      add(this.elements.attribution, \"beforeend\", this.elements.output);\n    }\n\n    if ((query?.length || 0) > 0 && this.entity.config.showCloseButton) {\n      add(this.elements.closeButton, \"afterend\", this.elements.input);\n    }\n  }\n\n  private selectResult() {\n    if (this.highlightedResult != null) {\n      const result = this.entity.results[this.highlightedResult];\n      if (this.entity.config.onResultSelected) {\n        Promise.resolve(\n          this.entity.config.onResultSelected(this.getQuery(), result)\n        ).finally(() => {\n          window.location.assign(result.entry.url);\n        });\n      } else {\n        window.location.assign(result.entry.url);\n      }\n    }\n  }\n\n  changeHighlightedResult(options: {\n    to: number;\n    shouldScrollTo: boolean;\n  }): number {\n    const previousValue = this.highlightedResult;\n\n    const resolvedIdx = Math.max(\n      -1, // `to` will be -1 if we want to clear the highlight\n      Math.min(this.entity.results.length - 1, options.to)\n    );\n\n    this.highlightedResult = resolvedIdx;\n    this.scrollAnchorPoint =\n      (previousValue || 0) < resolvedIdx ? \"end\" : \"start\";\n\n    let targetForScrollTo = null;\n\n    for (let i = 0; i < this.entity.results.length; i++) {\n      const element = this.elements.list?.children[i];\n      if (!element) {\n        continue;\n      }\n\n      const highlightedClassName = \"selected\";\n\n      if (i == resolvedIdx) {\n        element.classList.add(highlightedClassName);\n        targetForScrollTo = element;\n      } else {\n        element.classList.remove(highlightedClassName);\n      }\n    }\n\n    if (options.shouldScrollTo) {\n      this.hoverSelectEnabled = false;\n      if (targetForScrollTo) {\n        if (\n          existsBeyondContainerBounds(\n            targetForScrollTo as HTMLElement,\n            this.elements.list\n          )\n        ) {\n          (targetForScrollTo as HTMLElement).scrollIntoView({\n            behavior: \"smooth\",\n            block: this.scrollAnchorPoint,\n            inline: \"nearest\"\n          });\n        }\n      }\n    }\n\n    return resolvedIdx;\n  }\n\n  handleKeyDownEvent(event: KeyboardEvent): void {\n    const UP = 38;\n    const DOWN = 40;\n    const RETURN = 13;\n    const ESC = 27;\n\n    switch (event.keyCode) {\n      case DOWN: {\n        if (this.highlightedResult == null) {\n          this.changeHighlightedResult({ to: 0, shouldScrollTo: true });\n        } else {\n          const target = Math.min(\n            this.highlightedResult + 1,\n            this.entity.results.length - 1\n          );\n          this.changeHighlightedResult({ to: target, shouldScrollTo: true });\n        }\n        break;\n      }\n\n      case UP: {\n        if (this.highlightedResult != null) {\n          const target = Math.max(0, this.highlightedResult - 1);\n          this.changeHighlightedResult({ to: target, shouldScrollTo: true });\n        }\n        break;\n      }\n\n      case RETURN:\n        this.selectResult();\n        break;\n\n      case ESC:\n        if (this.lastRenderState.resultsVisible) {\n          this.render(hiddenInterfaceRenderState);\n          const m = this.entity.config.onResultsHidden;\n          m ? m() : null;\n        } else if (this.elements.input.value.length > 0) {\n          this.elements.input.value = \"\";\n          this.render(hiddenInterfaceRenderState); // To clear [x] button\n          const m = this.entity.config.onInputCleared;\n          m ? m() : null;\n        }\n\n        break;\n\n      default:\n        return;\n    }\n  }\n\n  handleInputEvent(event: InputEvent): void {\n    this.entity.performSearch((event.target as HTMLInputElement).value);\n  }\n\n  getQuery(): string {\n    return (this.elements.input as HTMLInputElement).value;\n  }\n}\n","import { Entity } from \"./entity\";\nimport { Configuration, calculateOverriddenConfig } from \"./config\";\nimport { loadIndexFromUrl } from \"./loaders/indexLoader\";\nimport { runAfterWasmLoaded } from \"./wasmManager\";\nimport StorkError from \"./storkError\";\n\nconst entities: Record<string, Entity> = {};\n\nconst register = (\n  name: string,\n  url: string,\n  partialConfig: Partial<Configuration>\n): Promise<void> => {\n  return new Promise((res, rej) => {\n    const fullConfig = calculateOverriddenConfig(partialConfig);\n    if (fullConfig instanceof StorkError) {\n      rej(fullConfig);\n      return;\n    }\n\n    if (entities[name] && !fullConfig.forceOverwrite) {\n      rej(\n        new StorkError(\n          `You're registering an index named \\`${name}\\`, but that already exists. If this is expected, set forceOverwrite to true in your Javascript config to allow overwriting indexes.`\n        )\n      );\n    }\n\n    const entity = new Entity(name, url, fullConfig);\n    entities[name] = entity;\n\n    loadIndexFromUrl(url, {\n      progress: percentage => {\n        entity.setDownloadProgress(percentage);\n      },\n\n      load: response => {\n        runAfterWasmLoaded(\n          () => {\n            entity.registerIndex(new Uint8Array(response)).then(res).catch(rej);\n          },\n          () => {\n            entity.state = \"error\";\n          }\n        );\n      },\n\n      error: () => {\n        entity.setDownloadError();\n        rej();\n      }\n    });\n  });\n};\n\nconst attachToDom = (name: string): void => {\n  if (!entities[name]) {\n    throw new Error(`Index ${name} has not been registered!`);\n  }\n\n  entities[name].attachToDom();\n};\n\nconst entityIsReady = (name: string): boolean => {\n  return entities[name]?.state === \"ready\";\n};\n\nconst debug = (): Record<string, unknown> => ({\n  entities: { ...entities },\n  entitiesCount: entities.length\n});\n\nexport { register, attachToDom, entityIsReady, debug };\n","interface IndexLoaderCallbacks {\n  load: (response: ArrayBufferLike) => void;\n  progress: (percentage: number) => void;\n  error: () => void;\n}\n\nexport function loadIndexFromUrl(\n  url: string,\n  callbacks: IndexLoaderCallbacks\n): void {\n  const r = new XMLHttpRequest();\n\n  r.addEventListener(\"load\", e => {\n    const { status, response } = e.target as XMLHttpRequest;\n\n    // This shouldn't happen on the `load` event, but handle it safely if it does\n    if (status === 0) {\n      callbacks.progress(e.loaded / e.total);\n      return;\n    }\n\n    if (status < 200 || status > 299) {\n      callbacks.error();\n      return;\n    }\n\n    callbacks.load(response);\n  });\n\n  r.addEventListener(\"error\", () => {\n    callbacks.error();\n  });\n\n  r.addEventListener(\"progress\", e => {\n    callbacks.progress(e.loaded / e.total);\n  });\n\n  r.responseType = \"arraybuffer\";\n  r.open(\"GET\", url);\n  r.send();\n}\n","import { Configuration } from \"./config\";\nimport {\n  register as registerEntity,\n  attachToDom,\n  entityIsReady,\n  debug as entityDebug\n} from \"./entityManager\";\nimport { loadWasm, debug as wasmDebug } from \"./wasmManager\";\nimport { resolveSearch, SearchData } from \"./searchData\";\nimport StorkError from \"./storkError\";\nimport { validateIndexParams } from \"./validators/indexParamValidator\";\nimport { wasm_stork_version } from \"stork-search\";\n\nfunction initialize(wasmOverrideUrl: string | null = null): Promise<void> {\n  return loadWasm(wasmOverrideUrl).then(() => {\n    return;\n  });\n}\n\nfunction downloadIndex(name: string, url: string, config = {}): Promise<void> {\n  return new Promise((res, rej) => {\n    const validationError = validateIndexParams(name, url);\n    if (validationError) {\n      rej(validationError);\n      return;\n    }\n\n    registerEntity(name, url, config).then(res).catch(rej);\n  });\n}\n\nfunction attach(name: string): void {\n  try {\n    attachToDom(name);\n  } catch (e) {\n    throw new StorkError(e.message);\n  }\n}\n\nfunction register(\n  name: string,\n  url: string,\n  config: Partial<Configuration> = {}\n): Promise<void> {\n  const initPromise = initialize();\n  const downloadPromise = downloadIndex(name, url, config);\n  attach(name);\n\n  // This silly `then` call turns a [(void), (void)] into a (void), which is\n  // only necessary to make Typescript happy.\n  // You begin to wonder if you write Typescript code, or if Typescript code writes you.\n  return Promise.all([initPromise, downloadPromise]).then();\n}\n\nfunction search(name: string, query: string): SearchData {\n  if (!name || !query) {\n    throw new StorkError(\n      \"Make sure to call stork.search() with two arguments: the index name and the search query.\"\n    );\n  }\n\n  if (!entityIsReady(name)) {\n    throw new StorkError(\n      \"Couldn't find index. Make sure the stork.downloadIndex() promise has resolved before calling stork.search().\"\n    );\n  }\n\n  return resolveSearch(name, query);\n}\n\nfunction debug(): Record<string, unknown> {\n  return {\n    ...wasmDebug(),\n    ...entityDebug(),\n    jsStorkVersion: process.env.VERSION,\n    wasmStorkVersion: wasm_stork_version\n  };\n}\n\nexport { initialize, downloadIndex, attach, search, register, debug };\n","// It's like Handlebars, but smaller.\n\nimport { HighlightRange } from \"./searchData\";\n\nexport function highlight(\n  text: string,\n  highlight_ranges: Array<HighlightRange>\n): string {\n  function insert(str: string, index: number, value: string) {\n    return str.substr(0, index) + value + str.substr(index);\n  }\n\n  let charactersAlreadyAdded = 0;\n\n  for (const range of highlight_ranges) {\n    const beginningInsertion = `<mark class=\"stork-highlight\">`;\n    const endInsertion = `</mark>`;\n\n    text = insert(\n      text,\n      range.beginning + charactersAlreadyAdded,\n      beginningInsertion\n    );\n    charactersAlreadyAdded += beginningInsertion.length;\n\n    text = insert(text, range.end + charactersAlreadyAdded, endInsertion);\n    charactersAlreadyAdded += endInsertion.length;\n  }\n\n  return text;\n}\n","import { highlight } from \"./pencil\";\nimport { Result } from \"./searchData\";\n\nexport interface ListItemDisplayOptions {\n  selected: boolean;\n  showScores: boolean;\n}\n\nexport function resultToListItem(\n  result: Result,\n  options: ListItemDisplayOptions\n): ChildNode {\n  const template = document.createElement(\"template\");\n  template.innerHTML = `\n<li class=\"stork-result${options.selected ? \" selected\" : \"\"}\">\n  <a href=\"${result.entry.url}\">\n    <div class=\"stork-title\">\n      <p>${highlight(\n        result.entry.title,\n        result.title_highlight_ranges || []\n      )}</p>\n      ${options.showScores ? `<code><b>${result.score}</b></code>` : \"\"}\n    </div>\n    ${\n      result.excerpts.length > 0\n        ? '<div class=\"stork-excerpt-container\" />'\n        : \"\"\n    }\n    ${result.excerpts\n      .map(\n        e => `<div class=\"stork-excerpt\"><p>\n        ...${highlight(e.text, e.highlight_ranges || [])}...\n        </p>\n        ${options.showScores ? `<code>${e.score}</code>` : \"\"}\n        </div>`\n      )\n      .join(\"\")}\n        ${result.excerpts.length > 0 ? \"</div>\" : \"\"}\n  </a>\n</li>`;\n  return template.content.firstElementChild as ChildNode;\n}\n","import { wasm_search } from \"stork-search\";\nimport StorkError from \"./storkError\";\n\nexport interface HighlightRange {\n  beginning: number;\n  end: number;\n}\n\nexport interface Entry {\n  fields: Record<string, unknown>;\n  title: string;\n  url: string;\n}\n\nexport interface Excerpt {\n  fields: Record<string, unknown>;\n  internal_annotations?: Array<Record<string, unknown>>;\n  highlight_ranges?: Array<HighlightRange>;\n  score: number;\n  text: string;\n}\n\nexport interface Result {\n  entry: Entry;\n  excerpts: Array<Excerpt>;\n  score: number;\n  title_highlight_ranges?: Array<HighlightRange>;\n}\n\nexport interface SearchData {\n  results: Array<Result>;\n  total_hit_count: number;\n  url_prefix: string;\n}\n\nexport function resolveSearch(name: string, query: string): SearchData {\n  let searchOutput = null;\n  let data = null;\n\n  try {\n    searchOutput = wasm_search(name, query);\n    // If wasm_search returns an error, it will return a JSON blob. Look for\n    // data.error to see if this is the case.\n    data = JSON.parse(searchOutput);\n  } catch (e) {\n    // Data has come back improperly, even beyond an error in Rust-land.\n    // analytics.log(e)\n    throw new StorkError(\n      \"Could not parse data from wasm_search. If you see this, please file a bug: https://jil.im/storkbug \" +\n        searchOutput\n    );\n  }\n\n  if (!data) {\n    throw new StorkError(\"Data was an empty object\");\n  }\n\n  if (data.error) {\n    throw new StorkError(`Could not perform search: the WASM binary failed to return search results.\n    You might not be serving your search index properly.\n    If you think this is an error, please file a bug: https://jil.im/storkbug\n    \n    The WASM binary came back with:\n    ${data.error}`);\n  }\n\n  return data;\n}\n","class StorkError extends Error {\n  constructor(message: string) {\n    super(message);\n    this.name = \"StorkError\";\n  }\n}\n\nexport default StorkError;\n","export function htmlToElement(html: string): ChildNode | null {\n  const template = document.createElement(\"template\");\n  html = html.trim(); // Never return a text node of whitespace as the result\n  template.innerHTML = html;\n  return template.content.firstChild;\n}\n\nexport function difference<T>(arr1: Array<T>, arr2: Array<T>): Array<T> {\n  const set1 = new Set(arr1);\n  const set2 = new Set(arr2);\n  const diff = new Set(Array.from(set1).filter(x => !set2.has(x)));\n  return Array.from(diff);\n}\n\nexport const plural = (\n  count: number,\n  singular: string,\n  plural: string\n): string => (count == 1 ? singular : plural);\n","import StorkError from \"../storkError\";\n\nexport const validateIndexParams = (\n  name: string,\n  url: string\n): StorkError | null => {\n  if (typeof name !== \"string\") {\n    return new StorkError(\"Index registration name must be a string.\");\n  }\n\n  if (typeof url !== \"string\") {\n    return new StorkError(\"URL must be a string.\");\n  }\n\n  return null;\n};\n","import init from \"stork-search\";\nimport StorkError from \"./storkError\";\n\nconst version = process.env.VERSION;\nconst DEFAULT_WASM_URL = version\n  ? `https://files.stork-search.net/releases/v${version}/stork.wasm`\n  : `https://files.stork-search.net/stork.wasm`;\n\nlet wasmSourceUrl: string | null = null; // only for debug output\nlet wasmLoadPromise: Promise<string | void> | null = null;\n\nlet queue: { (): void }[] = [];\nlet errorQueue: { (): void }[] = [];\n\nconst loadWasm = (\n  overrideUrl: string | null = null\n): Promise<string | void> => {\n  // If there's a WASM load in flight or complete, don't try to call init again\n  if (wasmLoadPromise) {\n    return wasmLoadPromise;\n  }\n\n  const url = overrideUrl || DEFAULT_WASM_URL;\n  wasmSourceUrl = url;\n\n  const p = init(url)\n    .then(() => {\n      flush();\n      return url;\n    })\n    .catch(() => {\n      errorFlush();\n      throw new StorkError(`Error while loading WASM at ${url}`);\n    });\n\n  wasmLoadPromise = p;\n  return p;\n};\n\n/**\n * Caller should use this to queue up a function to be run only when the\n * WASM is loaded. If the WASM is already loaded when this method is called,\n * the function will run immediately.\n *\n * @param fn Function to be run once WASM is loaded\n *\n * @returns a promise if loadWasm has been called, or undefined if loadWasm\n * has not been called. If loadWasm has been called, the promise will resolve\n * when the WASM has been loaded and when the function has been run.\n */\nconst runAfterWasmLoaded = (\n  fn: () => void,\n  err: () => void\n): Promise<string | void> | null => {\n  if (!wasmLoadPromise) {\n    queue.push(fn);\n    errorQueue.push(err);\n    return null;\n  } else {\n    // We have a wasmLoadPromise, but we don't know if it's resolved.\n    // Let's wait for it to resolve, then run the function.\n    wasmLoadPromise.then(() => fn()).catch(() => err());\n    return wasmLoadPromise;\n  }\n};\n\nconst flush = () => {\n  queue.forEach(fn => {\n    fn();\n  });\n  queue = [];\n};\n\nconst errorFlush = () => {\n  errorQueue.forEach(fn => {\n    fn();\n  });\n  errorQueue = [];\n};\n\nconst debug = (): Record<string, unknown> => ({\n  wasmSourceUrl,\n  wasmLoadPromise,\n  queueLength: queue.length\n});\n\nexport { runAfterWasmLoaded, loadWasm, debug };\n","// The module cache\nvar __webpack_module_cache__ = {};\n\n// The require function\nfunction __webpack_require__(moduleId) {\n\t// Check if module is in cache\n\tvar cachedModule = __webpack_module_cache__[moduleId];\n\tif (cachedModule !== undefined) {\n\t\treturn cachedModule.exports;\n\t}\n\t// Create a new module (and put it into the cache)\n\tvar module = __webpack_module_cache__[moduleId] = {\n\t\t// no module.id needed\n\t\t// no module.loaded needed\n\t\texports: {}\n\t};\n\n\t// Execute the module function\n\t__webpack_modules__[moduleId].call(module.exports, module, module.exports, __webpack_require__);\n\n\t// Return the exports of the module\n\treturn module.exports;\n}\n\n","// define getter functions for harmony exports\n__webpack_require__.d = (exports, definition) => {\n\tfor(var key in definition) {\n\t\tif(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {\n\t\t\tObject.defineProperty(exports, key, { enumerable: true, get: definition[key] });\n\t\t}\n\t}\n};","__webpack_require__.g = (function() {\n\tif (typeof globalThis === 'object') return globalThis;\n\ttry {\n\t\treturn this || new Function('return this')();\n\t} catch (e) {\n\t\tif (typeof window === 'object') return window;\n\t}\n})();","__webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))","// define __esModule on exports\n__webpack_require__.r = (exports) => {\n\tif(typeof Symbol !== 'undefined' && Symbol.toStringTag) {\n\t\tObject.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });\n\t}\n\tObject.defineProperty(exports, '__esModule', { value: true });\n};","var scriptUrl;\nif (__webpack_require__.g.importScripts) scriptUrl = __webpack_require__.g.location + \"\";\nvar document = __webpack_require__.g.document;\nif (!scriptUrl && document) {\n\tif (document.currentScript)\n\t\tscriptUrl = document.currentScript.src\n\tif (!scriptUrl) {\n\t\tvar scripts = document.getElementsByTagName(\"script\");\n\t\tif(scripts.length) scriptUrl = scripts[scripts.length - 1].src\n\t}\n}\n// When supporting browsers where an automatic publicPath is not supported you must specify an output.publicPath manually via configuration\n// or pass an empty string (\"\") and set the __webpack_public_path__ variable from your code to use your own logic.\nif (!scriptUrl) throw new Error(\"Automatic publicPath is not supported in this browser\");\nscriptUrl = scriptUrl.replace(/#.*$/, \"\").replace(/\\?.*$/, \"\").replace(/\\/[^\\/]+$/, \"/\");\n__webpack_require__.p = scriptUrl;","// startup\n// Load entry module and return exports\n// This entry module is referenced by other modules so it can't be inlined\nvar __webpack_exports__ = __webpack_require__(480);\n"],"names":["wasm","heap","Array","fill","undefined","getObject","idx","push","heap_next","length","WASM_VECTOR_LEN","cachegetUint8Memory0","getUint8Memory0","buffer","memory","Uint8Array","cachedTextEncoder","TextEncoder","encodeString","encodeInto","arg","view","buf","encode","set","read","written","passStringToWasm0","malloc","realloc","ptr","subarray","len","mem","offset","code","charCodeAt","slice","cachegetInt32Memory0","getInt32Memory0","Int32Array","cachedTextDecoder","TextDecoder","ignoreBOM","fatal","getStringFromWasm0","decode","wasm_register_index","name","data","retptr","__wbindgen_add_to_stack_pointer","ptr0","__wbindgen_malloc","__wbindgen_realloc","len0","ptr1","passArray8ToWasm0","len1","r0","r1","__wbindgen_free","wasm_search","query","wasm_stork_version","async","init","input","URL","relativeUrl","publicPath","url","indexOf","window","location","protocol","host","getAbsoluteUrl","imports","wbg","__wbg_new_693216e109162396","obj","addHeapObject","Error","__wbg_stack_0ddaca5d1abfb52f","arg0","arg1","stack","__wbg_error_09919627ac0992f5","console","error","__wbindgen_object_drop_ref","ret","dropObject","takeObject","Request","fetch","instance","module","Response","WebAssembly","instantiateStreaming","e","headers","get","warn","bytes","arrayBuffer","instantiate","Instance","load","exports","__wbindgen_wasm_module","defaultConfig","showProgress","printIndexInfo","showScores","showCloseButton","minimumQueryLength","forceOverwrite","resultNoun","singular","plural","onQueryUpdate","onResultSelected","onResultsHidden","onInputCleared","transformResultUrl","overrides","configKeyDiff","difference","Object","keys","invalidKeys","JSON","stringify","output","assign","key","overrideVal","attributes","elem","document","createElement","classNames","setAttribute","join","reference","insertAdjacentElement","firstChild","removeChild","text","textNode","createTextNode","replaceChild","appendChild","container","elemBoundingBox","getBoundingClientRect","containerBoundingBox","bottom","top","config","_state","downloadProgress","results","totalResultCount","eventListenerFunctions","highlightedResult","resultsVisible","hoverSelectEnabled","setDownloadProgress","percentage","state","render","this","value","getCurrentMessage","domManager","getQuery","generateRenderConfig","message","progress","registerIndex","Promise","resolve","reject","indexInfo","parse","log","attachToDom","EntityDom","injestSearchData","total_hit_count","urlPrefix","url_prefix","map","r","urlSuffix","firstInternalAnnotations","excerpts","internal_annotations","filter","ia","annotationMap","entry","getSanitizedResults","result","title_highlight_ranges","excerpt","highlight_ranges","setDownloadError","performSearch","resolveSearch","Entity","hiddenInterfaceRenderState","entity","scrollAnchorPoint","selector","elementName","d","element","querySelector","elements","list","create","attribution","closeButton","removeEventListener","inputInputEvent","inputKeydownEvent","handleInputEvent","handleKeyDownEvent","addEventListener","innerHTML","add","focus","m","n","clearDom","clear","remove","classList","lastRenderState","getFakeProgress","style","width","opacity","setText","i","generateOptions","selected","listItem","resultToListItem","changeHighlightedResult","to","shouldScrollTo","preventDefault","selectResult","finally","options","previousValue","resolvedIdx","Math","max","min","targetForScrollTo","children","highlightedClassName","existsBeyondContainerBounds","scrollIntoView","behavior","block","inline","event","keyCode","target","entities","register","partialConfig","res","rej","fullConfig","calculateOverriddenConfig","loadIndexFromUrl","response","runAfterWasmLoaded","then","catch","entityIsReady","debug","entitiesCount","callbacks","XMLHttpRequest","status","loaded","total","responseType","open","send","initialize","wasmOverrideUrl","loadWasm","downloadIndex","validationError","validateIndexParams","attach","initPromise","downloadPromise","all","search","jsStorkVersion","wasmStorkVersion","insert","str","index","substr","charactersAlreadyAdded","range","beginningInsertion","endInsertion","beginning","end","template","highlight","title","score","content","firstElementChild","searchOutput","StorkError","html","trim","arr1","arr2","set1","Set","set2","diff","from","x","has","count","DEFAULT_WASM_URL","wasmSourceUrl","wasmLoadPromise","queue","errorQueue","overrideUrl","p","flush","errorFlush","fn","err","forEach","queueLength","__webpack_module_cache__","__webpack_require__","moduleId","cachedModule","__webpack_modules__","call","definition","o","defineProperty","enumerable","g","globalThis","Function","prop","prototype","hasOwnProperty","Symbol","toStringTag","scriptUrl","importScripts","currentScript","src","scripts","getElementsByTagName","replace","__webpack_exports__"],"sourceRoot":""}
default/index.yaml view
@@ -36,6 +36,9 @@     # Whether this node in the sidebar tree should remain collapsed by default     # (unless a route in it is active)     collapsed: true+  stork:+    # https://stork-search.net/docs/config-ref#frontmatter_handling+    frontmatter-handling: omit  pandoc:   # Rewrite the class specified in Pandoc's Div and Span nodes. You can specify the class using@@ -95,6 +98,7 @@     <link href="https://cdnjs.cloudflare.com/ajax/libs/prism/1.28.0/themes/prism-tomorrow.min.css" rel="stylesheet" />     <script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.28.0/prism.min.js"></script>     <script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.28.0/plugins/autoloader/prism-autoloader.min.js"></script>+    <style>code span.token.table{display:inline;}</style> <!-- Tailwind and Prism both use the .table class. This resets the display:table property set by Tailwind. https://github.com/srid/emanote/issues/320 -->   # Syntax highlighting using highlight.js   highlightjs: |     <!-- highlight.js -->
default/templates/components/stork/stork-search-head.tpl view
@@ -15,11 +15,13 @@ <script src="${ema:emanoteStaticLayerUrl}/stork/stork.js"></script> <ema:metadata>   <with var="template">-    <script data-emanote-base-url="${value:baseUrl}">+    <script id="emanote-stork" data-emanote-base-url="${value:baseUrl}">       window.emanote = {};       window.emanote.stork = {         searchShown: false,+        indexIsStale: false,         toggleSearch: function () {+          window.emanote.stork.refreshIndex();           document.getElementById('stork-search-container').classList.toggle('hidden');           window.emanote.stork.searchShown = document.body.classList.toggle('stork-overflow-hidden-important');           if (window.emanote.stork.searchShown) {@@ -32,14 +34,25 @@           window.emanote.stork.searchShown = false;         }, -        init: function () {+        getBaseUrl: function () {+          const baseUrl = document.getElementById("emanote-stork").getAttribute('data-emanote-base-url') || '/';+          return baseUrl;+        },++        registerIndex: function (options) {           const indexName = 'emanote-search'; // used to match input[data-stork] attribute value-          const baseUrl = document.currentScript.getAttribute('data-emanote-base-url') || '/';-          const indexUrl = baseUrl + '-/stork.st';+          const indexUrl = window.emanote.stork.getBaseUrl() + '-/stork.st';+          stork.register(+            indexName,+            indexUrl,+            options);+        },++        init: function () {           if (document.readyState !== 'complete') {             window.addEventListener('load', function () {-              stork.initialize(baseUrl + '_emanote-static/stork/stork.wasm');-              stork.register(indexName, indexUrl);+              stork.initialize(window.emanote.stork.getBaseUrl() + '_emanote-static/stork/stork.wasm');+              window.emanote.stork.registerIndex();             });              document.addEventListener('keydown', event => {@@ -52,10 +65,32 @@               }             });           } else {-            // Override existing on Ema's hot-reload-            stork.register(indexName, indexUrl, { forceOverwrite: true });+            // This section is called during Ema's hot reload.+            // +            // Mark the current index as stale, and refresh it *only when* the+            // user actually invokes search.+            // +            // We do not refresh the index *right away*, as that will cause+            // memory leaks in the browser. See+            // https://github.com/srid/emanote/issues/411#issuecomment-1402056235+            console.log("stork: Marking index as stale");+            window.emanote.stork.markIndexAsStale();           }+        },++        markIndexAsStale: function () {+          window.emanote.stork.indexIsStale = true;+        },++        refreshIndex: function () {+          if (window.emanote.stork.indexIsStale) {+            console.log("stork: Reloading index");+            window.emanote.stork.indexIsStale = false;+            // NOTE: This will leak memory. See the comment above.+            window.emanote.stork.registerIndex({ forceOverwrite: true });+          }         }+       };        window.emanote.stork.init();
emanote.cabal view
@@ -1,6 +1,6 @@ cabal-version:      2.4 name:               emanote-version:            1.0.0.0+version:            1.0.2.0 license:            AGPL-3.0-only copyright:          2022 Sridhar Ratnakumar maintainer:         srid@srid.ca@@ -11,7 +11,7 @@   Create beautiful websites -- such as personal webpage, blog, wiki, Zettelkasten, notebook, knowledge-base, documentation, etc. from future-proof plain-text notes and arbitrary data -- with live preview that updates in real-time.  -- A URL where users can report bugs.-bug-reports:        https://github.com/EmaApps/emanote/issues+bug-reports:        https://github.com/srid/emanote/issues extra-source-files:   CHANGELOG.md   LICENSE@@ -19,13 +19,13 @@  data-dir:           default data-files:-  _emanote-bin/compile-css   _emanote-live-server/**/*.css   _emanote-static/**/*.ttf   _emanote-static/*.css   _emanote-static/*.svg   _emanote-static/stork/flat.css   _emanote-static/stork/stork.js+  _emanote-static/stork/stork.js.map   _emanote-static/stork/stork.wasm   favicon.svg   index.yaml@@ -104,6 +104,7 @@     , containers     , data-default     , dependent-sum+    , deriving-aeson     , directory     , ema                    >=0.9     , filepath
src/Emanote.hs view
@@ -1,11 +1,10 @@ {-# LANGUAGE RecordWildCards #-} {-# OPTIONS_GHC -Wno-orphans #-} -module Emanote-  ( run,-    defaultEmanoteConfig,-  )-where+module Emanote (+  run,+  defaultEmanoteConfig,+) where  import Control.Monad.Logger (LogLevel (LevelError), runStderrLoggingT, runStdoutLoggingT) import Control.Monad.Logger.Extras (Logger (Logger), logToStderr, runLoggerLoggingT)@@ -13,13 +12,13 @@ import Data.Default (def) import Data.Dependent.Sum (DSum ((:=>))) import Data.Map.Strict qualified as Map-import Ema-  ( EmaSite (..),-    IsRoute (..),-    fromPrism_,-    runSiteWithCli,-    toPrism_,-  )+import Ema (+  EmaSite (..),+  IsRoute (..),+  fromPrism_,+  runSiteWithCli,+  toPrism_,+ ) import Ema.CLI qualified import Ema.Dynamic (Dynamic (Dynamic)) import Emanote.CLI qualified as CLI@@ -139,17 +138,17 @@ defaultEmanotePandocRenderers =   let blockRenderers =         PandocRenderers-          [ PF.embedInlineWikiLinkResolvingSplice, -- embedInlineWikiLinkResolvingSplice should be first to recognize inline Link elements first-            PF.urlResolvingSplice+          [ PF.embedInlineWikiLinkResolvingSplice -- embedInlineWikiLinkResolvingSplice should be first to recognize inline Link elements first+          , PF.urlResolvingSplice           ]-          [ PF.embedBlockWikiLinkResolvingSplice,-            PF.embedBlockRegularLinkResolvingSplice,-            PF.queryResolvingSplice+          [ PF.embedBlockWikiLinkResolvingSplice+          , PF.embedBlockRegularLinkResolvingSplice+          , PF.queryResolvingSplice           ]       inlineRenderers =         PandocRenderers-          [ PF.embedInlineWikiLinkResolvingSplice, -- embedInlineWikiLinkResolvingSplice should be first to recognize inline Link elements first-            PF.urlResolvingSplice+          [ PF.embedInlineWikiLinkResolvingSplice -- embedInlineWikiLinkResolvingSplice should be first to recognize inline Link elements first+          , PF.urlResolvingSplice           ]           mempty       linkInlineRenderers =
src/Emanote/CLI.hs view
@@ -1,13 +1,12 @@ {-# LANGUAGE ApplicativeDo #-} {-# LANGUAGE RecordWildCards #-} -module Emanote.CLI-  ( Cli (..),-    Cmd (..),-    parseCli,-    cliParser,-  )-where+module Emanote.CLI (+  Cli (..),+  Cmd (..),+  parseCli,+  cliParser,+) where  import Data.Text qualified as T import Data.Version (showVersion)@@ -18,9 +17,9 @@ import UnliftIO.Directory (getCurrentDirectory)  data Cli = Cli-  { layers :: NonEmpty FilePath,-    allowBrokenLinks :: Bool,-    cmd :: Cmd+  { layers :: NonEmpty FilePath+  , allowBrokenLinks :: Bool+  , cmd :: Cmd   }  data Cmd@@ -39,11 +38,11 @@     pathList defaultPath = do       option pathListReader $         mconcat-          [ long "layers",-            short 'L',-            metavar "LAYERS",-            value defaultPath,-            help "List of (semicolon delimited) notebook folders to 'union mount', with the left-side folders being overlaid on top of the right-side ones. The default layer is implicitly included at the end of this list."+          [ long "layers"+          , short 'L'+          , metavar "LAYERS"+          , value defaultPath+          , help "List of (semicolon delimited) notebook folders to 'union mount', with the left-side folders being overlaid on top of the right-side ones. The default layer is implicitly included at the end of this list."           ]     pathListReader :: ReadM (NonEmpty FilePath)     pathListReader =
src/Emanote/Model/Calendar.hs view
@@ -1,5 +1,6 @@--- | A primitive module to eventually pave way towards first-class "calendar"--- (daily notes, etc.) support in Emanote; either built-in or as plugin.+{- | A primitive module to eventually pave way towards first-class "calendar"+ (daily notes, etc.) support in Emanote; either built-in or as plugin.+-} module Emanote.Model.Calendar where  import Data.Time.Calendar (Day, fromGregorianValid)@@ -42,5 +43,10 @@       void $ M.string "-"       -- Day       day <- asInt =<< replicateM 2 M.digitChar+      -- Optional suffix (ignored)+      void $+        optional $ do+          void $ M.oneOf ['-', '_', ' ']+          void M.takeRest       maybe (fail "Not a date") pure $         fromGregorianValid year (fromInteger month) (fromInteger day)
src/Emanote/Model/Graph.hs view
@@ -41,9 +41,9 @@               parentLmlRoute model lmlR           folgezettelParents =             mconcat-              [ folgezettelBacklinks,-                folgezettelFrontlinks,-                folgezettelFolder+              [ folgezettelBacklinks+              , folgezettelFrontlinks+              , folgezettelFolder               ]       fmap catMaybes . forM folgezettelParents $ \parentR -> do         let parentModelR = R.ModelRoute_LML parentR@@ -71,10 +71,11 @@       Rel.RRTFound x -> Just x       _ -> Nothing --- | Return the route to parent folder (unless indexRoute is passed).------  This will return the existing note (.org or .md) if possible. Otherwise---  fallback to .md even if missing.+{- | Return the route to parent folder (unless indexRoute is passed).++  This will return the existing note (.org or .md) if possible. Otherwise+  fallback to .md even if missing.+-} parentLmlRoute :: Model -> R.LMLRoute -> Maybe R.LMLRoute parentLmlRoute model r = do   pr <- do
src/Emanote/Model/Link/Rel.hs view
@@ -21,25 +21,27 @@ import Text.Pandoc.Definition qualified as B import Text.Pandoc.LinkContext qualified as LC --- | A relation from one note to anywhere in the model.------ Target will remain unresolved in the `Rel`, and can be resolved at a latter--- time (eg: during rendering).+{- | A relation from one note to anywhere in the model.++ Target will remain unresolved in the `Rel`, and can be resolved at a latter+ time (eg: during rendering).+-} data Rel = Rel   { -- The note containing this relation-    _relFrom :: LMLRoute,-    -- The target of the relation (can be a note or anything)-    _relTo :: UnresolvedRelTarget,-    -- | The relation context in LML-    _relCtx :: [B.Block]+    _relFrom :: LMLRoute+  , -- The target of the relation (can be a note or anything)+    _relTo :: UnresolvedRelTarget+  , _relCtx :: [B.Block]+  -- ^ The relation context in LML   }   deriving stock (Eq, Ord, Show) --- | A link target that has not been resolved (using model) yet.------ Resolving this may or may not result in a resource in the model. The ADT--- constructors capture the different possible types of links the user is--- allowed to link to.+{- | A link target that has not been resolved (using model) yet.++ Resolving this may or may not result in a resource in the model. The ADT+ constructors capture the different possible types of links the user is+ allowed to link to.+-} data UnresolvedRelTarget   = URTWikiLink (WL.WikiLinkType, WL.WikiLink)   | URTResource ModelRoute@@ -79,9 +81,10 @@    in (URTWikiLink <$> toList wls)         <> [URTResource r] --- | Parse a relative URL string for later resolution.------ TODO: Need tests for this function.+{- | Parse a relative URL string for later resolution.++ TODO: Need tests for this function.+-} parseUnresolvedRelTarget :: Maybe (R.R 'R.Folder) -> [(Text, Text)] -> Text -> Maybe (UnresolvedRelTarget, Maybe WL.Anchor) parseUnresolvedRelTarget baseDir attrs url = do   (wlRes, manchor) <- WL.delineateLink attrs url@@ -124,9 +127,10 @@         x -> maybe [] (go 0) $ nonEmpty $ NEL.drop n x    in toString . T.intercalate "/" . maybe [] (reverse . go 0 . NEL.reverse) . nonEmpty . T.splitOn "/" . toText --- | An `UnresolvedRelTarget` that has been resolved.------ See @Model.Link.Resolve@ for actual resolution logic.+{- | An `UnresolvedRelTarget` that has been resolved.++ See @Model.Link.Resolve@ for actual resolution logic.+-} data ResolvedRelTarget a   = RRTMissing   | RRTAmbiguous (NonEmpty a)
src/Emanote/Model/Meta.hs view
@@ -1,9 +1,8 @@-module Emanote.Model.Meta-  ( lookupRouteMeta,-    getEffectiveRouteMeta,-    getEffectiveRouteMetaWith,-  )-where+module Emanote.Model.Meta (+  lookupRouteMeta,+  getEffectiveRouteMeta,+  getEffectiveRouteMetaWith,+) where  import Data.Aeson (FromJSON) import Data.Aeson qualified as Aeson@@ -21,8 +20,9 @@ lookupRouteMeta x k r =   SData.lookupAeson x k . getEffectiveRouteMeta r --- | Get the (final) metadata of a note at the given route, by merging it with--- the defaults specified in parent routes all the way upto index.yaml.+{- | Get the (final) metadata of a note at the given route, by merging it with+ the defaults specified in parent routes all the way upto index.yaml.+-} getEffectiveRouteMeta :: R.LMLRoute -> ModelT f -> Aeson.Value getEffectiveRouteMeta mr model =   let mNote = modelLookupNoteByRoute mr model@@ -30,7 +30,7 @@  getEffectiveRouteMetaWith :: Aeson.Value -> R.LMLRoute -> ModelT f -> Aeson.Value getEffectiveRouteMetaWith frontmatter mr model =-  let defaultFiles = R.routeInits @'R.Yaml (R.withLmlRoute coerce mr)+  let defaultFiles = R.routeInits @('R.Yaml) (R.withLmlRoute coerce mr)       defaults = flip mapMaybe (toList defaultFiles) $ \r -> do         v <- getYamlMeta r model         guard $ v /= Aeson.Null
src/Emanote/Model/Note.hs view
@@ -35,11 +35,11 @@ import Text.Pandoc.Walk qualified as W  data Note = Note-  { _noteRoute :: R.LMLRoute,-    _noteDoc :: Pandoc,-    _noteMeta :: Aeson.Value,-    _noteTitle :: Tit.Title,-    _noteErrors :: [Text]+  { _noteRoute :: R.LMLRoute+  , _noteDoc :: Pandoc+  , _noteMeta :: Aeson.Value+  , _noteTitle :: Tit.Title+  , _noteErrors :: [Text]   }   deriving stock (Eq, Ord, Show, Generic)   deriving anyclass (Aeson.ToJSON)@@ -50,18 +50,18 @@  type NoteIxs =   '[ -- Route to this note-     R.LMLRoute,-     -- Allowed ways to wiki-link to this note.-     WL.WikiLink,-     -- HTML route for this note-     R 'R.Html,-     -- Ancestor folder routes-     RAncestor,-     -- Parent folder-     R 'R.Folder,-     -- Tag-     HT.Tag,-     -- Alias route for this note. Can be "foo" or "foo/bar".+     R.LMLRoute+   , -- Allowed ways to wiki-link to this note.+     WL.WikiLink+   , -- HTML route for this note+     R 'R.Html+   , -- Ancestor folder routes+     RAncestor+   , -- Parent folder+     R 'R.Folder+   , -- Tag+     HT.Tag+   , -- Alias route for this note. Can be "foo" or "foo/bar".      NonEmpty Slug    ] @@ -107,7 +107,7 @@ noteSlug :: Note -> Maybe (NonEmpty Slug) noteSlug note = do   slugPath :: Text <- lookupMeta (one "slug") note-  fmap R.unRoute $ R.mkRouteFromFilePath @_ @'R.AnyExt $ toString slugPath+  fmap R.unRoute $ R.mkRouteFromFilePath @_ @('R.AnyExt) $ toString slugPath  lookupMeta :: Aeson.FromJSON a => NonEmpty Text -> Note -> Maybe a lookupMeta k =@@ -169,13 +169,13 @@ ancestorPlaceholderNote :: R.R 'Folder -> Note ancestorPlaceholderNote r =   let placeHolder =-        [ folderListingQuery,-          -- TODO: Ideally, we should use semantic tags, like <aside> (rather+        [ folderListingQuery+        , -- TODO: Ideally, we should use semantic tags, like <aside> (rather           -- than <div>), to render these non-relevant content.           B.Div (cls "emanote:placeholder-message") . one . B.Para $             [ B.Str-                "Note: To override the auto-generated content here, create a file named one of: ",-              -- TODO: or, .org+                "Note: To override the auto-generated content here, create a file named one of: "+            , -- TODO: or, .org               B.Span (cls "font-mono text-sm") $                 one $                   B.Str $@@ -196,9 +196,9 @@   mkEmptyNoteWith (R.defaultLmlRoute route404) $     one $       B.Para-        [ B.Str "No note has the URL ",-          B.Code B.nullAttr $ "/" <> urlPath,-          -- TODO: org+        [ B.Str "No note has the URL "+        , B.Code B.nullAttr $ "/" <> urlPath+        , -- TODO: org           B.Span (cls "font-mono text-sm") $             one $               B.Str $@@ -215,9 +215,9 @@ ambiguousNoteURL urlPath rs =   mkEmptyNoteWith (head rs) $     [ B.Para-        [ B.Str "The URL ",-          B.Code B.nullAttr $ toText urlPath,-          B.Str " is ambiguous, as more than one note (see list below) use it. To fix this, specify a different slug for these notes:"+        [ B.Str "The URL "+        , B.Code B.nullAttr $ toText urlPath+        , B.Str " is ambiguous, as more than one note (see list below) use it. To fix this, specify a different slug for these notes:"         ]     ]       <> one candidates@@ -226,8 +226,8 @@     candidates =       B.BulletList $         toList rs <&> \(R.lmlRouteCase -> r) ->-          [ B.Plain $ one $ B.Str "  ",-            B.Plain $ one $ B.Code B.nullAttr $ show r+          [ B.Plain $ one $ B.Str "  "+          , B.Plain $ one $ B.Code B.nullAttr $ show r           ]  mkEmptyNoteWith :: R.LMLRoute -> [B.Block] -> Note@@ -317,9 +317,9 @@       frontmatter         & AO.key "tags" % AO._Array           .~ ( fromList . fmap Aeson.toJSON $-                 ordNub $-                   SData.lookupAeson @[HT.Tag] mempty (one "tags") frontmatter-                     <> HT.inlineTagsInPandoc doc+                ordNub $+                  SData.lookupAeson @[HT.Tag] mempty (one "tags") frontmatter+                    <> HT.inlineTagsInPandoc doc              )     addDescriptionFromBody =       overrideAesonText ("page" :| ["description"]) $ \case
src/Emanote/Model/SData.hs view
@@ -14,12 +14,13 @@ import Optics.TH (makeLenses) import Relude --- | `S` for "structured". Refers to a per-route data file represented by Aeson--- value.  Example: /foo/bar.yaml file+{- | `S` for "structured". Refers to a per-route data file represented by Aeson+ value.  Example: /foo/bar.yaml file+-} data SData = SData-  { _sdataValue :: Aeson.Value,-    -- | Location of this data file-    _sdataRoute :: R.R 'R.Yaml+  { _sdataValue :: Aeson.Value+  , _sdataRoute :: R.R 'R.Yaml+  -- ^ Location of this data file   }   deriving stock (Eq, Ord, Data, Show, Generic)   deriving anyclass (Aeson.ToJSON)
src/Emanote/Model/StaticFile.hs view
@@ -12,10 +12,10 @@ import Relude  data StaticFile = StaticFile-  { _staticFileRoute :: R.R 'R.AnyExt,-    _staticFilePath :: FilePath,-    -- | Indicates that this file was updated no latter than the given time.-    _staticFileTime :: UTCTime+  { _staticFileRoute :: R.R 'R.AnyExt+  , _staticFilePath :: FilePath+  , _staticFileTime :: UTCTime+  -- ^ Indicates that this file was updated no latter than the given time.   }   deriving stock (Eq, Ord, Show, Generic)   deriving anyclass (Aeson.ToJSON)
src/Emanote/Model/Stork.hs view
@@ -1,12 +1,19 @@-module Emanote.Model.Stork-  ( renderStorkIndex,-  )-where+module Emanote.Model.Stork (+  renderStorkIndex,+) where  import Control.Monad.Logger (MonadLoggerIO)+import Data.Default (Default (def)) import Data.IxSet.Typed qualified as Ix+import Emanote.Model.Meta (lookupRouteMeta) import Emanote.Model.Note qualified as N-import Emanote.Model.Stork.Index (File (File), Input (Input), readOrBuildStorkIndex)+import Emanote.Model.Stork.Index (+  Config (Config),+  File (File),+  Handling,+  Input (Input),+  readOrBuildStorkIndex,+ ) import Emanote.Model.Title qualified as Tit import Emanote.Model.Type (Model) import Emanote.Model.Type qualified as M@@ -19,7 +26,8 @@  renderStorkIndex :: (MonadIO m, MonadLoggerIO m) => Model -> m LByteString renderStorkIndex model = do-  readOrBuildStorkIndex (model ^. M.modelStorkIndex) (Input $ storkFiles model)+  let config = Config $ Input (storkFiles model) (frontmatterHandling model)+  readOrBuildStorkIndex (model ^. M.modelStorkIndex) config  storkFiles :: Model -> [File] storkFiles model =@@ -29,3 +37,8 @@           ((baseDir </>) $ R.withLmlRoute R.encodeRoute $ note ^. N.noteRoute)           (SR.siteRouteUrl model $ SR.lmlSiteRoute $ note ^. N.noteRoute)           (Tit.toPlain $ note ^. N.noteTitle)++frontmatterHandling :: Model -> Handling+frontmatterHandling model =+  let indexRoute = M.modelIndexRoute model+   in lookupRouteMeta def ("template" :| ["stork", "frontmatter-handling"]) indexRoute model
src/Emanote/Model/Stork/Index.hs view
@@ -1,25 +1,28 @@ {-# LANGUAGE BangPatterns #-} {-# LANGUAGE TemplateHaskell #-} -module Emanote.Model.Stork.Index-  ( IndexVar,-    newIndex,-    clearStorkIndex,-    readOrBuildStorkIndex,-    File (File),-    Input (Input),-  )-where+module Emanote.Model.Stork.Index (+  IndexVar,+  newIndex,+  clearStorkIndex,+  readOrBuildStorkIndex,+  File (File),+  Input (Input),+  Config (Config),+  Handling,+) where  import Control.Monad.Logger (MonadLoggerIO)+import Data.Default (Default (..)) import Data.Text qualified as T import Data.Time (NominalDiffTime, diffUTCTime, getCurrentTime)+import Deriving.Aeson import Emanote.Prelude (log, logD, logW) import Numeric (showGFloat) import Relude import System.Process.ByteString (readProcessWithExitCode) import System.Which (staticWhich)-import Toml (TomlCodec, encode, list, string, text, (.=))+import Toml (Key, TomlCodec, diwrap, encode, list, string, table, text, textBy, (.=))  -- | In-memory Stork index tracked in a @TVar@ newtype IndexVar = IndexVar (TVar (Maybe LByteString))@@ -31,8 +34,8 @@ clearStorkIndex :: (MonadIO m) => IndexVar -> m () clearStorkIndex (IndexVar var) = atomically $ writeTVar var mempty -readOrBuildStorkIndex :: (MonadIO m, MonadLoggerIO m) => IndexVar -> Input -> m LByteString-readOrBuildStorkIndex (IndexVar indexVar) input = do+readOrBuildStorkIndex :: (MonadIO m, MonadLoggerIO m) => IndexVar -> Config -> m LByteString+readOrBuildStorkIndex (IndexVar indexVar) config = do   readTVarIO indexVar >>= \case     Just index -> do       logD "STORK: Returning cached search index"@@ -41,7 +44,7 @@       -- TODO: What if there are concurrent reads? We probably need a lock.       -- And we want to encapsulate this whole thing.       logW "STORK: Generating search index (this may be expensive)"-      (diff, !index) <- timeIt $ runStork input+      (diff, !index) <- timeIt $ runStork config       log $ toText $ "STORK: Done generating search index in " <> showGFloat (Just 2) diff "" <> " seconds"       atomically $ modifyTVar' indexVar $ \_ -> Just index       pure index@@ -57,9 +60,9 @@ storkBin :: FilePath storkBin = $(staticWhich "stork") -runStork :: MonadIO m => Input -> m LByteString-runStork input = do-  let storkToml = handleTomlandBug $ Toml.encode inputCodec input+runStork :: MonadIO m => Config -> m LByteString+runStork config = do+  let storkToml = handleTomlandBug $ Toml.encode configCodec config   (_, !index, _) <-     liftIO $       readProcessWithExitCode@@ -72,33 +75,79 @@   where     handleTomlandBug =       -- HACK: Deal with tomland's bug.-      -- https://github.com/EmaApps/emanote/issues/336+      -- https://github.com/srid/emanote/issues/336       -- https://github.com/kowainik/tomland/issues/408       --       -- This could be problematic if the user literally uses \\U in their note       -- title (but why would they?)       T.replace "\\\\U" "\\U" -newtype Input = Input+newtype Config = Config+  { configInput :: Input+  }+  deriving stock (Eq, Show)++data Input = Input   { inputFiles :: [File]+  , inputFrontmatterHandling :: Handling   }   deriving stock (Eq, Show)  data File = File-  { filePath :: FilePath,-    fileUrl :: Text,-    fileTitle :: Text+  { filePath :: FilePath+  , fileUrl :: Text+  , fileTitle :: Text   }   deriving stock (Eq, Show) -fileCodec :: TomlCodec File-fileCodec =-  File-    <$> Toml.string "path" .= filePath-    <*> Toml.text "url" .= fileUrl-    <*> Toml.text "title" .= fileTitle+data Handling+  = Handling_Ignore+  | Handling_Omit+  | Handling_Parse+  deriving stock (Eq, Show, Generic)+  deriving+    (FromJSON)+    via CustomJSON+          '[ ConstructorTagModifier '[StripPrefix "Handling_", CamelToSnake]+           ]+          Handling -inputCodec :: TomlCodec Input-inputCodec =-  Input-    <$> Toml.list fileCodec "input.files" .= inputFiles+instance Default Handling where+  def = Handling_Omit++configCodec :: TomlCodec Config+configCodec =+  Config+    <$> Toml.table inputCodec "input"+      .= configInput+  where+    inputCodec :: TomlCodec Input+    inputCodec =+      Input+        <$> Toml.list fileCodec "files"+          .= inputFiles+        <*> Toml.diwrap (handlingCodec "frontmatter_handling")+          .= inputFrontmatterHandling+    fileCodec :: TomlCodec File+    fileCodec =+      File+        <$> Toml.string "path"+          .= filePath+        <*> Toml.text "url"+          .= fileUrl+        <*> Toml.text "title"+          .= fileTitle+    handlingCodec :: Toml.Key -> TomlCodec Handling+    handlingCodec = textBy showHandling parseHandling+      where+        showHandling :: Handling -> Text+        showHandling handling = case handling of+          Handling_Ignore -> "Ignore"+          Handling_Omit -> "Omit"+          Handling_Parse -> "Parse"+        parseHandling :: Text -> Either Text Handling+        parseHandling handling = case handling of+          "Ignore" -> Right Handling_Ignore+          "Omit" -> Right Handling_Omit+          "Parse" -> Right Handling_Parse+          other -> Left $ "Unsupported value for frontmatter handling: " <> other
src/Emanote/Model/Task.hs view
@@ -16,12 +16,12 @@ import Text.Pandoc.Builder qualified as B  data Task = Task-  { _taskRoute :: R.LMLRoute,-    -- Index of this task within the containing note. Used to sort tasks by+  { _taskRoute :: R.LMLRoute+  , -- Index of this task within the containing note. Used to sort tasks by     -- their original order of appearance in the Markdown file.-    _taskNum :: Word,-    _taskDescription :: [B.Inline],-    _taskChecked :: Bool+    _taskNum :: Word+  , _taskDescription :: [B.Inline]+  , _taskChecked :: Bool   }   deriving stock (Eq, Show, Generic)   deriving anyclass (Aeson.ToJSON)
src/Emanote/Model/Title.hs view
@@ -1,19 +1,18 @@ {-# LANGUAGE DeriveAnyClass #-} -module Emanote.Model.Title-  ( Title,+module Emanote.Model.Title (+  Title, -    -- * Title conversion-    fromRoute,-    fromInlines,-    toInlines,+  -- * Title conversion+  fromRoute,+  fromInlines,+  toInlines, -    -- * Rendering a Title-    titleSplice,-    titleSpliceNoHtml,-    toPlain,-  )-where+  -- * Rendering a Title+  titleSplice,+  titleSpliceNoHtml,+  toPlain,+) where  import Commonmark.Extensions.WikiLink (plainify) import Data.Aeson (ToJSON)
src/Emanote/Model/Type.hs view
@@ -18,16 +18,16 @@ import Ema.CLI qualified import Emanote.Model.Link.Rel (IxRel) import Emanote.Model.Link.Rel qualified as Rel-import Emanote.Model.Note-  ( IxNote,-    Note,-  )+import Emanote.Model.Note (+  IxNote,+  Note,+ ) import Emanote.Model.Note qualified as N import Emanote.Model.SData (IxSData, SData, sdataRoute)-import Emanote.Model.StaticFile-  ( IxStaticFile,-    StaticFile (StaticFile),-  )+import Emanote.Model.StaticFile (+  IxStaticFile,+  StaticFile (StaticFile),+ ) import Emanote.Model.Stork.Index qualified as Stork import Emanote.Model.Task (IxTask) import Emanote.Model.Task qualified as Task@@ -49,31 +49,32 @@   deriving stock (Eq, Show)  data ModelT encF = Model-  { _modelStatus :: Status,-    _modelLayers :: Set Loc,-    _modelEmaCLIAction :: Some Ema.CLI.Action,-    _modelRoutePrism :: encF (Prism' FilePath SiteRoute),-    -- | Dictates how exactly to render `Pandoc` to Heist nodes.-    _modelPandocRenderers :: EmanotePandocRenderers Model LMLRoute,-    _modelCompileTailwind :: Bool,-    -- | An unique ID for this process's model. ID changes across processes.-    _modelInstanceID :: UUID,-    _modelNotes :: IxNote,-    _modelRels :: IxRel,-    _modelSData :: IxSData,-    _modelStaticFiles :: IxStaticFile,-    _modelTasks :: IxTask,-    _modelNav :: [Tree Slug],-    _modelHeistTemplate :: TemplateState,-    _modelStorkIndex :: Stork.IndexVar+  { _modelStatus :: Status+  , _modelLayers :: Set Loc+  , _modelEmaCLIAction :: Some Ema.CLI.Action+  , _modelRoutePrism :: encF (Prism' FilePath SiteRoute)+  , _modelPandocRenderers :: EmanotePandocRenderers Model LMLRoute+  -- ^ Dictates how exactly to render `Pandoc` to Heist nodes.+  , _modelCompileTailwind :: Bool+  , _modelInstanceID :: UUID+  -- ^ An unique ID for this process's model. ID changes across processes.+  , _modelNotes :: IxNote+  , _modelRels :: IxRel+  , _modelSData :: IxSData+  , _modelStaticFiles :: IxStaticFile+  , _modelTasks :: IxTask+  , _modelNav :: [Tree Slug]+  , _modelHeistTemplate :: TemplateState+  , _modelStorkIndex :: Stork.IndexVar   }   deriving stock (Generic)  type Model = ModelT Identity --- | A bare version of `Model` that is managed by the Ema app.------ The only difference is that this one has no `RouteEncoder`.+{- | A bare version of `Model` that is managed by the Ema app.++ The only difference is that this one has no `RouteEncoder`.+-} type ModelEma = ModelT (Const ())  deriving stock instance Generic ModelEma@@ -108,9 +109,9 @@ modelInsertNote note =   modelNotes     %~ ( Ix.updateIx r note-           -- Insert folder placeholder automatically for ancestor paths-           >>> injectAncestors (N.noteAncestors note)-           >>> dropRedundantAncestor r+          -- Insert folder placeholder automatically for ancestor paths+          >>> injectAncestors (N.noteAncestors note)+          >>> dropRedundantAncestor r        )     >>> modelRels       %~ updateIxMulti r (Rel.noteRels note)@@ -121,8 +122,9 @@   where     r = note ^. N.noteRoute --- | If a placeholder route was added already, but the newly added note is a--- non-Markdown, removce that markdown placeholder route.+{- | If a placeholder route was added already, but the newly added note is a+ non-Markdown, removce that markdown placeholder route.+-} dropRedundantAncestor :: LMLRoute -> IxNote -> IxNote dropRedundantAncestor recentNoteRoute ns =   case recentNoteRoute of@@ -169,7 +171,7 @@   model     & modelNotes       %~ ( Ix.deleteIx k-             >>> restoreAncestor (N.RAncestor <$> mFolderR)+            >>> restoreAncestor (N.RAncestor <$> mFolderR)          )     & modelRels       %~ deleteIxMulti k@@ -281,9 +283,10 @@       guard $ not $ null errs       pure (note ^. N.noteRoute, errs) --- | Return the most suitable index LML route------  If index.org exist, use that. Otherwise, fallback to index.md.+{- | Return the most suitable index LML route++  If index.org exist, use that. Otherwise, fallback to index.md.+-} modelIndexRoute :: ModelT f -> LMLRoute modelIndexRoute model = do   resolveLmlRoute model R.indexRoute@@ -298,7 +301,7 @@   -- TODO: Refactor using `[minBound..maxBound] :: [LML]`   note <-     asum-      [ N.lookupNotesByRoute (R.LMLRoute_Org $ coerce r) notes,-        N.lookupNotesByRoute (R.LMLRoute_Md $ coerce r) notes+      [ N.lookupNotesByRoute (R.LMLRoute_Org $ coerce r) notes+      , N.lookupNotesByRoute (R.LMLRoute_Md $ coerce r) notes       ]   pure $ note ^. N.noteRoute
src/Emanote/Pandoc/BuiltinFilters.hs view
@@ -1,8 +1,7 @@-module Emanote.Pandoc.BuiltinFilters-  ( prepareNoteDoc,-    preparePandoc,-  )-where+module Emanote.Pandoc.BuiltinFilters (+  prepareNoteDoc,+  preparePandoc,+) where  import Emanote.Model.Note qualified as N import Emanote.Pandoc.ExternalLink (setExternalLinkIcon)
src/Emanote/Pandoc/ExternalLink.hs view
@@ -1,7 +1,6 @@-module Emanote.Pandoc.ExternalLink-  ( setExternalLinkIcon,-  )-where+module Emanote.Pandoc.ExternalLink (+  setExternalLinkIcon,+) where  import Relude import Text.Pandoc.Definition qualified as B
src/Emanote/Pandoc/Link.hs view
@@ -4,9 +4,10 @@ import Relude import Text.Pandoc.Definition qualified as B --- | A Pandoc inline node that refers to something else.------ There are, currently, only two possible nodes: link & image.+{- | A Pandoc inline node that refers to something else.++ There are, currently, only two possible nodes: link & image.+-} data InlineRef   = InlineLink   | InlineImage@@ -21,8 +22,9 @@   _ ->     Nothing --- | Given an inline that is known to be an InlineRef, reconstruct and return--- its orginal Markdown source.+{- | Given an inline that is known to be an InlineRef, reconstruct and return+ its orginal Markdown source.+-} unParseLink :: HasCallStack => B.Inline -> Text unParseLink inl =   case WL.wikiLinkInlineRendered inl of
src/Emanote/Pandoc/Markdown/Parser.hs view
@@ -1,7 +1,6 @@-module Emanote.Pandoc.Markdown.Parser-  ( parseMarkdown,-  )-where+module Emanote.Pandoc.Markdown.Parser (+  parseMarkdown,+) where  import Commonmark qualified as CM import Commonmark.Extensions qualified as CE@@ -31,17 +30,17 @@   where     baseExtsSansPipeTable =       mconcat-        [ CE.fancyListSpec,-          CE.footnoteSpec,-          CE.mathSpec,-          CE.smartPunctuationSpec,-          CE.definitionListSpec,-          CE.attributesSpec,-          CE.rawAttributeSpec,-          CE.fencedDivSpec,-          CE.bracketedSpanSpec,-          CE.autolinkSpec,-          CM.defaultSyntaxSpec+        [ CE.fancyListSpec+        , CE.footnoteSpec+        , CE.mathSpec+        , CE.smartPunctuationSpec+        , CE.definitionListSpec+        , CE.attributesSpec+        , CE.rawAttributeSpec+        , CE.fencedDivSpec+        , CE.bracketedSpanSpec+        , CE.autolinkSpec+        , CM.defaultSyntaxSpec         ]     gfmExtensionsSansPipeTable =       CE.emojiSpec
src/Emanote/Pandoc/Markdown/Syntax/HashTag.hs view
@@ -1,17 +1,16 @@-module Emanote.Pandoc.Markdown.Syntax.HashTag-  ( hashTagSpec,-    inlineTagsInPandoc,-    getTagFromInline,-    TT.Tag (..),-    TT.TagPattern (..),-    TT.TagNode (..),-    TT.mkTagPattern,-    TT.tagMatch,-    TT.constructTag,-    TT.deconstructTag,-    TT.tagTree,-  )-where+module Emanote.Pandoc.Markdown.Syntax.HashTag (+  hashTagSpec,+  inlineTagsInPandoc,+  getTagFromInline,+  TT.Tag (..),+  TT.TagPattern (..),+  TT.TagNode (..),+  TT.mkTagPattern,+  TT.tagMatch,+  TT.constructTag,+  TT.deconstructTag,+  TT.tagTree,+) where  import Commonmark (TokType (..)) import Commonmark qualified as CM@@ -42,12 +41,12 @@ instance HasHashTag (CP.Cm b B.Inlines) where   hashTag (TT.Tag tag) =     let attrs =-          [ ("title", "Tag"),-            (tagDataAttr, tag)+          [ ("title", "Tag")+          , (tagDataAttr, tag)           ]         classes =-          [ "emanote:inline-tag",-            -- This must be placed *after* the class above, to allow the user to+          [ "emanote:inline-tag"+          , -- This must be placed *after* the class above, to allow the user to             -- override generic styles (of the class above)             "emanote:inline-tag:" <> tag           ]
src/Emanote/Pandoc/Markdown/Syntax/Highlight.hs view
@@ -1,7 +1,6 @@-module Emanote.Pandoc.Markdown.Syntax.Highlight-  ( highlightSpec,-  )-where+module Emanote.Pandoc.Markdown.Syntax.Highlight (+  highlightSpec,+) where  import Commonmark qualified as CM import Commonmark.Inlines qualified as CM
src/Emanote/Pandoc/Renderer.hs view
@@ -1,23 +1,23 @@ {-# LANGUAGE RecordWildCards #-} --- | Types for custom render extensions to Pandoc AST nodes.------ Note that unlike Pandoc *filters* (which operate on entire document), these--- are modeled based on Text.Pandoc.Walk, ie. fine-grained on individual inline--- and block processing. We do this only so as to render a specific node during--- recursion (cf. `rpBlock` and `rpInline` in Render.hs).------ So we expect the extensions to be in Haskell, however external script may be--- supported using a traditional whole-AST extension API.-module Emanote.Pandoc.Renderer-  ( PandocRenderers (PandocRenderers),-    PandocInlineRenderer,-    PandocBlockRenderer,-    mkRenderCtxWithPandocRenderers,-    EmanotePandocRenderers (..),-  )-where+{- | Types for custom render extensions to Pandoc AST nodes. + Note that unlike Pandoc *filters* (which operate on entire document), these+ are modeled based on Text.Pandoc.Walk, ie. fine-grained on individual inline+ and block processing. We do this only so as to render a specific node during+ recursion (cf. `rpBlock` and `rpInline` in Render.hs).++ So we expect the extensions to be in Haskell, however external script may be+ supported using a traditional whole-AST extension API.+-}+module Emanote.Pandoc.Renderer (+  PandocRenderers (PandocRenderers),+  PandocInlineRenderer,+  PandocBlockRenderer,+  mkRenderCtxWithPandocRenderers,+  EmanotePandocRenderers (..),+) where+ import Heist (HeistT) import Heist.Extra.Splices.Pandoc qualified as Splices import Heist.Extra.Splices.Pandoc.Ctx qualified as Splices@@ -39,8 +39,8 @@ type PandocBlockRenderer model route = PandocRenderF model route B.Block  data PandocRenderers model route = PandocRenderers-  { pandocInlineRenderers :: [PandocInlineRenderer model route],-    pandocBlockRenderers :: [PandocBlockRenderer model route]+  { pandocInlineRenderers :: [PandocInlineRenderer model route]+  , pandocBlockRenderers :: [PandocBlockRenderer model route]   }  mkRenderCtxWithPandocRenderers ::@@ -66,12 +66,12 @@     )  data EmanotePandocRenderers a r = EmanotePandocRenderers-  { blockRenderers :: PandocRenderers a r,-    -- | Like `blockRenderers` but for use in inline contexts.-    ---    -- Backlinks and titles constitute an example of inline context, where we don't-    -- care about block elements.-    inlineRenderers :: PandocRenderers a r,-    -- | Like `inlineRenderers` but suitable for use inside links (<a> tags).-    linkInlineRenderers :: PandocRenderers a r+  { blockRenderers :: PandocRenderers a r+  , inlineRenderers :: PandocRenderers a r+  -- ^ Like `blockRenderers` but for use in inline contexts.+  --+  -- Backlinks and titles constitute an example of inline context, where we don't+  -- care about block elements.+  , linkInlineRenderers :: PandocRenderers a r+  -- ^ Like `inlineRenderers` but suitable for use inside links (<a> tags).   }
src/Emanote/Pandoc/Renderer/Embed.hs view
@@ -93,18 +93,18 @@  imageExts :: [Text] imageExts =-  [ ".jpg",-    ".jpeg",-    ".png",-    ".svg",-    ".gif",-    ".bmp",-    ".webp"+  [ ".jpg"+  , ".jpeg"+  , ".png"+  , ".svg"+  , ".gif"+  , ".bmp"+  , ".webp"   ]  videoExts :: [Text] videoExts =-  [ ".mp4",-    ".webm",-    ".ogv"+  [ ".mp4"+  , ".webm"+  , ".ogv"   ]
src/Emanote/Pandoc/Renderer/Query.hs view
@@ -1,8 +1,7 @@-module Emanote.Pandoc.Renderer.Query-  ( queryResolvingSplice,-    noteSpliceMap,-  )-where+module Emanote.Pandoc.Renderer.Query (+  queryResolvingSplice,+  noteSpliceMap,+) where  import Data.List qualified as List import Data.Map.Syntax ((##))
src/Emanote/Pandoc/Renderer/Url.hs view
@@ -1,9 +1,8 @@-module Emanote.Pandoc.Renderer.Url-  ( urlResolvingSplice,-    plainifyWikiLinkSplice,-    renderSomeInlineRefWith,-  )-where+module Emanote.Pandoc.Renderer.Url (+  urlResolvingSplice,+  plainifyWikiLinkSplice,+  renderSomeInlineRefWith,+) where  import Commonmark.Extensions.WikiLink qualified as WL import Data.Text qualified as T@@ -72,8 +71,8 @@             ctx             ( tooltip                 "Link is broken"-                [ B.Strikeout $ one $ B.Str $ Link.unParseLink origInl,-                  B.Str "❌"+                [ B.Strikeout $ one $ B.Str $ Link.unParseLink origInl+                , B.Str "❌"                 ]             )         details <-@@ -133,8 +132,8 @@   ([B.Inline], Text) ->   ([B.Inline], (Text, Bool)) replaceLinkNodeWithRoute model r (inner, url) =-  ( inlinesWithWikiLinksPlainified $ nonEmptyLinkInlines model url (Just r) inner,-    let linkUrl = SR.siteRouteUrl model r+  ( inlinesWithWikiLinksPlainified $ nonEmptyLinkInlines model url (Just r) inner+  , let linkUrl = SR.siteRouteUrl model r      in (linkUrl, "?" `T.isInfixOf` linkUrl)   )   where
src/Emanote/Prelude.hs view
@@ -1,7 +1,8 @@ {-# LANGUAGE DeriveAnyClass #-} --- | We use `relude` as our prelude; any extra Prelude-like functionality is put--- here.+{- | We use `relude` as our prelude; any extra Prelude-like functionality is put+ here.+-} module Emanote.Prelude where  import Control.Monad.Logger (MonadLogger, logDebugNS, logErrorNS, logInfoNS, logWarnNS)
src/Emanote/Route/Ext.hs view
@@ -27,18 +27,20 @@  deriving stock instance Ord a => Ord (FileType a) --- | A lightweight markup language------ https://en.wikipedia.org/wiki/Lightweight_markup_language------ This type exists simply because we may support more formats (eg: org-mode) in--- the future.+{- | A lightweight markup language++ https://en.wikipedia.org/wiki/Lightweight_markup_language++ This type exists simply because we may support more formats (eg: org-mode) in+ the future.+-} data LML = Md | Org   deriving stock (Generic, Eq, Ord, Typeable, Data, Enum, Bounded)   deriving anyclass (ToJSON) --- | The `HasExt` class's responsibility is to allow dealing with basepath sans--- extension (and vice-versa).+{- | The `HasExt` class's responsibility is to allow dealing with basepath sans+ extension (and vice-versa).+-} class HasExt (ext :: FileType a) where   fileType :: FileType a @@ -78,8 +80,9 @@   withExt = id   withoutKnownExt = pure --- | The AnyExt instance ignores explicitly dealing with extensions, expecting--- the user to explicitly encode the extension in their value tpye.+{- | The AnyExt instance ignores explicitly dealing with extensions, expecting+ the user to explicitly encode the extension in their value tpye.+-} instance HasExt 'AnyExt where   fileType = AnyExt   withExt = id
src/Emanote/Route/ModelRoute.hs view
@@ -1,27 +1,27 @@ {-# LANGUAGE DeriveAnyClass #-} --- | Route types representing the resources in our `Model`.------ See also: `Emanote.Route.SiteRoute`.-module Emanote.Route.ModelRoute-  ( -- Some route in a generated site-    ModelRoute (..),-    modelRouteCase,-    mkModelRouteFromFilePath,-    -- Only LML routes-    LMLRoute (..),-    defaultLmlRoute,-    possibleLmlRoutes,-    lmlRouteCase,-    withLmlRoute,-    mkLMLRouteFromFilePath,-    mkLMLRouteFromKnownFilePath,-    isMdRoute,-    -- Static file routes-    StaticFileRoute,-  )-where+{- | Route types representing the resources in our `Model`. + See also: `Emanote.Route.SiteRoute`.+-}+module Emanote.Route.ModelRoute (+  -- Some route in a generated site+  ModelRoute (..),+  modelRouteCase,+  mkModelRouteFromFilePath,+  -- Only LML routes+  LMLRoute (..),+  defaultLmlRoute,+  possibleLmlRoutes,+  lmlRouteCase,+  withLmlRoute,+  mkLMLRouteFromFilePath,+  mkLMLRouteFromKnownFilePath,+  isMdRoute,+  -- Static file routes+  StaticFileRoute,+) where+ import Data.Aeson.Types (ToJSON) import Emanote.Route.Ext (FileType (AnyExt, LMLType), HasExt, LML (Md, Org)) import Emanote.Route.R (R)@@ -50,8 +50,8 @@  possibleLmlRoutes :: R (ext :: FileType a) -> [LMLRoute] possibleLmlRoutes r =-  [ LMLRoute_Md (coerce r),-    LMLRoute_Org (coerce r)+  [ LMLRoute_Md (coerce r)+  , LMLRoute_Org (coerce r)   ]  lmlRouteCase ::@@ -86,8 +86,9 @@   mkLMLRouteFromKnownFilePath Md fp     <|> mkLMLRouteFromKnownFilePath Org fp --- | Like `mkLMLRouteFromFilePath`, but when the file extension is known ahead--- to be of `lmlType`.+{- | Like `mkLMLRouteFromFilePath`, but when the file extension is known ahead+ to be of `lmlType`.+-} mkLMLRouteFromKnownFilePath :: LML -> FilePath -> Maybe LMLRoute mkLMLRouteFromKnownFilePath lmlType fp =   case lmlType of
src/Emanote/Route/R.hs view
@@ -11,8 +11,9 @@ import System.FilePath (splitPath) import Text.Show qualified (Show (show)) --- | Represents the relative path to some file (or its isomporphic URL--- represetation).+{- | Represents the relative path to some file (or its isomporphic URL+ represetation).+-} newtype R (ext :: FileType a) = R {unRoute :: NonEmpty Slug}   deriving stock (Eq, Ord, Typeable, Data) @@ -106,4 +107,4 @@  decodeAnyRoute :: FilePath -> Maybe (R 'AnyExt) decodeAnyRoute =-  mkRouteFromFilePath @_ @'AnyExt+  mkRouteFromFilePath @_ @('AnyExt)
src/Emanote/Route/SiteRoute.hs view
@@ -1,15 +1,14 @@-module Emanote.Route.SiteRoute-  ( SiteRoute (..),-    VirtualRoute (..),-    ResourceRoute (..),-    decodeVirtualRoute,-    noteFileSiteRoute,-    staticFileSiteRoute,-    lmlSiteRoute,-    siteRouteUrl,-    siteRouteUrlStatic,-  )-where+module Emanote.Route.SiteRoute (+  SiteRoute (..),+  VirtualRoute (..),+  ResourceRoute (..),+  decodeVirtualRoute,+  noteFileSiteRoute,+  staticFileSiteRoute,+  lmlSiteRoute,+  siteRouteUrl,+  siteRouteUrlStatic,+) where  import Emanote.Route.SiteRoute.Class import Emanote.Route.SiteRoute.Type
src/Emanote/Route/SiteRoute/Class.hs view
@@ -1,20 +1,19 @@-module Emanote.Route.SiteRoute.Class-  ( decodeVirtualRoute,-    noteFileSiteRoute,-    staticFileSiteRoute,-    lmlSiteRoute,-    indexRoute,-    tagIndexRoute,-    taskIndexRoute,-    siteRouteUrl,-    siteRouteUrlStatic,-    urlStrategySuffix,+module Emanote.Route.SiteRoute.Class (+  decodeVirtualRoute,+  noteFileSiteRoute,+  staticFileSiteRoute,+  lmlSiteRoute,+  indexRoute,+  tagIndexRoute,+  taskIndexRoute,+  siteRouteUrl,+  siteRouteUrlStatic,+  urlStrategySuffix, -    -- * Ema stuff-    emanoteRouteEncoder,-    emanoteGeneratableRoutes,-  )-where+  -- * Ema stuff+  emanoteRouteEncoder,+  emanoteGeneratableRoutes,+) where  import Data.IxSet.Typed qualified as Ix import Data.List.NonEmpty qualified as NE
src/Emanote/Route/SiteRoute/Type.hs view
@@ -1,14 +1,13 @@ {-# LANGUAGE DeriveAnyClass #-} -module Emanote.Route.SiteRoute.Type-  ( SiteRoute (..),-    VirtualRoute (..),-    ResourceRoute (..),-    decodeVirtualRoute,-    encodeVirtualRoute,-    encodeTagIndexR,-  )-where+module Emanote.Route.SiteRoute.Type (+  SiteRoute (..),+  VirtualRoute (..),+  ResourceRoute (..),+  decodeVirtualRoute,+  encodeVirtualRoute,+  encodeTagIndexR,+) where  import Data.Aeson (ToJSON) import Emanote.Pandoc.Markdown.Syntax.HashTag qualified as HT@@ -29,10 +28,11 @@   deriving stock (Eq, Ord, Show, Generic)   deriving anyclass (ToJSON) --- | A route to a resource in `Model`------ This is *mostly isomorphic* to `ModelRoute`, except for containing the--- absolute path to the static file.+{- | A route to a resource in `Model`++ This is *mostly isomorphic* to `ModelRoute`, except for containing the+ absolute path to the static file.+-} data ResourceRoute   = ResourceRoute_StaticFile StaticFileRoute FilePath   | ResourceRoute_LML LMLRoute@@ -101,13 +101,13 @@   VirtualRoute_TagIndex tagNodes ->     R.encodeRoute $ encodeTagIndexR tagNodes   VirtualRoute_Index ->-    R.encodeRoute $ R.R @() @'Ext.Html $ "-" :| ["all"]+    R.encodeRoute $ R.R @() @('Ext.Html) $ "-" :| ["all"]   VirtualRoute_Export ->-    R.encodeRoute $ R.R @Ext.SourceExt @'Ext.AnyExt $ "-" :| ["export.json"]+    R.encodeRoute $ R.R @Ext.SourceExt @('Ext.AnyExt) $ "-" :| ["export.json"]   VirtualRoute_StorkIndex ->-    R.encodeRoute $ R.R @Ext.SourceExt @'Ext.AnyExt $ "-" :| ["stork.st"]+    R.encodeRoute $ R.R @Ext.SourceExt @('Ext.AnyExt) $ "-" :| ["stork.st"]   VirtualRoute_TaskIndex ->-    R.encodeRoute $ R.R @() @'Ext.Html $ "-" :| ["tasks"]+    R.encodeRoute $ R.R @() @('Ext.Html) $ "-" :| ["tasks"]  encodeTagIndexR :: [HT.TagNode] -> R.R 'Ext.Html encodeTagIndexR tagNodes =
src/Emanote/Source/Dynamic.hs view
@@ -1,15 +1,14 @@ {-# LANGUAGE RecordWildCards #-} {-# LANGUAGE TemplateHaskell #-} -module Emanote.Source.Dynamic-  ( emanoteSiteInput,-    EmanoteConfig (..),-    emanoteCompileTailwind,-    emanoteConfigCli,-    emanoteConfigNoteFn,-    emanoteConfigPandocRenderers,-  )-where+module Emanote.Source.Dynamic (+  emanoteSiteInput,+  EmanoteConfig (..),+  emanoteCompileTailwind,+  emanoteConfigCli,+  emanoteConfigNoteFn,+  emanoteConfigPandocRenderers,+) where  import Control.Monad.Logger (MonadLogger, MonadLoggerIO) import Data.Map.Strict qualified as Map@@ -37,19 +36,20 @@  -- | Everything that's required to run an Emanote site. data EmanoteConfig = EmanoteConfig-  { -- | CLI arguments (includes layers)-    _emanoteConfigCli :: CLI.Cli,-    -- | A function to filter the `Note` before it gets added to the model.-    _emanoteConfigNoteFn :: Note -> Note,-    -- | How to render Pandoc to Heist HTML.-    _emanoteConfigPandocRenderers :: EmanotePandocRenderers Model.Model LMLRoute,-    -- | Whether to replace Tailwind2 CDN with a minimized Tailwind3 CSS file.-    _emanoteCompileTailwind :: Bool+  { _emanoteConfigCli :: CLI.Cli+  -- ^ CLI arguments (includes layers)+  , _emanoteConfigNoteFn :: Note -> Note+  -- ^ A function to filter the `Note` before it gets added to the model.+  , _emanoteConfigPandocRenderers :: EmanotePandocRenderers Model.Model LMLRoute+  -- ^ How to render Pandoc to Heist HTML.+  , _emanoteCompileTailwind :: Bool+  -- ^ Whether to replace Tailwind2 CDN with a minimized Tailwind3 CSS file.   } --- | Make an Ema `Dynamic` for the Emanote model.------ The bulk of logic for building the Dynamic is in `Patch.hs`.+{- | Make an Ema `Dynamic` for the Emanote model.++ The bulk of logic for building the Dynamic is in `Patch.hs`.+-} emanoteSiteInput :: (MonadUnliftIO m, MonadLoggerIO m) => Some Ema.CLI.Action -> EmanoteConfig -> m (Dynamic m Model.ModelEma) emanoteSiteInput cliAct EmanoteConfig {..} = do   defaultLayer <- Loc.defaultLayer <$> liftIO Paths_emanote.getDataDir
src/Emanote/Source/Loc.hs view
@@ -1,29 +1,29 @@ -- | Notebook location-module Emanote.Source.Loc-  ( -- * Type-    Loc (..),+module Emanote.Source.Loc (+  -- * Type+  Loc (..), -    -- * Making a `Loc`-    defaultLayer,-    userLayers,+  -- * Making a `Loc`+  defaultLayer,+  userLayers, -    -- * Using a `Loc`-    locResolve,-    locPath,+  -- * Using a `Loc`+  locResolve,+  locPath, -    -- * Dealing with layers of locs-    LocLayers,-    primaryLayer,-  )-where+  -- * Dealing with layers of locs+  LocLayers,+  primaryLayer,+) where  import Data.Set qualified as Set import Relude import System.FilePath ((</>)) --- | Location of the notebook------ The order here matters. Top = higher precedence.+{- | Location of the notebook++ The order here matters. Top = higher precedence.+-} data Loc   = -- | The Int argument specifies the precedence (lower value = higher precedence)     LocUser Int FilePath@@ -33,9 +33,10 @@  type LocLayers = Set Loc --- | Return the "primary" `LocUser` layer (that which are not overrides).------ Assumes that the user has put it always by last; i.e, `-L foo;primary/layer`.+{- | Return the "primary" `LocUser` layer (that which are not overrides).++ Assumes that the user has put it always by last; i.e, `-L foo;primary/layer`.+-} primaryLayer :: HasCallStack => LocLayers -> Loc primaryLayer =   Set.findMax . Set.filter isUserLayer
src/Emanote/Source/Patch.hs view
@@ -1,10 +1,9 @@ -- | Patch model state depending on file change event.-module Emanote.Source.Patch-  ( patchModel,-    filePatterns,-    ignorePatterns,-  )-where+module Emanote.Source.Patch (+  patchModel,+  filePatterns,+  ignorePatterns,+) where  import Control.Exception (throwIO) import Control.Monad.Logger (LoggingT (runLoggingT), MonadLogger, MonadLoggerIO (askLoggerIO))@@ -16,11 +15,11 @@ import Emanote.Model.SData qualified as SD import Emanote.Model.Stork.Index qualified as Stork import Emanote.Model.Type (ModelEma)-import Emanote.Prelude-  ( BadInput (BadInput),-    log,-    logD,-  )+import Emanote.Prelude (+  BadInput (BadInput),+  log,+  logD,+ ) import Emanote.Route qualified as R import Emanote.Source.Loc (Loc, LocLayers, locPath, locResolve, primaryLayer) import Emanote.Source.Pattern (filePatterns, ignorePatterns)@@ -159,11 +158,12 @@     _ ->       readFileFollowingFsnotify fp --- | Like `readFileBS` but accounts for file truncation due to us responding--- *immediately* to a fsnotify modify event (which is triggered even before the--- writer *finishes* writing the new contents). We solve this "glitch" by--- delaying the read retry, expecting (hoping really) that *this time* the new--- non-empty contents will come through. 'tis a bit of a HACK though.+{- | Like `readFileBS` but accounts for file truncation due to us responding+ *immediately* to a fsnotify modify event (which is triggered even before the+ writer *finishes* writing the new contents). We solve this "glitch" by+ delaying the read retry, expecting (hoping really) that *this time* the new+ non-empty contents will come through. 'tis a bit of a HACK though.+-} readFileFollowingFsnotify :: (MonadIO m, MonadLogger m) => FilePath -> m ByteString readFileFollowingFsnotify fp = do   log $ "Reading file: " <> toText fp
src/Emanote/Source/Pattern.hs view
@@ -13,10 +13,9 @@     R.withExt @_ @('R.LMLType 'R.Org) $       "**/*"   R.Yaml ->-    R.withExt @_ @'R.Yaml $-      "**/*"+    R.withExt @_ @('R.Yaml) "**/*"   R.HeistTpl ->-    R.withExt @_ @'R.HeistTpl $+    R.withExt @_ @('R.HeistTpl) $       "**/*"   R.AnyExt ->     "**"@@ -24,19 +23,26 @@ filePatterns :: [(R.FileType R.SourceExt, FilePattern)] filePatterns =   (id &&& filePattern)-    <$> [ R.LMLType R.Md,-          R.LMLType R.Org,-          R.Yaml,-          R.HeistTpl,-          R.AnyExt+    <$> [ R.LMLType R.Md+        , R.LMLType R.Org+        , R.Yaml+        , R.HeistTpl+        , R.AnyExt         ]  ignorePatterns :: [FilePattern] ignorePatterns =   [ -- Ignore all dotfile directories (eg: .git, .vscode)-    "**/.*/**",-    -- Ignore vi/vim/neovim writebackup files (see ":help writebackup")-    "**/*~",-    -- /Top-level ./-/ directory is reserved by Emanote+    "**/.*/**"+  , -- Ignore vi/vim/neovim writebackup files (see ":help writebackup")+    "**/*~"+  , -- /Top-level ./-/ directory is reserved by Emanote     "-/**"+  , -- Special files that are not meant to be rendered+    -- NOTE: We must hardcode this only because there is no user-controllable+    -- `.emanoteignore` setting yet.{-# ANN annotation #-}+    --+    -- Any top-level Nix flake files+    "flake.nix"+  , "flake.lock"   ]
src/Emanote/View/Common.hs view
@@ -1,17 +1,16 @@ {-# LANGUAGE RecordWildCards #-} -module Emanote.View.Common-  ( commonSplices,-    renderModelTemplate,-    routeBreadcrumbs,-    generatedCssFile,+module Emanote.View.Common (+  commonSplices,+  renderModelTemplate,+  routeBreadcrumbs,+  generatedCssFile, -    -- * Render context-    TemplateRenderCtx (..),-    mkTemplateRenderCtx,-    defaultRouteMeta,-  )-where+  -- * Render context+  TemplateRenderCtx (..),+  mkTemplateRenderCtx,+  defaultRouteMeta,+) where  import Data.Aeson.Types qualified as Aeson import Data.Map.Syntax ((##))@@ -48,14 +47,15 @@ import Text.Blaze.Renderer.XmlHtml qualified as RX  data TemplateRenderCtx n = TemplateRenderCtx-  { withInlineCtx :: (RenderCtx -> HI.Splice Identity) -> HI.Splice Identity,-    withBlockCtx :: (RenderCtx -> HI.Splice Identity) -> HI.Splice Identity,-    withLinkInlineCtx :: (RenderCtx -> HI.Splice Identity) -> HI.Splice Identity,-    titleSplice :: Tit.Title -> HI.Splice Identity+  { withInlineCtx :: (RenderCtx -> HI.Splice Identity) -> HI.Splice Identity+  , withBlockCtx :: (RenderCtx -> HI.Splice Identity) -> HI.Splice Identity+  , withLinkInlineCtx :: (RenderCtx -> HI.Splice Identity) -> HI.Splice Identity+  , titleSplice :: Tit.Title -> HI.Splice Identity   } --- | Create the context in which Heist templates (notably `pandoc.tpl`) will be--- rendered.+{- | Create the context in which Heist templates (notably `pandoc.tpl`) will be+ rendered.+-} mkTemplateRenderCtx ::   -- | Current model.   Model ->@@ -172,7 +172,7 @@                   fromMaybe (error "no _emanote-static?") $                     M.modelLookupStaticFile (staticFolder </> "inverted-tree.css") model             staticFolderUrl = fst $ T.breakOn "/inverted-tree.css" itUrl-            -- Deal with a silly Firefox bug https://github.com/EmaApps/emanote/issues/340+            -- Deal with a silly Firefox bug https://github.com/srid/emanote/issues/340             --             -- Firefox deduces an incorrect <base> after doing morphdom             -- patching, unless the <base> is absolute (i.e., starts with a '/').
src/Emanote/View/Export.hs view
@@ -1,11 +1,10 @@ {-# LANGUAGE DeriveAnyClass #-} -module Emanote.View.Export-  ( renderGraphExport,-    Link (..),-    modelRels,-  )-where+module Emanote.View.Export (+  renderGraphExport,+  Link (..),+  modelRels,+) where  import Data.Aeson (ToJSON) import Data.Aeson qualified as Aeson@@ -24,8 +23,8 @@ import Relude  data Export = Export-  { version :: Word,-    files :: Map Text SourceFile+  { version :: Word+  , files :: Map Text SourceFile   }   deriving stock (Generic, Show)   deriving anyclass (ToJSON)@@ -35,19 +34,19 @@  -- | A source file in `Model` data SourceFile = SourceFile-  { title :: Text,-    filePath :: Text,-    parentNote :: Maybe Text,-    url :: Text,-    meta :: Aeson.Value,-    links :: [Link]+  { title :: Text+  , filePath :: Text+  , parentNote :: Maybe Text+  , url :: Text+  , meta :: Aeson.Value+  , links :: [Link]   }   deriving stock (Generic, Show)   deriving anyclass (ToJSON)  data Link = Link-  { unresolvedRelTarget :: Rel.UnresolvedRelTarget,-    resolvedRelTarget :: Rel.ResolvedRelTarget Text+  { unresolvedRelTarget :: Rel.UnresolvedRelTarget+  , resolvedRelTarget :: Rel.ResolvedRelTarget Text   }   deriving stock (Generic, Eq, Ord, Show)   deriving anyclass (ToJSON)
src/Emanote/View/LiveServerFiles.hs view
@@ -1,9 +1,8 @@-module Emanote.View.LiveServerFiles-  ( isLiveServerFile,-    tailwindFullCssPath,-    tailwindCssFile,-  )-where+module Emanote.View.LiveServerFiles (+  isLiveServerFile,+  tailwindFullCssPath,+  tailwindCssFile,+) where  import Data.Text qualified as T import Emanote.Model.StaticFile (StaticFile)
src/Emanote/View/TagIndex.hs view
@@ -12,13 +12,13 @@ import Emanote.Pandoc.Markdown.Syntax.HashTag qualified as HT import Emanote.Pandoc.Renderer.Query qualified as PF import Emanote.Route.SiteRoute.Class qualified as SR-import Emanote.View.Common-  ( TemplateRenderCtx (withInlineCtx),-    commonSplices,-    defaultRouteMeta,-    mkTemplateRenderCtx,-    renderModelTemplate,-  )+import Emanote.View.Common (+  TemplateRenderCtx (withInlineCtx),+  commonSplices,+  defaultRouteMeta,+  mkTemplateRenderCtx,+  renderModelTemplate,+ ) import Heist.Extra.Splices.List qualified as Splices import Heist.Extra.Splices.Pandoc.Ctx (emptyRenderCtx) import Heist.Interpreted qualified as HI@@ -26,16 +26,16 @@  -- An index view into the notebook indexed by the given tag path. data TagIndex = TagIndex-  { -- | The tag path under which this index is creatd-    tagIndexPath :: [HT.TagNode],-    -- | User descriptive title of this index-    tagIndexTitle :: Text,-    -- | All notes tagged precisely with this tag path-    tagIndexNotes :: [MN.Note],-    -- | Tags immediately under this tag path.-    ---    -- If the tag path being index is "foo/bar", this will contain "foo/bar/qux".-    tagIndexChildren :: [(NonEmpty HT.TagNode, [MN.Note])]+  { tagIndexPath :: [HT.TagNode]+  -- ^ The tag path under which this index is creatd+  , tagIndexTitle :: Text+  -- ^ User descriptive title of this index+  , tagIndexNotes :: [MN.Note]+  -- ^ All notes tagged precisely with this tag path+  , tagIndexChildren :: [(NonEmpty HT.TagNode, [MN.Note])]+  -- ^ Tags immediately under this tag path.+  --+  -- If the tag path being index is "foo/bar", this will contain "foo/bar/qux".   }   deriving stock (Eq) 
src/Emanote/View/Template.hs view
@@ -192,8 +192,8 @@   "ema:route-tree" ##     ( let tree = PathTree.treeDeleteChild "index" $ model ^. M.modelNav           getOrder tr =-            ( Meta.lookupRouteMeta @Int 0 (one "order") tr model,-              tr+            ( Meta.lookupRouteMeta @Int 0 (one "order") tr model+            , tr             )           getCollapsed tr =             Meta.lookupRouteMeta @Bool True ("template" :| ["sidebar", "collapsed"]) tr model
test/Emanote/Pandoc/ExternalLinkSpec.hs view
@@ -28,7 +28,7 @@       getDataLinkIconAttrs "#tag" === Right []       getDataLinkIconAttrs "[[abc|def]]" === Right []     it "adds attribute if link is external and its description contains text, code or math" . hedgehog $ do-      getDataLinkIconAttrs "[Emanote](https://github.com/EmaApps/emanote)" === Right ["external"]+      getDataLinkIconAttrs "[Emanote](https://github.com/srid/emanote)" === Right ["external"]       getDataLinkIconAttrs "[`text`](http://somehost:1234/test)" === Right ["external"]       getDataLinkIconAttrs "[$$E=mc^2$$](ssh://user@host.abc/~/path/)" === Right ["external"]       getDataLinkIconAttrs "[![[picture.png]] ~~**`code`**~~](scheme://host:port/path?query)" === Right ["external"]