Adopt Step 7 language features across SX codebase
112 conversions across 19 .sx files using match, let-match, and pipe operators: match (17): type/value dispatch replacing cond/if chains - lib/vm.sx: HO form dispatch (for-each/map/filter/reduce/some/every?) - lib/tree-tools.sx: node-display, node-matches?, rename, count, replace, free-symbols - lib/types.sx: narrow-type, substitute-in-type, infer-type, resolve-type - web/engine.sx: default-trigger, resolve-target, classify-trigger - web/deps.sx: scan-refs-walk, scan-io-refs-walk let-match (89): dict destructuring replacing (get d "key") patterns - shared/page-functions.sx (20), blog/admin.sx (17), pub-api.sx (13) - events/ layouts/page/tickets/entries/forms (27 total) - specs-explorer.sx (7), federation/social.sx (3), lib/ small files (3) -> pipes (6): replacing triple-chained gets in lib/vm.sx - frame-closure → closure-code → code-bytecode chains Also: lib/vm.sx accessor upgrades (get vm "sp" → vm-sp vm throughout) 2650/2650 tests pass, zero regressions. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
703
blog/sx/admin.sx
703
blog/sx/admin.sx
@@ -144,78 +144,140 @@
|
|||||||
edit-form delete-form))
|
edit-form delete-form))
|
||||||
|
|
||||||
;; Data-driven snippets list (replaces Python _snippets_sx loop)
|
;; Data-driven snippets list (replaces Python _snippets_sx loop)
|
||||||
(defcomp ~admin/snippets-from-data (&key snippets user-id is-admin csrf badge-colours)
|
(defcomp
|
||||||
|
~admin/snippets-from-data
|
||||||
|
(&key snippets user-id is-admin csrf badge-colours)
|
||||||
(~admin/snippets-list
|
(~admin/snippets-list
|
||||||
:rows (<> (map (lambda (s)
|
:rows (<>
|
||||||
(let* ((s-id (get s "id"))
|
(map
|
||||||
(s-name (get s "name"))
|
(lambda
|
||||||
(s-uid (get s "user_id"))
|
(s)
|
||||||
(s-vis (get s "visibility"))
|
(let-match
|
||||||
(owner (if (= s-uid user-id) "You" (str "User #" s-uid)))
|
{:visibility s-vis :delete_url delete-url :patch_url patch-url :id s-id :user_id s-uid :name s-name}
|
||||||
(badge-cls (or (get badge-colours s-vis) "bg-stone-200 text-stone-700"))
|
s
|
||||||
(extra (<>
|
(let*
|
||||||
(when is-admin
|
((owner (if (= s-uid user-id) "You" (str "User #" s-uid)))
|
||||||
(~admin/snippet-visibility-select
|
(badge-cls
|
||||||
:patch-url (get s "patch_url")
|
(or (get badge-colours s-vis) "bg-stone-200 text-stone-700"))
|
||||||
:hx-headers (str "{\"X-CSRFToken\": \"" csrf "\"}")
|
(extra
|
||||||
:options (<>
|
(<>
|
||||||
(~admin/snippet-option :value "private" :selected (= s-vis "private") :label "private")
|
(when
|
||||||
(~admin/snippet-option :value "shared" :selected (= s-vis "shared") :label "shared")
|
is-admin
|
||||||
(~admin/snippet-option :value "admin" :selected (= s-vis "admin") :label "admin"))
|
(~admin/snippet-visibility-select
|
||||||
:cls "text-sm border border-stone-300 rounded px-2 py-1"))
|
:patch-url patch-url
|
||||||
(when (or (= s-uid user-id) is-admin)
|
:hx-headers (str "{\"X-CSRFToken\": \"" csrf "\"}")
|
||||||
(~shared:misc/delete-btn :url (get s "delete_url") :trigger-target "#snippets-list"
|
:options (<>
|
||||||
:title "Delete snippet?"
|
(~admin/snippet-option
|
||||||
:text (str "Delete \u201c" s-name "\u201d?")
|
:value "private"
|
||||||
:sx-headers (str "{\"X-CSRFToken\": \"" csrf "\"}")
|
:selected (= s-vis "private")
|
||||||
:cls "px-3 py-1 text-sm bg-red-200 hover:bg-red-300 rounded text-red-800 flex-shrink-0")))))
|
:label "private")
|
||||||
(~admin/snippet-row :name s-name :owner owner :badge-cls badge-cls
|
(~admin/snippet-option
|
||||||
:visibility s-vis :extra extra)))
|
:value "shared"
|
||||||
(or snippets (list))))))
|
:selected (= s-vis "shared")
|
||||||
|
:label "shared")
|
||||||
|
(~admin/snippet-option
|
||||||
|
:value "admin"
|
||||||
|
:selected (= s-vis "admin")
|
||||||
|
:label "admin"))
|
||||||
|
:cls "text-sm border border-stone-300 rounded px-2 py-0.5"))
|
||||||
|
(when
|
||||||
|
(or (= s-uid user-id) is-admin)
|
||||||
|
(~shared:misc/delete-btn
|
||||||
|
:url delete-url
|
||||||
|
:trigger-target "#snippets-list"
|
||||||
|
:title "Delete snippet?"
|
||||||
|
:text (str "Delete \"" s-name "\"?")
|
||||||
|
:sx-headers (str "{\"X-CSRFToken\": \"" csrf "\"}")
|
||||||
|
:cls "px-3 py-1 text-sm bg-red-200 hover:bg-red-300 text-red-800 rounded-full")))))
|
||||||
|
(~admin/snippet-row
|
||||||
|
:name s-name
|
||||||
|
:owner owner
|
||||||
|
:badge-cls badge-cls
|
||||||
|
:visibility s-vis
|
||||||
|
:extra extra))))
|
||||||
|
(or snippets (list))))))
|
||||||
|
|
||||||
;; Data-driven menu items list (replaces Python _menu_items_list_sx loop)
|
;; Data-driven menu items list (replaces Python _menu_items_list_sx loop)
|
||||||
(defcomp ~admin/menu-items-from-data (&key items csrf)
|
(defcomp
|
||||||
|
~admin/menu-items-from-data
|
||||||
|
(&key items csrf)
|
||||||
(~admin/menu-items-list
|
(~admin/menu-items-list
|
||||||
:rows (<> (map (lambda (item)
|
:rows (<>
|
||||||
(let* ((img (~shared:misc/img-or-placeholder :src (get item "feature_image") :alt (get item "label")
|
(map
|
||||||
:size-cls "w-12 h-12 rounded-full object-cover flex-shrink-0")))
|
(lambda
|
||||||
(~admin/menu-item-row
|
(item)
|
||||||
:img img :label (get item "label") :slug (get item "slug")
|
(let-match
|
||||||
:sort-order (get item "sort_order") :edit-url (get item "edit_url")
|
{:delete_url delete-url :sort_order sort-order :edit_url edit-url :feature_image feature-image :label label :slug slug}
|
||||||
:delete-url (get item "delete_url")
|
item
|
||||||
:confirm-text (str "Remove " (get item "label") " from the menu?")
|
(let
|
||||||
:hx-headers (str "{\"X-CSRFToken\": \"" csrf "\"}"))))
|
((img (~shared:misc/img-or-placeholder :src feature-image :alt label :size-cls "w-12 h-12 rounded-full object-cover flex-shrink-0")))
|
||||||
(or items (list))))))
|
(~admin/menu-item-row
|
||||||
|
:img img
|
||||||
|
:label label
|
||||||
|
:slug slug
|
||||||
|
:sort-order sort-order
|
||||||
|
:edit-url edit-url
|
||||||
|
:delete-url delete-url
|
||||||
|
:confirm-text (str "Remove " label " from the menu?")
|
||||||
|
:hx-headers (str "{\"X-CSRFToken\": \"" csrf "\"}")))))
|
||||||
|
(or items (list))))))
|
||||||
|
|
||||||
;; Data-driven tag groups main (replaces Python _tag_groups_main_panel_sx loops)
|
;; Data-driven tag groups main (replaces Python _tag_groups_main_panel_sx loops)
|
||||||
(defcomp ~admin/tag-groups-from-data (&key groups unassigned-tags csrf create-url)
|
(defcomp
|
||||||
|
~admin/tag-groups-from-data
|
||||||
|
(&key groups unassigned-tags csrf create-url)
|
||||||
(~admin/tag-groups-main
|
(~admin/tag-groups-main
|
||||||
:form (~admin/tag-groups-create-form :create-url create-url :csrf csrf)
|
:form (~admin/tag-groups-create-form :create-url create-url :csrf csrf)
|
||||||
:groups (if (empty? (or groups (list)))
|
:groups (if
|
||||||
(~shared:misc/empty-state :message "No tag groups yet." :cls "text-stone-500 text-sm")
|
(empty? (or groups (list)))
|
||||||
|
(~shared:misc/empty-state
|
||||||
|
:message "No tag groups yet."
|
||||||
|
:cls "text-stone-500 text-sm")
|
||||||
(~admin/tag-groups-list
|
(~admin/tag-groups-list
|
||||||
:items (<> (map (lambda (g)
|
:items (<>
|
||||||
(let* ((icon (if (get g "feature_image")
|
(map
|
||||||
(~admin/tag-group-icon-image :src (get g "feature_image") :name (get g "name"))
|
(lambda
|
||||||
(~admin/tag-group-icon-color :style (get g "style") :initial (get g "initial")))))
|
(g)
|
||||||
(~admin/tag-group-li :icon icon :edit-href (get g "edit_href")
|
(let-match
|
||||||
:name (get g "name") :slug (get g "slug") :sort-order (get g "sort_order"))))
|
{:sort_order sort-order :feature_image feature-image :slug slug :edit_href edit-href :initial initial :name name :style style}
|
||||||
groups))))
|
g
|
||||||
:unassigned (when (not (empty? (or unassigned-tags (list))))
|
(let
|
||||||
|
((icon (if feature-image (~admin/tag-group-icon-image :src feature-image :name name) (~admin/tag-group-icon-color :style style :initial initial))))
|
||||||
|
(~admin/tag-group-li
|
||||||
|
:icon icon
|
||||||
|
:edit-href edit-href
|
||||||
|
:name name
|
||||||
|
:slug slug
|
||||||
|
:sort-order sort-order))))
|
||||||
|
groups))))
|
||||||
|
:unassigned (when
|
||||||
|
(not (empty? (or unassigned-tags (list))))
|
||||||
(~admin/unassigned-tags
|
(~admin/unassigned-tags
|
||||||
:heading (str "Unassigned Tags (" (len unassigned-tags) ")")
|
:heading (str "Unassigned Tags (" (len unassigned-tags) ")")
|
||||||
:spans (<> (map (lambda (t)
|
:spans (<>
|
||||||
(~admin/unassigned-tag :name (get t "name")))
|
(map
|
||||||
unassigned-tags))))))
|
(lambda (t) (~admin/unassigned-tag :name (get t "name")))
|
||||||
|
unassigned-tags))))))
|
||||||
|
|
||||||
;; Data-driven tag group edit (replaces Python _tag_groups_edit_main_panel_sx loop)
|
;; Data-driven tag group edit (replaces Python _tag_groups_edit_main_panel_sx loop)
|
||||||
(defcomp ~admin/tag-checkboxes-from-data (&key tags)
|
(defcomp
|
||||||
(<> (map (lambda (t)
|
~admin/tag-checkboxes-from-data
|
||||||
(~admin/tag-checkbox
|
(&key tags)
|
||||||
:tag-id (get t "tag_id") :checked (get t "checked")
|
(<>
|
||||||
:img (when (get t "feature_image") (~admin/tag-checkbox-image :src (get t "feature_image")))
|
(map
|
||||||
:name (get t "name")))
|
(lambda
|
||||||
(or tags (list)))))
|
(t)
|
||||||
|
(let-match
|
||||||
|
{:tag_id tag-id :checked checked :feature_image feature-image :name name}
|
||||||
|
t
|
||||||
|
(~admin/tag-checkbox
|
||||||
|
:tag-id tag-id
|
||||||
|
:checked checked
|
||||||
|
:img (when
|
||||||
|
feature-image
|
||||||
|
(~admin/tag-checkbox-image :src feature-image))
|
||||||
|
:name name)))
|
||||||
|
(or tags (list)))))
|
||||||
|
|
||||||
;; Preview panel components
|
;; Preview panel components
|
||||||
|
|
||||||
@@ -258,113 +320,175 @@
|
|||||||
;; ---------------------------------------------------------------------------
|
;; ---------------------------------------------------------------------------
|
||||||
|
|
||||||
;; Snippets — receives serialized snippet dicts from service
|
;; Snippets — receives serialized snippet dicts from service
|
||||||
(defcomp ~admin/snippets-content (&key snippets is-admin csrf)
|
(defcomp
|
||||||
|
~admin/snippets-content
|
||||||
|
(&key snippets is-admin csrf)
|
||||||
(~admin/snippets-panel
|
(~admin/snippets-panel
|
||||||
:list (if (empty? (or snippets (list)))
|
:list (if
|
||||||
(~shared:misc/empty-state :icon "fa fa-puzzle-piece"
|
(empty? (or snippets (list)))
|
||||||
|
(~shared:misc/empty-state
|
||||||
|
:icon "fa fa-puzzle-piece"
|
||||||
:message "No snippets yet. Create one from the blog editor.")
|
:message "No snippets yet. Create one from the blog editor.")
|
||||||
(~admin/snippets-list
|
(~admin/snippets-list
|
||||||
:rows (map (lambda (s)
|
:rows (map
|
||||||
(let* ((badge-colours (dict
|
(lambda
|
||||||
"private" "bg-stone-200 text-stone-700"
|
(s)
|
||||||
"shared" "bg-blue-100 text-blue-700"
|
(let-match
|
||||||
"admin" "bg-amber-100 text-amber-700"))
|
{:visibility vis* :delete_url delete-url :owner owner :can_delete can-delete :patch_url patch-url :name name}
|
||||||
(vis (or (get s "visibility") "private"))
|
s
|
||||||
(badge-cls (or (get badge-colours vis) "bg-stone-200 text-stone-700"))
|
(let*
|
||||||
(name (get s "name"))
|
((vis (or vis* "private"))
|
||||||
(owner (get s "owner"))
|
(badge-colours
|
||||||
(can-delete (get s "can_delete")))
|
(dict
|
||||||
(~admin/snippet-row
|
"private"
|
||||||
:name name :owner owner :badge-cls badge-cls :visibility vis
|
"bg-stone-200 text-stone-700"
|
||||||
:extra (<>
|
"shared"
|
||||||
(when is-admin
|
"bg-blue-100 text-blue-700"
|
||||||
(~admin/snippet-visibility-select
|
"admin"
|
||||||
:patch-url (get s "patch_url")
|
"bg-amber-100 text-amber-700"))
|
||||||
:hx-headers {:X-CSRFToken csrf}
|
(badge-cls
|
||||||
:options (<>
|
(or (get badge-colours vis) "bg-stone-200 text-stone-700")))
|
||||||
(~admin/snippet-option :value "private" :selected (= vis "private") :label "private")
|
(~admin/snippet-row
|
||||||
(~admin/snippet-option :value "shared" :selected (= vis "shared") :label "shared")
|
:name name
|
||||||
(~admin/snippet-option :value "admin" :selected (= vis "admin") :label "admin"))))
|
:owner owner
|
||||||
(when can-delete
|
:badge-cls badge-cls
|
||||||
(~shared:misc/delete-btn
|
:visibility vis
|
||||||
:url (get s "delete_url")
|
:extra (<>
|
||||||
:trigger-target "#snippets-list"
|
(when
|
||||||
:title "Delete snippet?"
|
is-admin
|
||||||
:text (str "Delete \u201c" name "\u201d?")
|
(~admin/snippet-visibility-select
|
||||||
:sx-headers {:X-CSRFToken csrf}
|
:patch-url patch-url
|
||||||
:cls "px-3 py-1 text-sm bg-red-200 hover:bg-red-300 rounded text-red-800 flex-shrink-0"))))))
|
:hx-headers (str "{\"X-CSRFToken\": \"" csrf "\"}")
|
||||||
|
:options (<>
|
||||||
|
(~admin/snippet-option
|
||||||
|
:value "private"
|
||||||
|
:selected (= vis "private")
|
||||||
|
:label "private")
|
||||||
|
(~admin/snippet-option
|
||||||
|
:value "shared"
|
||||||
|
:selected (= vis "shared")
|
||||||
|
:label "shared")
|
||||||
|
(~admin/snippet-option
|
||||||
|
:value "admin"
|
||||||
|
:selected (= vis "admin")
|
||||||
|
:label "admin"))))
|
||||||
|
(when
|
||||||
|
can-delete
|
||||||
|
(~shared:misc/delete-btn
|
||||||
|
:url delete-url
|
||||||
|
:trigger-target "#snippets-list"
|
||||||
|
:title "Delete snippet?"
|
||||||
|
:text (str "Delete \"" name "\"?")
|
||||||
|
:sx-headers (str "{\"X-CSRFToken\": \"" csrf "\"}")
|
||||||
|
:cls "px-3 py-1 text-sm bg-red-200 hover:bg-red-300 text-red-800 rounded-full")))))))
|
||||||
(or snippets (list)))))))
|
(or snippets (list)))))))
|
||||||
|
|
||||||
;; Menu Items — receives serialized menu item dicts from service
|
;; Menu Items — receives serialized menu item dicts from service
|
||||||
(defcomp ~admin/menu-items-content (&key menu-items new-url csrf)
|
(defcomp
|
||||||
|
~admin/menu-items-content
|
||||||
|
(&key menu-items new-url csrf)
|
||||||
(~admin/menu-items-panel
|
(~admin/menu-items-panel
|
||||||
:new-url new-url
|
:new-url new-url
|
||||||
:list (if (empty? (or menu-items (list)))
|
:list (if
|
||||||
(~shared:misc/empty-state :icon "fa fa-inbox"
|
(empty? (or menu-items (list)))
|
||||||
|
(~shared:misc/empty-state
|
||||||
|
:icon "fa fa-inbox"
|
||||||
:message "No menu items yet. Add one to get started!")
|
:message "No menu items yet. Add one to get started!")
|
||||||
(~admin/menu-items-list
|
(~admin/menu-items-list
|
||||||
:rows (map (lambda (mi)
|
:rows (map
|
||||||
(~admin/menu-item-row
|
(lambda
|
||||||
:img (~shared:misc/img-or-placeholder
|
(mi)
|
||||||
:src (get mi "feature_image") :alt (get mi "label")
|
(let-match
|
||||||
:size-cls "w-12 h-12 rounded-full object-cover flex-shrink-0")
|
{:delete_url delete-url :url url :sort_order sort-order :edit_url edit-url :feature_image feature-image :label label}
|
||||||
:label (get mi "label")
|
mi
|
||||||
:slug (get mi "url")
|
(~admin/menu-item-row
|
||||||
:sort-order (str (or (get mi "sort_order") 0))
|
:img (~shared:misc/img-or-placeholder
|
||||||
:edit-url (get mi "edit_url")
|
:src feature-image
|
||||||
:delete-url (get mi "delete_url")
|
:alt label
|
||||||
:confirm-text (str "Remove " (get mi "label") " from the menu?")
|
:size-cls "w-12 h-12 rounded-full object-cover flex-shrink-0")
|
||||||
:hx-headers {:X-CSRFToken csrf}))
|
:label label
|
||||||
|
:slug url
|
||||||
|
:sort-order (str (or sort-order 0))
|
||||||
|
:edit-url edit-url
|
||||||
|
:delete-url delete-url
|
||||||
|
:confirm-text (str "Remove " label " from the menu?")
|
||||||
|
:hx-headers (str "{\"X-CSRFToken\": \"" csrf "\"}"))))
|
||||||
(or menu-items (list)))))))
|
(or menu-items (list)))))))
|
||||||
|
|
||||||
;; Tag Groups — receives serialized tag group data from service
|
;; Tag Groups — receives serialized tag group data from service
|
||||||
(defcomp ~admin/tag-groups-content (&key groups unassigned-tags create-url csrf)
|
(defcomp
|
||||||
|
~admin/tag-groups-content
|
||||||
|
(&key groups unassigned-tags create-url csrf)
|
||||||
(~admin/tag-groups-main
|
(~admin/tag-groups-main
|
||||||
:form (~admin/tag-groups-create-form :create-url create-url :csrf csrf)
|
:form (~admin/tag-groups-create-form :create-url create-url :csrf csrf)
|
||||||
:groups (if (empty? (or groups (list)))
|
:groups (if
|
||||||
(~shared:misc/empty-state :icon "fa fa-tags" :message "No tag groups yet.")
|
(empty? (or groups (list)))
|
||||||
|
(~shared:misc/empty-state
|
||||||
|
:icon "fa fa-tags"
|
||||||
|
:message "No tag groups yet.")
|
||||||
(~admin/tag-groups-list
|
(~admin/tag-groups-list
|
||||||
:items (map (lambda (g)
|
:items (map
|
||||||
(let* ((fi (get g "feature_image"))
|
(lambda
|
||||||
(colour (get g "colour"))
|
(g)
|
||||||
(name (get g "name"))
|
(let-match
|
||||||
(initial (slice (or name "?") 0 1))
|
{:colour colour :sort_order sort-order* :feature_image fi :edit_href edit-href :slug slug* :name name}
|
||||||
(icon (if fi
|
g
|
||||||
(~admin/tag-group-icon-image :src fi :name name)
|
(let*
|
||||||
(~admin/tag-group-icon-color
|
((initial (slice (or name "?") 0 1))
|
||||||
:style (if colour (str "background:" colour) "background:#e7e5e4")
|
(icon
|
||||||
:initial initial))))
|
(if
|
||||||
(~admin/tag-group-li
|
fi
|
||||||
:icon icon
|
(~admin/tag-group-icon-image :src fi :name name)
|
||||||
:edit-href (get g "edit_href")
|
(~admin/tag-group-icon-color
|
||||||
:name name
|
:style (if
|
||||||
:slug (or (get g "slug") "")
|
colour
|
||||||
:sort-order (or (get g "sort_order") 0))))
|
(str "background:" colour)
|
||||||
|
"background:#e7e5e4")
|
||||||
|
:initial initial))))
|
||||||
|
(~admin/tag-group-li
|
||||||
|
:icon icon
|
||||||
|
:edit-href edit-href
|
||||||
|
:name name
|
||||||
|
:slug (or slug* "")
|
||||||
|
:sort-order (or sort-order* 0)))))
|
||||||
(or groups (list)))))
|
(or groups (list)))))
|
||||||
:unassigned (when (not (empty? (or unassigned-tags (list))))
|
:unassigned (when
|
||||||
|
(not (empty? (or unassigned-tags (list))))
|
||||||
(~admin/unassigned-tags
|
(~admin/unassigned-tags
|
||||||
:heading (str (len (or unassigned-tags (list))) " Unassigned Tags")
|
:heading (str (len (or unassigned-tags (list))) " Unassigned Tags")
|
||||||
:spans (map (lambda (t)
|
:spans (map
|
||||||
(~admin/unassigned-tag :name (get t "name")))
|
(lambda (t) (~admin/unassigned-tag :name (get t "name")))
|
||||||
(or unassigned-tags (list)))))))
|
(or unassigned-tags (list)))))))
|
||||||
|
|
||||||
;; Tag Group Edit — receives serialized tag group + tags from service
|
;; Tag Group Edit — receives serialized tag group + tags from service
|
||||||
(defcomp ~admin/tag-group-edit-content (&key group all-tags save-url delete-url csrf)
|
(defcomp
|
||||||
|
~admin/tag-group-edit-content
|
||||||
|
(&key group all-tags save-url delete-url csrf)
|
||||||
(~admin/tag-group-edit-main
|
(~admin/tag-group-edit-main
|
||||||
:edit-form (~admin/tag-group-edit-form
|
:edit-form (let-match
|
||||||
:save-url save-url :csrf csrf
|
{:colour colour :sort_order sort-order :feature_image feature-image :name name}
|
||||||
:name (get group "name")
|
group
|
||||||
:colour (get group "colour")
|
(~admin/tag-group-edit-form
|
||||||
:sort-order (get group "sort_order")
|
:save-url save-url
|
||||||
:feature-image (get group "feature_image")
|
:csrf csrf
|
||||||
:tags (map (lambda (t)
|
:name name
|
||||||
(~admin/tag-checkbox
|
:colour colour
|
||||||
:tag-id (get t "id")
|
:sort-order sort-order
|
||||||
:checked (get t "checked")
|
:feature-image feature-image
|
||||||
:img (when (get t "feature_image")
|
:tags (map
|
||||||
(~admin/tag-checkbox-image :src (get t "feature_image")))
|
(lambda
|
||||||
:name (get t "name")))
|
(t)
|
||||||
(or all-tags (list))))
|
(let-match
|
||||||
|
{:checked checked :feature_image t-feature-image :id tag-id :name t-name}
|
||||||
|
t
|
||||||
|
(~admin/tag-checkbox
|
||||||
|
:tag-id tag-id
|
||||||
|
:checked checked
|
||||||
|
:img (when
|
||||||
|
t-feature-image
|
||||||
|
(~admin/tag-checkbox-image :src t-feature-image))
|
||||||
|
:name t-name)))
|
||||||
|
(or all-tags (list)))))
|
||||||
:delete-form (~admin/tag-group-delete-form :delete-url delete-url :csrf csrf)))
|
:delete-form (~admin/tag-group-delete-form :delete-url delete-url :csrf csrf)))
|
||||||
|
|
||||||
;; ---------------------------------------------------------------------------
|
;; ---------------------------------------------------------------------------
|
||||||
@@ -400,31 +524,54 @@
|
|||||||
(code value)
|
(code value)
|
||||||
value))))
|
value))))
|
||||||
|
|
||||||
(defcomp ~admin/data-scalar-table (&key columns)
|
(defcomp
|
||||||
(div :class "w-full overflow-x-auto sm:overflow-visible"
|
~admin/data-scalar-table
|
||||||
(table :class "w-full table-fixed text-sm border border-neutral-200 rounded-xl overflow-hidden"
|
(&key columns)
|
||||||
(thead :class "bg-neutral-50/70"
|
(div
|
||||||
(tr (th :class "px-3 py-2 text-left font-medium w-40 sm:w-56" "Field")
|
:class "w-full overflow-x-auto sm:overflow-visible"
|
||||||
(th :class "px-3 py-2 text-left font-medium" "Value")))
|
(table
|
||||||
|
:class "w-full table-fixed text-sm border border-neutral-200 rounded-xl overflow-hidden"
|
||||||
|
(thead
|
||||||
|
:class "bg-neutral-50/70"
|
||||||
|
(tr
|
||||||
|
(th :class "px-3 py-2 text-left font-medium w-40 sm:w-56" "Field")
|
||||||
|
(th :class "px-3 py-2 text-left font-medium" "Value")))
|
||||||
(tbody
|
(tbody
|
||||||
(map (lambda (col)
|
(map
|
||||||
(tr :class "border-t border-neutral-200 align-top"
|
(lambda
|
||||||
(td :class "px-3 py-2 whitespace-nowrap text-neutral-600 align-top" (get col "key"))
|
(col)
|
||||||
(td :class "px-3 py-2 align-top"
|
(let-match
|
||||||
(~admin/data-value-cell :value (get col "value") :value-type (get col "type")))))
|
{:value value :key key :type type}
|
||||||
|
col
|
||||||
|
(tr
|
||||||
|
:class "border-t border-neutral-200 align-top"
|
||||||
|
(td :class "px-3 py-2 whitespace-nowrap text-neutral-600" key)
|
||||||
|
(td
|
||||||
|
:class "px-3 py-2 align-top"
|
||||||
|
(~admin/data-value-cell :value value :value-type type)))))
|
||||||
(or columns (list)))))))
|
(or columns (list)))))))
|
||||||
|
|
||||||
(defcomp ~admin/data-relationship-item (&key index summary children)
|
(defcomp
|
||||||
(tr :class "border-t border-neutral-200 align-top"
|
~admin/data-relationship-item
|
||||||
|
(&key index summary children)
|
||||||
|
(tr
|
||||||
|
:class "border-t border-neutral-200 align-top"
|
||||||
(td :class "px-2 py-1 whitespace-nowrap align-top" (str index))
|
(td :class "px-2 py-1 whitespace-nowrap align-top" (str index))
|
||||||
(td :class "px-2 py-1 align-top"
|
(td
|
||||||
(pre :class "whitespace-pre-wrap break-words break-all text-xs"
|
:class "px-2 py-1 align-top"
|
||||||
|
(pre
|
||||||
|
:class "whitespace-pre-wrap break-words break-all text-xs"
|
||||||
(code summary))
|
(code summary))
|
||||||
(when children
|
(when
|
||||||
(div :class "mt-2 pl-3 border-l border-neutral-200"
|
children
|
||||||
(~admin/data-model-content
|
(div
|
||||||
:columns (get children "columns")
|
:class "mt-2 pl-3 border-l border-neutral-200"
|
||||||
:relationships (get children "relationships")))))))
|
(let-match
|
||||||
|
{:relationships relationships :columns columns}
|
||||||
|
children
|
||||||
|
(~admin/data-model-content
|
||||||
|
:columns columns
|
||||||
|
:relationships relationships)))))))
|
||||||
|
|
||||||
(defcomp ~admin/data-relationship (&key name cardinality class-name loaded value)
|
(defcomp ~admin/data-relationship (&key name cardinality class-name loaded value)
|
||||||
(div :class "rounded-xl border border-neutral-200"
|
(div :class "rounded-xl border border-neutral-200"
|
||||||
@@ -463,29 +610,50 @@
|
|||||||
:columns (get (get value "children") "columns")
|
:columns (get (get value "children") "columns")
|
||||||
:relationships (get (get value "children") "relationships"))))))))))
|
:relationships (get (get value "children") "relationships"))))))))))
|
||||||
|
|
||||||
(defcomp ~admin/data-model-content (&key columns relationships)
|
(defcomp
|
||||||
(div :class "space-y-4"
|
~admin/data-model-content
|
||||||
|
(&key columns relationships)
|
||||||
|
(div
|
||||||
|
:class "space-y-4"
|
||||||
(~admin/data-scalar-table :columns columns)
|
(~admin/data-scalar-table :columns columns)
|
||||||
(when (not (empty? (or relationships (list))))
|
(when
|
||||||
(div :class "space-y-3"
|
(not (empty? (or relationships (list))))
|
||||||
(map (lambda (rel)
|
(div
|
||||||
(~admin/data-relationship
|
:class "space-y-3"
|
||||||
:name (get rel "name")
|
(map
|
||||||
:cardinality (get rel "cardinality")
|
(lambda
|
||||||
:class-name (get rel "class_name")
|
(rel)
|
||||||
:loaded (get rel "loaded")
|
(let-match
|
||||||
:value (get rel "value")))
|
{:cardinality cardinality :class_name class-name :loaded loaded :value value :name name}
|
||||||
|
rel
|
||||||
|
(~admin/data-relationship
|
||||||
|
:name name
|
||||||
|
:cardinality cardinality
|
||||||
|
:class-name class-name
|
||||||
|
:loaded loaded
|
||||||
|
:value value)))
|
||||||
relationships)))))
|
relationships)))))
|
||||||
|
|
||||||
(defcomp ~admin/data-table-content (&key tablename model-data)
|
(defcomp
|
||||||
(if (not model-data)
|
~admin/data-table-content
|
||||||
|
(&key tablename model-data)
|
||||||
|
(if
|
||||||
|
(not model-data)
|
||||||
(div :class "px-4 py-8 text-stone-400" "No post data available.")
|
(div :class "px-4 py-8 text-stone-400" "No post data available.")
|
||||||
(div :class "px-4 py-8"
|
(div
|
||||||
(div :class "mb-6 text-sm text-neutral-500"
|
:class "px-4 py-8"
|
||||||
"Model: " (code "Post") " \u2022 Table: " (code tablename))
|
(div
|
||||||
(~admin/data-model-content
|
:class "mb-6 text-sm text-neutral-500"
|
||||||
:columns (get model-data "columns")
|
"Model: "
|
||||||
:relationships (get model-data "relationships")))))
|
(code "Post")
|
||||||
|
" • Table: "
|
||||||
|
(code tablename))
|
||||||
|
(let-match
|
||||||
|
{:relationships relationships :columns columns}
|
||||||
|
model-data
|
||||||
|
(~admin/data-model-content
|
||||||
|
:columns columns
|
||||||
|
:relationships relationships)))))
|
||||||
|
|
||||||
;; ---------------------------------------------------------------------------
|
;; ---------------------------------------------------------------------------
|
||||||
;; Calendar month view for browsing/toggling entries (B1)
|
;; Calendar month view for browsing/toggling entries (B1)
|
||||||
@@ -518,59 +686,117 @@
|
|||||||
:sx-on:afterSwap "document.body.dispatchEvent(new CustomEvent('entryToggled'))"
|
:sx-on:afterSwap "document.body.dispatchEvent(new CustomEvent('entryToggled'))"
|
||||||
(span :class "truncate block" name)))
|
(span :class "truncate block" name)))
|
||||||
|
|
||||||
(defcomp ~admin/calendar-view (&key cal-id year month-name
|
(defcomp
|
||||||
current-url prev-month-url prev-year-url
|
~admin/calendar-view
|
||||||
next-month-url next-year-url
|
(&key
|
||||||
weekday-names days csrf)
|
cal-id
|
||||||
(let* ((target (str "#calendar-view-" cal-id)))
|
year
|
||||||
(div :id (str "calendar-view-" cal-id)
|
month-name
|
||||||
:sx-get current-url :sx-trigger "entryToggled from:body" :sx-swap "outerHTML"
|
current-url
|
||||||
(header :class "flex items-center justify-center mb-4"
|
prev-month-url
|
||||||
(nav :class "flex items-center gap-2 text-xl"
|
prev-year-url
|
||||||
(a :class "px-2 py-1 hover:bg-stone-100 rounded"
|
next-month-url
|
||||||
:sx-get prev-year-url :sx-target target :sx-swap "outerHTML"
|
next-year-url
|
||||||
|
weekday-names
|
||||||
|
days
|
||||||
|
csrf)
|
||||||
|
(let*
|
||||||
|
((target (str "#calendar-view-" cal-id)))
|
||||||
|
(div
|
||||||
|
:id (str "calendar-view-" cal-id)
|
||||||
|
:sx-get current-url
|
||||||
|
:sx-trigger "entryToggled from:body"
|
||||||
|
:sx-swap "outerHTML"
|
||||||
|
(header
|
||||||
|
:class "flex items-center justify-center mb-4"
|
||||||
|
(nav
|
||||||
|
:class "flex items-center gap-2 text-xl"
|
||||||
|
(a
|
||||||
|
:class "px-2 py-1 hover:bg-stone-100 rounded"
|
||||||
|
:sx-get prev-year-url
|
||||||
|
:sx-target target
|
||||||
|
:sx-swap "outerHTML"
|
||||||
(raw! "«"))
|
(raw! "«"))
|
||||||
(a :class "px-2 py-1 hover:bg-stone-100 rounded"
|
(a
|
||||||
:sx-get prev-month-url :sx-target target :sx-swap "outerHTML"
|
:class "px-2 py-1 hover:bg-stone-100 rounded"
|
||||||
|
:sx-get prev-month-url
|
||||||
|
:sx-target target
|
||||||
|
:sx-swap "outerHTML"
|
||||||
(raw! "‹"))
|
(raw! "‹"))
|
||||||
(div :class "px-3 font-medium" (str month-name " " year))
|
(div :class "px-3 font-medium" (str month-name " " year))
|
||||||
(a :class "px-2 py-1 hover:bg-stone-100 rounded"
|
(a
|
||||||
:sx-get next-month-url :sx-target target :sx-swap "outerHTML"
|
:class "px-2 py-1 hover:bg-stone-100 rounded"
|
||||||
|
:sx-get next-month-url
|
||||||
|
:sx-target target
|
||||||
|
:sx-swap "outerHTML"
|
||||||
(raw! "›"))
|
(raw! "›"))
|
||||||
(a :class "px-2 py-1 hover:bg-stone-100 rounded"
|
(a
|
||||||
:sx-get next-year-url :sx-target target :sx-swap "outerHTML"
|
:class "px-2 py-1 hover:bg-stone-100 rounded"
|
||||||
|
:sx-get next-year-url
|
||||||
|
:sx-target target
|
||||||
|
:sx-swap "outerHTML"
|
||||||
(raw! "»"))))
|
(raw! "»"))))
|
||||||
(div :class "rounded border bg-white"
|
(div
|
||||||
(div :class "hidden sm:grid grid-cols-7 text-center text-xs font-semibold text-stone-700 bg-stone-50 border-b"
|
:class "rounded border bg-white"
|
||||||
(map (lambda (wd) (div :class "py-2" wd)) (or weekday-names (list))))
|
(div
|
||||||
(div :class "grid grid-cols-1 sm:grid-cols-7 gap-px bg-stone-200"
|
:class "hidden sm:grid grid-cols-7 text-center text-xs font-semibold text-stone-700 bg-stone-50 border-b"
|
||||||
(map (lambda (day)
|
(map
|
||||||
(let* ((extra-cls (if (get day "in_month") "" " bg-stone-50 text-stone-400"))
|
(lambda (wd) (div :class "py-2" wd))
|
||||||
(entries (or (get day "entries") (list))))
|
(or weekday-names (list))))
|
||||||
(div :class (str "min-h-20 bg-white px-2 py-2 text-xs" extra-cls)
|
(div
|
||||||
(div :class "font-medium mb-1" (str (get day "day")))
|
:class "grid grid-cols-1 sm:grid-cols-7 gap-px bg-stone-200"
|
||||||
(when (not (empty? entries))
|
(map
|
||||||
(div :class "space-y-0.5"
|
(lambda
|
||||||
(map (lambda (e)
|
(day)
|
||||||
(if (get e "is_associated")
|
(let-match
|
||||||
(~admin/cal-entry-associated
|
{:entries entries* :in_month in-month :day day-num}
|
||||||
:name (get e "name") :toggle-url (get e "toggle_url") :csrf csrf)
|
day
|
||||||
(~admin/cal-entry-unassociated
|
(let*
|
||||||
:name (get e "name") :toggle-url (get e "toggle_url") :csrf csrf)))
|
((extra-cls (if in-month "" " bg-stone-50 text-stone-400"))
|
||||||
entries))))))
|
(entries (or entries* (list))))
|
||||||
|
(div
|
||||||
|
:class (str "min-h-20 bg-white px-2 py-2 text-xs" extra-cls)
|
||||||
|
(div :class "font-medium mb-1" (str day-num))
|
||||||
|
(when
|
||||||
|
(not (empty? entries))
|
||||||
|
(div
|
||||||
|
:class "space-y-0.5"
|
||||||
|
(map
|
||||||
|
(lambda
|
||||||
|
(e)
|
||||||
|
(let-match
|
||||||
|
{:is_associated is-associated :toggle_url toggle-url :name name}
|
||||||
|
e
|
||||||
|
(if
|
||||||
|
is-associated
|
||||||
|
(~admin/cal-entry-associated
|
||||||
|
:name name
|
||||||
|
:toggle-url toggle-url
|
||||||
|
:csrf csrf)
|
||||||
|
(~admin/cal-entry-unassociated
|
||||||
|
:name name
|
||||||
|
:toggle-url toggle-url
|
||||||
|
:csrf csrf))))
|
||||||
|
entries)))))))
|
||||||
(or days (list))))))))
|
(or days (list))))))))
|
||||||
|
|
||||||
;; ---------------------------------------------------------------------------
|
;; ---------------------------------------------------------------------------
|
||||||
;; Nav entries OOB — renders associated entry/calendar items in scroll wrapper (B2)
|
;; Nav entries OOB — renders associated entry/calendar items in scroll wrapper (B2)
|
||||||
;; ---------------------------------------------------------------------------
|
;; ---------------------------------------------------------------------------
|
||||||
|
|
||||||
(defcomp ~admin/nav-entries-oob (&key entries calendars)
|
(defcomp
|
||||||
(let* ((entry-list (or entries (list)))
|
~admin/nav-entries-oob
|
||||||
(cal-list (or calendars (list)))
|
(&key entries calendars)
|
||||||
(has-items (or (not (empty? entry-list)) (not (empty? cal-list))))
|
(let*
|
||||||
(nav-cls "justify-center cursor-pointer flex flex-row items-center gap-2 rounded bg-stone-200 text-black [.hover-capable_&]:hover:bg-yellow-300 aria-selected:bg-stone-500 aria-selected:text-white [.hover-capable_&[aria-selected=true]:hover]:bg-orange-500 p-2")
|
((entry-list (or entries (list)))
|
||||||
(scroll-hs "on load or scroll if window.innerWidth >= 640 and my.scrollWidth > my.clientWidth remove .hidden from .entries-nav-arrow add .flex to .entries-nav-arrow else add .hidden to .entries-nav-arrow remove .flex from .entries-nav-arrow end"))
|
(cal-list (or calendars (list)))
|
||||||
(if (not has-items)
|
(has-items (or (not (empty? entry-list)) (not (empty? cal-list))))
|
||||||
|
(nav-cls
|
||||||
|
"justify-center cursor-pointer flex flex-row items-center gap-2 rounded bg-stone-200 text-black [.hover-capable_&]:hover:bg-yellow-300 aria-selected:bg-stone-500 aria-selected:text-white [.hover-capable_&[aria-selected=true]:hover]:bg-orange-500 p-2")
|
||||||
|
(scroll-hs
|
||||||
|
"on load or scroll if window.innerWidth >= 640 and my.scrollWidth > my.clientWidth remove .hidden from .entries-nav-arrow add .flex to .entries-nav-arrow else add .hidden to .entries-nav-arrow remove .flex from .entries-nav-arrow end"))
|
||||||
|
(if
|
||||||
|
(not has-items)
|
||||||
(~shared:nav/blog-nav-entries-empty)
|
(~shared:nav/blog-nav-entries-empty)
|
||||||
(~shared:misc/scroll-nav-wrapper
|
(~shared:misc/scroll-nav-wrapper
|
||||||
:wrapper-id "entries-calendars-nav-wrapper"
|
:wrapper-id "entries-calendars-nav-wrapper"
|
||||||
@@ -580,14 +806,27 @@
|
|||||||
:scroll-hs scroll-hs
|
:scroll-hs scroll-hs
|
||||||
:right-hs "on click set #associated-items-container.scrollLeft to #associated-items-container.scrollLeft + 200"
|
:right-hs "on click set #associated-items-container.scrollLeft to #associated-items-container.scrollLeft + 200"
|
||||||
:items (<>
|
:items (<>
|
||||||
(map (lambda (e)
|
(map
|
||||||
(~shared:navigation/calendar-entry-nav
|
(lambda
|
||||||
:href (get e "href") :nav-class nav-cls
|
(e)
|
||||||
:name (get e "name") :date-str (get e "date_str")))
|
(let-match
|
||||||
|
{:href href :date_str date-str :name name}
|
||||||
|
e
|
||||||
|
(~shared:navigation/calendar-entry-nav
|
||||||
|
:href href
|
||||||
|
:nav-class nav-cls
|
||||||
|
:name name
|
||||||
|
:date-str date-str)))
|
||||||
entry-list)
|
entry-list)
|
||||||
(map (lambda (c)
|
(map
|
||||||
(~shared:nav/blog-nav-calendar-item
|
(lambda
|
||||||
:href (get c "href") :nav-cls nav-cls
|
(c)
|
||||||
:name (get c "name")))
|
(let-match
|
||||||
|
{:href href :name name}
|
||||||
|
c
|
||||||
|
(~shared:nav/blog-nav-calendar-item
|
||||||
|
:href href
|
||||||
|
:nav-cls nav-cls
|
||||||
|
:name name)))
|
||||||
cal-list))
|
cal-list))
|
||||||
:oob true))))
|
:oob true))))
|
||||||
|
|||||||
@@ -159,91 +159,147 @@
|
|||||||
:btn (~page/tw-plus))))))
|
:btn (~page/tw-plus))))))
|
||||||
|
|
||||||
;; Entry card (list view) from data
|
;; Entry card (list view) from data
|
||||||
(defcomp ~entries/entry-card-from-data (&key entry-href name day-href
|
(defcomp
|
||||||
page-badge-href page-badge-title cal-name
|
~entries/entry-card-from-data
|
||||||
date-str start-time end-time is-page-scoped
|
(&key
|
||||||
cost has-ticket ticket-data)
|
entry-href
|
||||||
|
name
|
||||||
|
day-href
|
||||||
|
page-badge-href
|
||||||
|
page-badge-title
|
||||||
|
cal-name
|
||||||
|
date-str
|
||||||
|
start-time
|
||||||
|
end-time
|
||||||
|
is-page-scoped
|
||||||
|
cost
|
||||||
|
has-ticket
|
||||||
|
ticket-data)
|
||||||
(~entries/entry-card
|
(~entries/entry-card
|
||||||
:title (if entry-href
|
:title (if
|
||||||
|
entry-href
|
||||||
(~entries/entry-title-linked :href entry-href :name name)
|
(~entries/entry-title-linked :href entry-href :name name)
|
||||||
(~entries/entry-title-plain :name name))
|
(~entries/entry-title-plain :name name))
|
||||||
:badges (<>
|
:badges (<>
|
||||||
(when page-badge-title
|
(when
|
||||||
(~entries/entry-page-badge :href page-badge-href :title page-badge-title))
|
page-badge-title
|
||||||
(when cal-name
|
(~entries/entry-page-badge
|
||||||
(~entries/entry-cal-badge :name cal-name)))
|
:href page-badge-href
|
||||||
|
:title page-badge-title))
|
||||||
|
(when cal-name (~entries/entry-cal-badge :name cal-name)))
|
||||||
:time-parts (<>
|
:time-parts (<>
|
||||||
(when (and day-href (not is-page-scoped))
|
(when
|
||||||
|
(and day-href (not is-page-scoped))
|
||||||
(~entries/entry-time-linked :href day-href :date-str date-str))
|
(~entries/entry-time-linked :href day-href :date-str date-str))
|
||||||
(when (and (not day-href) (not is-page-scoped) date-str)
|
(when
|
||||||
|
(and (not day-href) (not is-page-scoped) date-str)
|
||||||
(~entries/entry-time-plain :date-str date-str))
|
(~entries/entry-time-plain :date-str date-str))
|
||||||
start-time
|
start-time
|
||||||
(when end-time (str " \u2013 " end-time)))
|
(when end-time (str " – " end-time)))
|
||||||
:cost (when cost (~entries/entry-cost :cost cost))
|
:cost (when cost (~entries/entry-cost :cost cost))
|
||||||
:widget (when has-ticket
|
:widget (when
|
||||||
(~entries/entry-widget-wrapper
|
has-ticket
|
||||||
:widget (~entries/tw-widget-from-data
|
(let-match
|
||||||
:entry-id (get ticket-data "entry-id")
|
{:csrf csrf :entry-id entry-id :qty qty :price price :ticket-url ticket-url}
|
||||||
:price (get ticket-data "price")
|
ticket-data
|
||||||
:qty (get ticket-data "qty")
|
(~entries/entry-widget-wrapper
|
||||||
:ticket-url (get ticket-data "ticket-url")
|
:widget (~entries/tw-widget-from-data
|
||||||
:csrf (get ticket-data "csrf"))))))
|
:entry-id entry-id
|
||||||
|
:price price
|
||||||
|
:qty qty
|
||||||
|
:ticket-url ticket-url
|
||||||
|
:csrf csrf))))))
|
||||||
|
|
||||||
;; Entry card (tile view) from data
|
;; Entry card (tile view) from data
|
||||||
(defcomp ~entries/entry-card-tile-from-data (&key entry-href name day-href
|
(defcomp
|
||||||
page-badge-href page-badge-title cal-name
|
~entries/entry-card-tile-from-data
|
||||||
date-str time-str
|
(&key
|
||||||
cost has-ticket ticket-data)
|
entry-href
|
||||||
|
name
|
||||||
|
day-href
|
||||||
|
page-badge-href
|
||||||
|
page-badge-title
|
||||||
|
cal-name
|
||||||
|
date-str
|
||||||
|
time-str
|
||||||
|
cost
|
||||||
|
has-ticket
|
||||||
|
ticket-data)
|
||||||
(~entries/entry-card-tile
|
(~entries/entry-card-tile
|
||||||
:title (if entry-href
|
:title (if
|
||||||
|
entry-href
|
||||||
(~entries/entry-title-tile-linked :href entry-href :name name)
|
(~entries/entry-title-tile-linked :href entry-href :name name)
|
||||||
(~entries/entry-title-tile-plain :name name))
|
(~entries/entry-title-tile-plain :name name))
|
||||||
:badges (<>
|
:badges (<>
|
||||||
(when page-badge-title
|
(when
|
||||||
(~entries/entry-page-badge :href page-badge-href :title page-badge-title))
|
page-badge-title
|
||||||
(when cal-name
|
(~entries/entry-page-badge
|
||||||
(~entries/entry-cal-badge :name cal-name)))
|
:href page-badge-href
|
||||||
|
:title page-badge-title))
|
||||||
|
(when cal-name (~entries/entry-cal-badge :name cal-name)))
|
||||||
:time time-str
|
:time time-str
|
||||||
:cost (when cost (~entries/entry-cost :cost cost))
|
:cost (when cost (~entries/entry-cost :cost cost))
|
||||||
:widget (when has-ticket
|
:widget (when
|
||||||
(~entries/entry-tile-widget-wrapper
|
has-ticket
|
||||||
:widget (~entries/tw-widget-from-data
|
(let-match
|
||||||
:entry-id (get ticket-data "entry-id")
|
{:csrf csrf :entry-id entry-id :qty qty :price price :ticket-url ticket-url}
|
||||||
:price (get ticket-data "price")
|
ticket-data
|
||||||
:qty (get ticket-data "qty")
|
(~entries/entry-tile-widget-wrapper
|
||||||
:ticket-url (get ticket-data "ticket-url")
|
:widget (~entries/tw-widget-from-data
|
||||||
:csrf (get ticket-data "csrf"))))))
|
:entry-id entry-id
|
||||||
|
:price price
|
||||||
|
:qty qty
|
||||||
|
:ticket-url ticket-url
|
||||||
|
:csrf csrf))))))
|
||||||
|
|
||||||
;; Entry cards list (with date separators + sentinel) from data
|
;; Entry cards list (with date separators + sentinel) from data
|
||||||
(defcomp ~entries/entry-cards-from-data (&key items view page has-more next-url)
|
(defcomp
|
||||||
|
~entries/entry-cards-from-data
|
||||||
|
(&key items view page has-more next-url)
|
||||||
(<>
|
(<>
|
||||||
(map (lambda (item)
|
(map
|
||||||
(if (get item "is-separator")
|
(lambda
|
||||||
(~entries/date-separator :date-str (get item "date-str"))
|
(item)
|
||||||
(if (= view "tile")
|
(let-match
|
||||||
(~entries/entry-card-tile-from-data
|
{:date-str date-str :time-str time-str :has-ticket has-ticket :is-separator is-separator :ticket-data ticket-data :day-href day-href :page-badge-title page-badge-title :entry-href entry-href :start-time start-time :end-time end-time :is-page-scoped is-page-scoped :page-badge-href page-badge-href :cal-name cal-name :cost cost :name name}
|
||||||
:entry-href (get item "entry-href") :name (get item "name")
|
item
|
||||||
:day-href (get item "day-href")
|
(if
|
||||||
:page-badge-href (get item "page-badge-href")
|
is-separator
|
||||||
:page-badge-title (get item "page-badge-title")
|
(~entries/date-separator :date-str date-str)
|
||||||
:cal-name (get item "cal-name")
|
(if
|
||||||
:date-str (get item "date-str") :time-str (get item "time-str")
|
(= view "tile")
|
||||||
:cost (get item "cost") :has-ticket (get item "has-ticket")
|
(~entries/entry-card-tile-from-data
|
||||||
:ticket-data (get item "ticket-data"))
|
:entry-href entry-href
|
||||||
(~entries/entry-card-from-data
|
:name name
|
||||||
:entry-href (get item "entry-href") :name (get item "name")
|
:day-href day-href
|
||||||
:day-href (get item "day-href")
|
:page-badge-href page-badge-href
|
||||||
:page-badge-href (get item "page-badge-href")
|
:page-badge-title page-badge-title
|
||||||
:page-badge-title (get item "page-badge-title")
|
:cal-name cal-name
|
||||||
:cal-name (get item "cal-name")
|
:date-str date-str
|
||||||
:date-str (get item "date-str")
|
:time-str time-str
|
||||||
:start-time (get item "start-time") :end-time (get item "end-time")
|
:cost cost
|
||||||
:is-page-scoped (get item "is-page-scoped")
|
:has-ticket has-ticket
|
||||||
:cost (get item "cost") :has-ticket (get item "has-ticket")
|
:ticket-data ticket-data)
|
||||||
:ticket-data (get item "ticket-data")))))
|
(~entries/entry-card-from-data
|
||||||
|
:entry-href entry-href
|
||||||
|
:name name
|
||||||
|
:day-href day-href
|
||||||
|
:page-badge-href page-badge-href
|
||||||
|
:page-badge-title page-badge-title
|
||||||
|
:cal-name cal-name
|
||||||
|
:date-str date-str
|
||||||
|
:start-time start-time
|
||||||
|
:end-time end-time
|
||||||
|
:is-page-scoped is-page-scoped
|
||||||
|
:cost cost
|
||||||
|
:has-ticket has-ticket
|
||||||
|
:ticket-data ticket-data)))))
|
||||||
(or items (list)))
|
(or items (list)))
|
||||||
(when has-more
|
(when
|
||||||
(~shared:misc/sentinel-simple :id (str "sentinel-" page) :next-url next-url))))
|
has-more
|
||||||
|
(~shared:misc/sentinel-simple
|
||||||
|
:id (str "sentinel-" page)
|
||||||
|
:next-url next-url))))
|
||||||
|
|
||||||
;; Events main panel (toggle + cards grid) from data
|
;; Events main panel (toggle + cards grid) from data
|
||||||
(defcomp ~entries/main-panel-from-data (&key toggle items view page has-more next-url)
|
(defcomp ~entries/main-panel-from-data (&key toggle items view page has-more next-url)
|
||||||
|
|||||||
@@ -323,28 +323,43 @@
|
|||||||
;; ---------------------------------------------------------------------------
|
;; ---------------------------------------------------------------------------
|
||||||
|
|
||||||
;; Day checkboxes from data — replaces Python loop
|
;; Day checkboxes from data — replaces Python loop
|
||||||
(defcomp ~forms/day-checkboxes-from-data (&key days-data all-checked)
|
(defcomp
|
||||||
|
~forms/day-checkboxes-from-data
|
||||||
|
(&key days-data all-checked)
|
||||||
(<>
|
(<>
|
||||||
(~forms/day-all-checkbox :checked (when all-checked "checked"))
|
(~forms/day-all-checkbox :checked (when all-checked "checked"))
|
||||||
(map (lambda (d)
|
(map
|
||||||
(~forms/day-checkbox
|
(lambda
|
||||||
:name (get d "name")
|
(d)
|
||||||
:label (get d "label")
|
(let-match
|
||||||
:checked (when (get d "checked") "checked")))
|
{:checked checked :label label :name name}
|
||||||
|
d
|
||||||
|
(~forms/day-checkbox
|
||||||
|
:name name
|
||||||
|
:label label
|
||||||
|
:checked (when checked "checked"))))
|
||||||
(or days-data (list)))))
|
(or days-data (list)))))
|
||||||
|
|
||||||
;; Slot options from data — replaces _slot_options_html Python loop
|
;; Slot options from data — replaces _slot_options_html Python loop
|
||||||
(defcomp ~forms/slot-options-from-data (&key slots)
|
(defcomp
|
||||||
(<> (map (lambda (s)
|
~forms/slot-options-from-data
|
||||||
(~forms/slot-option
|
(&key slots)
|
||||||
:value (get s "value")
|
(<>
|
||||||
:data-start (get s "data-start")
|
(map
|
||||||
:data-end (get s "data-end")
|
(lambda
|
||||||
:data-flexible (get s "data-flexible")
|
(s)
|
||||||
:data-cost (get s "data-cost")
|
(let-match
|
||||||
:selected (get s "selected")
|
{:data-end data-end :data-flexible data-flexible :selected selected :value value :data-cost data-cost :label label :data-start data-start}
|
||||||
:label (get s "label")))
|
s
|
||||||
(or slots (list)))))
|
(~forms/slot-option
|
||||||
|
:value value
|
||||||
|
:data-start data-start
|
||||||
|
:data-end data-end
|
||||||
|
:data-flexible data-flexible
|
||||||
|
:data-cost data-cost
|
||||||
|
:selected selected
|
||||||
|
:label label)))
|
||||||
|
(or slots (list)))))
|
||||||
|
|
||||||
;; Slot picker from data — wraps picker + options
|
;; Slot picker from data — wraps picker + options
|
||||||
(defcomp ~forms/slot-picker-from-data (&key id slots)
|
(defcomp ~forms/slot-picker-from-data (&key id slots)
|
||||||
|
|||||||
@@ -5,155 +5,247 @@
|
|||||||
;; Auto-fetching header macros — calendar, day, entry, slot, tickets
|
;; Auto-fetching header macros — calendar, day, entry, slot, tickets
|
||||||
;; ---------------------------------------------------------------------------
|
;; ---------------------------------------------------------------------------
|
||||||
|
|
||||||
(defmacro ~events-calendar-header-auto (oob)
|
(defmacro
|
||||||
|
~events-calendar-header-auto
|
||||||
|
(oob)
|
||||||
"Calendar header row using (events-calendar-ctx)."
|
"Calendar header row using (events-calendar-ctx)."
|
||||||
(quasiquote
|
(quasiquote
|
||||||
(let ((__cal (events-calendar-ctx))
|
(let
|
||||||
(__sc (select-colours)))
|
((__cal (events-calendar-ctx)) (__sc (select-colours)))
|
||||||
(when (get __cal "slug")
|
(let-match
|
||||||
(~shared:layout/menu-row-sx :id "calendar-row" :level 3
|
{:description description :slug slug :name name}
|
||||||
:link-href (url-for "calendar.get"
|
__cal
|
||||||
:calendar-slug (get __cal "slug"))
|
(when
|
||||||
:link-label-content (~header/calendar-label
|
slug
|
||||||
:name (get __cal "name")
|
(~shared:layout/menu-row-sx
|
||||||
:description (get __cal "description"))
|
:id "calendar-row"
|
||||||
:nav (<>
|
:level 3
|
||||||
(~shared:layout/nav-link :href (url-for "defpage_slots_listing"
|
:link-href (url-for "calendar.get" :calendar-slug slug)
|
||||||
:calendar-slug (get __cal "slug"))
|
:link-label-content (~header/calendar-label :name name :description description)
|
||||||
:icon "fa fa-clock" :label "Slots"
|
:nav (<>
|
||||||
:select-colours __sc)
|
(~shared:layout/nav-link
|
||||||
(let ((__rights (app-rights)))
|
:href (url-for "defpage_slots_listing" :calendar-slug slug)
|
||||||
(when (get __rights "admin")
|
:icon "fa fa-clock"
|
||||||
(~shared:layout/nav-link :href (url-for "defpage_calendar_admin"
|
:label "Slots"
|
||||||
:calendar-slug (get __cal "slug"))
|
:select-colours __sc)
|
||||||
:icon "fa fa-cog"
|
(let
|
||||||
:select-colours __sc))))
|
((__rights (app-rights)))
|
||||||
:child-id "calendar-header-child"
|
(when
|
||||||
:oob (unquote oob))))))
|
(get __rights "admin")
|
||||||
|
(~shared:layout/nav-link
|
||||||
|
:href (url-for "defpage_calendar_admin" :calendar-slug slug)
|
||||||
|
:icon "fa fa-cog"
|
||||||
|
:select-colours __sc))))
|
||||||
|
:child-id "calendar-header-child"
|
||||||
|
:oob (unquote oob)))))))
|
||||||
|
|
||||||
(defmacro ~events-calendar-admin-header-auto (oob)
|
(defmacro
|
||||||
|
~events-calendar-admin-header-auto
|
||||||
|
(oob)
|
||||||
"Calendar admin header row."
|
"Calendar admin header row."
|
||||||
(quasiquote
|
(quasiquote
|
||||||
(let ((__cal (events-calendar-ctx))
|
(let
|
||||||
(__sc (select-colours)))
|
((__cal (events-calendar-ctx)) (__sc (select-colours)))
|
||||||
(when (get __cal "slug")
|
(let-match
|
||||||
(~shared:layout/menu-row-sx :id "calendar-admin-row" :level 4
|
{:slug slug}
|
||||||
:link-label "admin" :icon "fa fa-cog"
|
__cal
|
||||||
:nav (<>
|
(when
|
||||||
(~shared:layout/nav-link :href (url-for "defpage_slots_listing"
|
slug
|
||||||
:calendar-slug (get __cal "slug"))
|
(~shared:layout/menu-row-sx
|
||||||
:label "slots" :select-colours __sc)
|
:id "calendar-admin-row"
|
||||||
(~shared:layout/nav-link :href (url-for "calendar.admin.calendar_description_edit"
|
:level 4
|
||||||
:calendar-slug (get __cal "slug"))
|
:link-label "admin"
|
||||||
:label "description" :select-colours __sc))
|
:icon "fa fa-cog"
|
||||||
:child-id "calendar-admin-header-child"
|
:nav (<>
|
||||||
:oob (unquote oob))))))
|
(~shared:layout/nav-link
|
||||||
|
:href (url-for "defpage_slots_listing" :calendar-slug slug)
|
||||||
|
:label "slots"
|
||||||
|
:select-colours __sc)
|
||||||
|
(~shared:layout/nav-link
|
||||||
|
:href (url-for
|
||||||
|
"calendar.admin.calendar_description_edit"
|
||||||
|
:calendar-slug slug)
|
||||||
|
:label "description"
|
||||||
|
:select-colours __sc))
|
||||||
|
:child-id "calendar-admin-header-child"
|
||||||
|
:oob (unquote oob)))))))
|
||||||
|
|
||||||
(defmacro ~events-day-header-auto (oob)
|
(defmacro
|
||||||
|
~events-day-header-auto
|
||||||
|
(oob)
|
||||||
"Day header row using (events-day-ctx)."
|
"Day header row using (events-day-ctx)."
|
||||||
(quasiquote
|
(quasiquote
|
||||||
(let ((__day (events-day-ctx))
|
(let
|
||||||
(__cal (events-calendar-ctx)))
|
((__day (events-day-ctx)) (__cal (events-calendar-ctx)))
|
||||||
(when (get __day "date-str")
|
(let-match
|
||||||
(~shared:layout/menu-row-sx :id "day-row" :level 4
|
{:date-str date-str :nav nav :year year :day day :month month}
|
||||||
:link-href (url-for "calendar.day.show_day"
|
__day
|
||||||
:calendar-slug (get __cal "slug")
|
(when
|
||||||
:year (get __day "year")
|
date-str
|
||||||
:month (get __day "month")
|
(let-match
|
||||||
:day (get __day "day"))
|
{:slug cal-slug}
|
||||||
:link-label-content (~header/day-label
|
__cal
|
||||||
:date-str (get __day "date-str"))
|
(~shared:layout/menu-row-sx
|
||||||
:nav (get __day "nav")
|
:id "day-row"
|
||||||
:child-id "day-header-child"
|
:level 4
|
||||||
:oob (unquote oob))))))
|
:link-href (url-for
|
||||||
|
"calendar.day.show_day"
|
||||||
|
:calendar-slug cal-slug
|
||||||
|
:year year
|
||||||
|
:month month
|
||||||
|
:day day)
|
||||||
|
:link-label-content (~header/day-label :date-str date-str)
|
||||||
|
:nav nav
|
||||||
|
:child-id "day-header-child"
|
||||||
|
:oob (unquote oob))))))))
|
||||||
|
|
||||||
(defmacro ~events-day-admin-header-auto (oob)
|
(defmacro
|
||||||
|
~events-day-admin-header-auto
|
||||||
|
(oob)
|
||||||
"Day admin header row."
|
"Day admin header row."
|
||||||
(quasiquote
|
(quasiquote
|
||||||
(let ((__day (events-day-ctx))
|
(let
|
||||||
(__cal (events-calendar-ctx)))
|
((__day (events-day-ctx)) (__cal (events-calendar-ctx)))
|
||||||
(when (get __day "date-str")
|
(let-match
|
||||||
(~shared:layout/menu-row-sx :id "day-admin-row" :level 5
|
{:date-str date-str :year year :day day :month month}
|
||||||
:link-href (url-for "defpage_day_admin"
|
__day
|
||||||
:calendar-slug (get __cal "slug")
|
(when
|
||||||
:year (get __day "year")
|
date-str
|
||||||
:month (get __day "month")
|
(let-match
|
||||||
:day (get __day "day"))
|
{:slug cal-slug}
|
||||||
:link-label "admin" :icon "fa fa-cog"
|
__cal
|
||||||
:child-id "day-admin-header-child"
|
(~shared:layout/menu-row-sx
|
||||||
:oob (unquote oob))))))
|
:id "day-admin-row"
|
||||||
|
:level 5
|
||||||
|
:link-href (url-for
|
||||||
|
"defpage_day_admin"
|
||||||
|
:calendar-slug cal-slug
|
||||||
|
:year year
|
||||||
|
:month month
|
||||||
|
:day day)
|
||||||
|
:link-label "admin"
|
||||||
|
:icon "fa fa-cog"
|
||||||
|
:child-id "day-admin-header-child"
|
||||||
|
:oob (unquote oob))))))))
|
||||||
|
|
||||||
(defmacro ~events-entry-header-auto (oob)
|
(defmacro
|
||||||
|
~events-entry-header-auto
|
||||||
|
(oob)
|
||||||
"Entry header row using (events-entry-ctx)."
|
"Entry header row using (events-entry-ctx)."
|
||||||
(quasiquote
|
(quasiquote
|
||||||
(let ((__ectx (events-entry-ctx)))
|
(let
|
||||||
(when (get __ectx "id")
|
((__ectx (events-entry-ctx)))
|
||||||
(~shared:layout/menu-row-sx :id "entry-row" :level 5
|
(let-match
|
||||||
:link-href (get __ectx "link-href")
|
{:time-str time-str :nav nav :link-href link-href :id id :name name}
|
||||||
:link-label-content (~header/entry-label
|
__ectx
|
||||||
:entry-id (get __ectx "id")
|
(when
|
||||||
:title (~admin/entry-title :name (get __ectx "name"))
|
id
|
||||||
:times (~admin/entry-times :time-str (get __ectx "time-str")))
|
(~shared:layout/menu-row-sx
|
||||||
:nav (get __ectx "nav")
|
:id "entry-row"
|
||||||
:child-id "entry-header-child"
|
:level 5
|
||||||
:oob (unquote oob))))))
|
:link-href link-href
|
||||||
|
:link-label-content (~header/entry-label
|
||||||
|
:entry-id id
|
||||||
|
:title (~admin/entry-title :name name)
|
||||||
|
:times (~admin/entry-times :time-str time-str))
|
||||||
|
:nav nav
|
||||||
|
:child-id "entry-header-child"
|
||||||
|
:oob (unquote oob)))))))
|
||||||
|
|
||||||
(defmacro ~events-entry-admin-header-auto (oob)
|
(defmacro
|
||||||
|
~events-entry-admin-header-auto
|
||||||
|
(oob)
|
||||||
"Entry admin header row."
|
"Entry admin header row."
|
||||||
(quasiquote
|
(quasiquote
|
||||||
(let ((__ectx (events-entry-ctx)))
|
(let
|
||||||
(when (get __ectx "id")
|
((__ectx (events-entry-ctx)))
|
||||||
(~shared:layout/menu-row-sx :id "entry-admin-row" :level 6
|
(let-match
|
||||||
:link-href (get __ectx "admin-href")
|
{:admin-href admin-href :is-admin is-admin :ticket-types-href ticket-types-href :select-colours select-colours :id id}
|
||||||
:link-label "admin" :icon "fa fa-cog"
|
__ectx
|
||||||
:nav (when (get __ectx "is-admin")
|
(when
|
||||||
(~shared:layout/nav-link :href (get __ectx "ticket-types-href")
|
id
|
||||||
:label "ticket_types"
|
(~shared:layout/menu-row-sx
|
||||||
:select-colours (get __ectx "select-colours")))
|
:id "entry-admin-row"
|
||||||
:child-id "entry-admin-header-child"
|
:level 6
|
||||||
:oob (unquote oob))))))
|
:link-href admin-href
|
||||||
|
:link-label "admin"
|
||||||
|
:icon "fa fa-cog"
|
||||||
|
:nav (when
|
||||||
|
is-admin
|
||||||
|
(~shared:layout/nav-link
|
||||||
|
:href ticket-types-href
|
||||||
|
:label "ticket_types"
|
||||||
|
:select-colours select-colours))
|
||||||
|
:child-id "entry-admin-header-child"
|
||||||
|
:oob (unquote oob)))))))
|
||||||
|
|
||||||
(defmacro ~events-slot-header-auto (oob)
|
(defmacro
|
||||||
|
~events-slot-header-auto
|
||||||
|
(oob)
|
||||||
"Slot detail header row using (events-slot-ctx)."
|
"Slot detail header row using (events-slot-ctx)."
|
||||||
(quasiquote
|
(quasiquote
|
||||||
(let ((__slot (events-slot-ctx)))
|
(let
|
||||||
(when (get __slot "name")
|
((__slot (events-slot-ctx)))
|
||||||
(~shared:layout/menu-row-sx :id "slot-row" :level 5
|
(let-match
|
||||||
:link-label-content (~header/slot-label
|
{:description description :name name}
|
||||||
:name (get __slot "name")
|
__slot
|
||||||
:description (get __slot "description"))
|
(when
|
||||||
:child-id "slot-header-child"
|
name
|
||||||
:oob (unquote oob))))))
|
(~shared:layout/menu-row-sx
|
||||||
|
:id "slot-row"
|
||||||
|
:level 5
|
||||||
|
:link-label-content (~header/slot-label :name name :description description)
|
||||||
|
:child-id "slot-header-child"
|
||||||
|
:oob (unquote oob)))))))
|
||||||
|
|
||||||
(defmacro ~events-ticket-types-header-auto (oob)
|
(defmacro
|
||||||
|
~events-ticket-types-header-auto
|
||||||
|
(oob)
|
||||||
"Ticket types header row."
|
"Ticket types header row."
|
||||||
(quasiquote
|
(quasiquote
|
||||||
(let ((__ectx (events-entry-ctx))
|
(let
|
||||||
(__cal (events-calendar-ctx)))
|
((__ectx (events-entry-ctx)) (__cal (events-calendar-ctx)))
|
||||||
(when (get __ectx "id")
|
(let-match
|
||||||
(~shared:layout/menu-row-sx :id "ticket_types-row" :level 7
|
{:ticket-types-href ticket-types-href :id id}
|
||||||
:link-href (get __ectx "ticket-types-href")
|
__ectx
|
||||||
:link-label-content (<>
|
(when
|
||||||
(i :class "fa fa-ticket")
|
id
|
||||||
(div :class "shrink-0" "ticket types"))
|
(~shared:layout/menu-row-sx
|
||||||
:nav (~forms/admin-placeholder-nav)
|
:id "ticket_types-row"
|
||||||
:child-id "ticket_type-header-child"
|
:level 7
|
||||||
:oob (unquote oob))))))
|
:link-href ticket-types-href
|
||||||
|
:link-label-content (<>
|
||||||
|
(i :class "fa fa-ticket")
|
||||||
|
(div :class "shrink-0" "ticket types"))
|
||||||
|
:nav (~forms/admin-placeholder-nav)
|
||||||
|
:child-id "ticket_type-header-child"
|
||||||
|
:oob (unquote oob)))))))
|
||||||
|
|
||||||
(defmacro ~events-ticket-type-header-auto (oob)
|
(defmacro
|
||||||
|
~events-ticket-type-header-auto
|
||||||
|
(oob)
|
||||||
"Single ticket type header row using (events-ticket-type-ctx)."
|
"Single ticket type header row using (events-ticket-type-ctx)."
|
||||||
(quasiquote
|
(quasiquote
|
||||||
(let ((__tt (events-ticket-type-ctx)))
|
(let
|
||||||
(when (get __tt "id")
|
((__tt (events-ticket-type-ctx)))
|
||||||
(~shared:layout/menu-row-sx :id "ticket_type-row" :level 8
|
(let-match
|
||||||
:link-href (get __tt "link-href")
|
{:link-href link-href :id id :name name}
|
||||||
:link-label-content (div :class "flex flex-col md:flex-row md:gap-2 items-center"
|
__tt
|
||||||
(div :class "flex flex-row items-center gap-2"
|
(when
|
||||||
(i :class "fa fa-ticket")
|
id
|
||||||
(div :class "shrink-0" (get __tt "name"))))
|
(~shared:layout/menu-row-sx
|
||||||
:nav (~forms/admin-placeholder-nav)
|
:id "ticket_type-row"
|
||||||
:child-id "ticket_type-header-child-inner"
|
:level 8
|
||||||
:oob (unquote oob))))))
|
:link-href link-href
|
||||||
|
:link-label-content (div
|
||||||
|
:class "flex flex-col md:flex-row md:gap-2 items-baseline"
|
||||||
|
(div
|
||||||
|
:class "flex flex-row items-center gap-2"
|
||||||
|
(i :class "fa fa-ticket")
|
||||||
|
(div :class "shrink-0" name)))
|
||||||
|
:nav (~forms/admin-placeholder-nav)
|
||||||
|
:child-id "ticket_type-header-child-inner"
|
||||||
|
:oob (unquote oob)))))))
|
||||||
|
|
||||||
(defmacro ~events-markets-header-auto (oob)
|
(defmacro ~events-markets-header-auto (oob)
|
||||||
"Markets section header row."
|
"Markets section header row."
|
||||||
|
|||||||
@@ -98,24 +98,47 @@
|
|||||||
(~page/slot-description-oob :description (or description "")))))
|
(~page/slot-description-oob :description (or description "")))))
|
||||||
|
|
||||||
;; Slots table from data
|
;; Slots table from data
|
||||||
(defcomp ~page/slots-table-from-data (&key list-container slots pre-action add-url
|
(defcomp
|
||||||
tr-cls pill-cls action-btn hx-select csrf-hdr)
|
~page/slots-table-from-data
|
||||||
|
(&key
|
||||||
|
list-container
|
||||||
|
slots
|
||||||
|
pre-action
|
||||||
|
add-url
|
||||||
|
tr-cls
|
||||||
|
pill-cls
|
||||||
|
action-btn
|
||||||
|
hx-select
|
||||||
|
csrf-hdr)
|
||||||
(~page/slots-table
|
(~page/slots-table
|
||||||
:list-container list-container
|
:list-container list-container
|
||||||
:rows (if (empty? (or slots (list)))
|
:rows (if
|
||||||
|
(empty? (or slots (list)))
|
||||||
(~page/slots-empty-row)
|
(~page/slots-empty-row)
|
||||||
(<> (map (lambda (s)
|
(<>
|
||||||
(~page/slots-row
|
(map
|
||||||
:tr-cls tr-cls :slot-href (get s "slot-href")
|
(lambda
|
||||||
:pill-cls pill-cls :hx-select hx-select
|
(s)
|
||||||
:slot-name (get s "slot-name") :description (get s "description")
|
(let-match
|
||||||
:flexible (get s "flexible")
|
{:slot-name slot-name :time-str time-str :flexible flexible :description description :days days :cost-str cost-str :del-url del-url :slot-href slot-href}
|
||||||
:days (~page/days-pills-from-data :days (get s "days"))
|
s
|
||||||
:time-str (get s "time-str")
|
(~page/slots-row
|
||||||
:cost-str (get s "cost-str") :action-btn action-btn
|
:tr-cls tr-cls
|
||||||
:del-url (get s "del-url") :csrf-hdr csrf-hdr))
|
:slot-href slot-href
|
||||||
(or slots (list)))))
|
:pill-cls pill-cls
|
||||||
:pre-action pre-action :add-url add-url))
|
:hx-select hx-select
|
||||||
|
:slot-name slot-name
|
||||||
|
:description description
|
||||||
|
:flexible flexible
|
||||||
|
:days (~page/days-pills-from-data :days days)
|
||||||
|
:time-str time-str
|
||||||
|
:cost-str cost-str
|
||||||
|
:action-btn action-btn
|
||||||
|
:del-url del-url
|
||||||
|
:csrf-hdr csrf-hdr)))
|
||||||
|
(or slots (list)))))
|
||||||
|
:pre-action pre-action
|
||||||
|
:add-url add-url))
|
||||||
|
|
||||||
(defcomp ~page/ticket-type-col (&key label value)
|
(defcomp ~page/ticket-type-col (&key label value)
|
||||||
(div :class "flex flex-col"
|
(div :class "flex flex-col"
|
||||||
@@ -203,47 +226,87 @@
|
|||||||
:onclick hide-js "Cancel"))))
|
:onclick hide-js "Cancel"))))
|
||||||
|
|
||||||
;; Data-driven buy form — Python passes pre-resolved data, .sx does layout + iteration
|
;; Data-driven buy form — Python passes pre-resolved data, .sx does layout + iteration
|
||||||
(defcomp ~page/buy-form (&key entry-id info-sold info-remaining info-basket
|
(defcomp
|
||||||
ticket-types user-ticket-counts-by-type
|
~page/buy-form
|
||||||
user-ticket-count price-str adjust-url csrf state
|
(&key
|
||||||
my-tickets-href)
|
entry-id
|
||||||
(if (!= state "confirmed")
|
info-sold
|
||||||
|
info-remaining
|
||||||
|
info-basket
|
||||||
|
ticket-types
|
||||||
|
user-ticket-counts-by-type
|
||||||
|
user-ticket-count
|
||||||
|
price-str
|
||||||
|
adjust-url
|
||||||
|
csrf
|
||||||
|
state
|
||||||
|
my-tickets-href)
|
||||||
|
(if
|
||||||
|
(!= state "confirmed")
|
||||||
(~page/buy-not-confirmed :entry-id (str entry-id))
|
(~page/buy-not-confirmed :entry-id (str entry-id))
|
||||||
(let ((eid-s (str entry-id))
|
(let
|
||||||
(target (str "#ticket-buy-" entry-id)))
|
((eid-s (str entry-id)) (target (str "#ticket-buy-" entry-id)))
|
||||||
(div :id (str "ticket-buy-" entry-id) :class "rounded-xl border border-stone-200 bg-white p-4"
|
(div
|
||||||
(h3 :class "text-sm font-semibold text-stone-700 mb-3"
|
:id (str "ticket-buy-" entry-id)
|
||||||
(i :class "fa fa-ticket mr-1" :aria-hidden "true") "Tickets")
|
:class "rounded-xl border border-stone-200 bg-white p-4"
|
||||||
;; Info bar
|
(h3
|
||||||
(when (or info-sold info-remaining info-basket)
|
:class "text-sm font-semibold text-stone-700 mb-3"
|
||||||
(div :class "flex items-center gap-3 mb-3 text-xs text-stone-500"
|
(i :class "fa fa-ticket mr-1" :aria-hidden "true")
|
||||||
|
"Tickets")
|
||||||
|
(when
|
||||||
|
(or info-sold info-remaining info-basket)
|
||||||
|
(div
|
||||||
|
:class "flex items-center gap-3 mb-3 text-xs text-stone-500"
|
||||||
(when info-sold (span (str info-sold " sold")))
|
(when info-sold (span (str info-sold " sold")))
|
||||||
(when info-remaining (span (str info-remaining " remaining")))
|
(when info-remaining (span (str info-remaining " remaining")))
|
||||||
(when info-basket
|
(when
|
||||||
(span :class "text-emerald-600 font-medium"
|
info-basket
|
||||||
(i :class "fa fa-shopping-cart text-[0.6rem]" :aria-hidden "true")
|
(span
|
||||||
|
:class "text-emerald-600 font-medium"
|
||||||
|
(i
|
||||||
|
:class "fa fa-shopping-cart text-[0.6rem]"
|
||||||
|
:aria-hidden "true")
|
||||||
(str " " info-basket " in basket")))))
|
(str " " info-basket " in basket")))))
|
||||||
;; Body — multi-type or default
|
(if
|
||||||
(if (and ticket-types (not (empty? ticket-types)))
|
(and ticket-types (not (empty? ticket-types)))
|
||||||
(div :class "space-y-2"
|
(div
|
||||||
(map (fn (tt)
|
:class "space-y-2"
|
||||||
(let ((tt-count (if user-ticket-counts-by-type
|
(map
|
||||||
(get user-ticket-counts-by-type (str (get tt "id")) 0)
|
(fn
|
||||||
0))
|
(tt)
|
||||||
(tt-id (get tt "id")))
|
(let-match
|
||||||
(div :class "flex items-center justify-between p-3 rounded-lg bg-stone-50 border border-stone-100"
|
{:cost_str cost-str :id tt-id :name tt-name}
|
||||||
(div (div :class "font-medium text-sm" (get tt "name"))
|
tt
|
||||||
(div :class "text-xs text-stone-500" (get tt "cost_str")))
|
(let
|
||||||
(~page/adjust-inline :csrf csrf :adjust-url adjust-url :target target
|
((tt-count (if user-ticket-counts-by-type (get user-ticket-counts-by-type (str tt-id) 0) 0)))
|
||||||
:entry-id eid-s :count tt-count :ticket-type-id tt-id
|
(div
|
||||||
:my-tickets-href my-tickets-href))))
|
:class "flex items-center justify-between p-3 rounded-lg bg-stone-50 border border-stone-100"
|
||||||
|
(div
|
||||||
|
(div :class "font-medium text-sm" tt-name)
|
||||||
|
(div :class "text-xs text-stone-500" cost-str))
|
||||||
|
(~page/adjust-inline
|
||||||
|
:csrf csrf
|
||||||
|
:adjust-url adjust-url
|
||||||
|
:target target
|
||||||
|
:entry-id eid-s
|
||||||
|
:count tt-count
|
||||||
|
:ticket-type-id tt-id
|
||||||
|
:my-tickets-href my-tickets-href)))))
|
||||||
ticket-types))
|
ticket-types))
|
||||||
(<> (div :class "flex items-center justify-between mb-4"
|
(<>
|
||||||
(div (span :class "font-medium text-green-600" price-str)
|
(div
|
||||||
(span :class "text-sm text-stone-500 ml-2" "per ticket")))
|
:class "flex items-center justify-between mb-4"
|
||||||
(~page/adjust-inline :csrf csrf :adjust-url adjust-url :target target
|
(div
|
||||||
:entry-id eid-s :count (if user-ticket-count user-ticket-count 0)
|
(span :class "font-medium text-green-600" price-str)
|
||||||
:ticket-type-id nil :my-tickets-href my-tickets-href)))))))
|
(span :class "text-sm text-stone-500 ml-2" "per ticket")))
|
||||||
|
(~page/adjust-inline
|
||||||
|
:csrf csrf
|
||||||
|
:adjust-url adjust-url
|
||||||
|
:target target
|
||||||
|
:entry-id eid-s
|
||||||
|
:count (if user-ticket-count user-ticket-count 0)
|
||||||
|
:ticket-type-id nil
|
||||||
|
:my-tickets-href my-tickets-href)))))))
|
||||||
|
|
||||||
;; Inline +/- controls (used by both default and per-type)
|
;; Inline +/- controls (used by both default and per-type)
|
||||||
(defcomp ~page/adjust-inline (&key csrf adjust-url target entry-id count ticket-type-id my-tickets-href)
|
(defcomp ~page/adjust-inline (&key csrf adjust-url target entry-id count ticket-type-id my-tickets-href)
|
||||||
@@ -285,26 +348,53 @@
|
|||||||
"Tickets available once this event is confirmed."))
|
"Tickets available once this event is confirmed."))
|
||||||
|
|
||||||
|
|
||||||
(defcomp ~page/buy-result (&key entry-id tickets remaining my-tickets-href)
|
(defcomp
|
||||||
(let ((count (len tickets))
|
~page/buy-result
|
||||||
(suffix (if (= count 1) "" "s")))
|
(&key entry-id tickets remaining my-tickets-href)
|
||||||
(div :id (str "ticket-buy-" entry-id) :class "rounded-xl border border-emerald-200 bg-emerald-50 p-4"
|
(let
|
||||||
(div :class "flex items-center gap-2 mb-3"
|
((count (len tickets)) (suffix (if (= count 1) "" "s")))
|
||||||
|
(div
|
||||||
|
:id (str "ticket-buy-" entry-id)
|
||||||
|
:class "rounded-xl border border-emerald-200 bg-emerald-50 p-4"
|
||||||
|
(div
|
||||||
|
:class "flex items-center gap-2 mb-3"
|
||||||
(i :class "fa fa-check-circle text-emerald-600" :aria-hidden "true")
|
(i :class "fa fa-check-circle text-emerald-600" :aria-hidden "true")
|
||||||
(span :class "font-semibold text-emerald-800" (str count " ticket" suffix " reserved")))
|
(span
|
||||||
(div :class "space-y-2 mb-4"
|
:class "font-semibold text-emerald-800"
|
||||||
(map (fn (t)
|
(str count " ticket" suffix " reserved")))
|
||||||
(a :href (get t "href") :class "flex items-center justify-between p-2 rounded-lg bg-white border border-emerald-100 hover:border-emerald-300 transition text-sm"
|
(div
|
||||||
(div :class "flex items-center gap-2"
|
:class "space-y-2 mb-4"
|
||||||
(i :class "fa fa-ticket text-emerald-500" :aria-hidden "true")
|
(map
|
||||||
(span :class "font-mono text-xs text-stone-500" (get t "code_short")))
|
(fn
|
||||||
(span :class "text-xs text-emerald-600 font-medium" "View ticket")))
|
(t)
|
||||||
|
(let-match
|
||||||
|
{:href href :code_short code-short}
|
||||||
|
t
|
||||||
|
(a
|
||||||
|
:href href
|
||||||
|
:class "flex items-center justify-between p-2 rounded-lg bg-white border border-emerald-100 hover:border-emerald-300 transition text-sm"
|
||||||
|
(div
|
||||||
|
:class "flex items-center gap-2"
|
||||||
|
(i
|
||||||
|
:class "fa fa-ticket text-emerald-500"
|
||||||
|
:aria-hidden "true")
|
||||||
|
(span :class "font-mono text-xs text-stone-500" code-short))
|
||||||
|
(span
|
||||||
|
:class "text-xs text-emerald-600 font-medium"
|
||||||
|
"View ticket"))))
|
||||||
tickets))
|
tickets))
|
||||||
(when (not (nil? remaining))
|
(when
|
||||||
(let ((r-suffix (if (= remaining 1) "" "s")))
|
(not (nil? remaining))
|
||||||
(p :class "text-xs text-stone-500" (str remaining " ticket" r-suffix " remaining"))))
|
(let
|
||||||
(div :class "mt-3 flex gap-2"
|
((r-suffix (if (= remaining 1) "" "s")))
|
||||||
(a :href my-tickets-href :class "text-sm text-emerald-700 hover:text-emerald-900 underline"
|
(p
|
||||||
|
:class "text-xs text-stone-500"
|
||||||
|
(str remaining " ticket" r-suffix " remaining"))))
|
||||||
|
(div
|
||||||
|
:class "mt-3 flex gap-2"
|
||||||
|
(a
|
||||||
|
:href my-tickets-href
|
||||||
|
:class "text-sm text-emerald-700 hover:text-emerald-900 underline"
|
||||||
"View all my tickets")))))
|
"View all my tickets")))))
|
||||||
|
|
||||||
;; Single response wrappers for POST routes (include OOB cart icon)
|
;; Single response wrappers for POST routes (include OOB cart icon)
|
||||||
@@ -477,27 +567,46 @@
|
|||||||
(~page/post-img-placeholder)))
|
(~page/post-img-placeholder)))
|
||||||
|
|
||||||
;; Entry posts nav OOB from data
|
;; Entry posts nav OOB from data
|
||||||
(defcomp ~page/entry-posts-nav-oob-from-data (&key nav-btn posts)
|
(defcomp
|
||||||
(if (empty? (or posts (list)))
|
~page/entry-posts-nav-oob-from-data
|
||||||
|
(&key nav-btn posts)
|
||||||
|
(if
|
||||||
|
(empty? (or posts (list)))
|
||||||
(~page/entry-posts-nav-oob-empty)
|
(~page/entry-posts-nav-oob-empty)
|
||||||
(~page/entry-posts-nav-oob
|
(~page/entry-posts-nav-oob
|
||||||
:items (<> (map (lambda (p)
|
:items (<>
|
||||||
(~page/entry-nav-post
|
(map
|
||||||
:href (get p "href") :nav-btn nav-btn
|
(lambda
|
||||||
:img (~page/post-img-from-data :src (get p "img") :alt (get p "title"))
|
(p)
|
||||||
:title (get p "title")))
|
(let-match
|
||||||
posts)))))
|
{:href href :title title :img img}
|
||||||
|
p
|
||||||
|
(~page/entry-nav-post
|
||||||
|
:href href
|
||||||
|
:nav-btn nav-btn
|
||||||
|
:img (~page/post-img-from-data :src img :alt title)
|
||||||
|
:title title)))
|
||||||
|
posts)))))
|
||||||
|
|
||||||
;; Entry posts nav (non-OOB) from data — for desktop nav embedding
|
;; Entry posts nav (non-OOB) from data — for desktop nav embedding
|
||||||
(defcomp ~page/entry-posts-nav-inner-from-data (&key posts)
|
(defcomp
|
||||||
(when (not (empty? (or posts (list))))
|
~page/entry-posts-nav-inner-from-data
|
||||||
|
(&key posts)
|
||||||
|
(when
|
||||||
|
(not (empty? (or posts (list))))
|
||||||
(~page/entry-posts-nav-oob
|
(~page/entry-posts-nav-oob
|
||||||
:items (<> (map (lambda (p)
|
:items (<>
|
||||||
(~page/entry-nav-post-link
|
(map
|
||||||
:href (get p "href")
|
(lambda
|
||||||
:img (~page/post-img-from-data :src (get p "img") :alt (get p "title"))
|
(p)
|
||||||
:title (get p "title")))
|
(let-match
|
||||||
posts)))))
|
{:href href :title title :img img}
|
||||||
|
p
|
||||||
|
(~page/entry-nav-post-link
|
||||||
|
:href href
|
||||||
|
:img (~page/post-img-from-data :src img :alt title)
|
||||||
|
:title title)))
|
||||||
|
posts)))))
|
||||||
|
|
||||||
;; Post nav entries+calendars OOB from data
|
;; Post nav entries+calendars OOB from data
|
||||||
(defcomp ~page/post-nav-wrapper-from-data (&key nav-btn entries calendars hyperscript)
|
(defcomp ~page/post-nav-wrapper-from-data (&key nav-btn entries calendars hyperscript)
|
||||||
@@ -602,14 +711,23 @@
|
|||||||
(~shared:layout/nav-link :href admin-href :icon "fa fa-cog"))))
|
(~shared:layout/nav-link :href admin-href :icon "fa fa-cog"))))
|
||||||
|
|
||||||
;; Post search results from data
|
;; Post search results from data
|
||||||
(defcomp ~page/post-search-results-from-data (&key items page next-url has-more)
|
(defcomp
|
||||||
|
~page/post-search-results-from-data
|
||||||
|
(&key items page next-url has-more)
|
||||||
(<>
|
(<>
|
||||||
(map (lambda (item)
|
(map
|
||||||
(~forms/post-search-item
|
(lambda
|
||||||
:post-url (get item "post-url") :entry-id (get item "entry-id")
|
(item)
|
||||||
:csrf (get item "csrf") :post-id (get item "post-id")
|
(let-match
|
||||||
:img (~page/post-img-from-data :src (get item "img") :alt (get item "title"))
|
{:csrf csrf :entry-id entry-id :post-url post-url :title title :img img :post-id post-id}
|
||||||
:title (get item "title")))
|
item
|
||||||
|
(~forms/post-search-item
|
||||||
|
:post-url post-url
|
||||||
|
:entry-id entry-id
|
||||||
|
:csrf csrf
|
||||||
|
:post-id post-id
|
||||||
|
:img (~page/post-img-from-data :src img :alt title)
|
||||||
|
:title title)))
|
||||||
(or items (list)))
|
(or items (list)))
|
||||||
(cond
|
(cond
|
||||||
(has-more (~forms/post-search-sentinel :page page :next-url next-url))
|
(has-more (~forms/post-search-sentinel :page page :next-url next-url))
|
||||||
@@ -617,16 +735,26 @@
|
|||||||
(true ""))))
|
(true ""))))
|
||||||
|
|
||||||
;; Entry options from data — state-driven button composition
|
;; Entry options from data — state-driven button composition
|
||||||
(defcomp ~page/entry-options-from-data (&key entry-id state buttons)
|
(defcomp
|
||||||
|
~page/entry-options-from-data
|
||||||
|
(&key entry-id state buttons)
|
||||||
(~admin/entry-options
|
(~admin/entry-options
|
||||||
:entry-id entry-id
|
:entry-id entry-id
|
||||||
:buttons (<> (map (lambda (b)
|
:buttons (<>
|
||||||
(~admin/entry-option-button
|
(map
|
||||||
:url (get b "url") :target (str "#calendar_entry_options_" entry-id)
|
(lambda
|
||||||
:csrf (get b "csrf") :btn-type (get b "btn-type")
|
(b)
|
||||||
:action-btn (get b "action-btn")
|
(let-match
|
||||||
:confirm-title (get b "confirm-title")
|
{:csrf csrf :confirm-title confirm-title :url url :btn-type btn-type :action-btn action-btn :confirm-text confirm-text :label label :is-btn is-btn}
|
||||||
:confirm-text (get b "confirm-text")
|
b
|
||||||
:label (get b "label")
|
(~admin/entry-option-button
|
||||||
:is-btn (get b "is-btn")))
|
:url url
|
||||||
(or buttons (list))))))
|
:target (str "#calendar_entry_options_" entry-id)
|
||||||
|
:csrf csrf
|
||||||
|
:btn-type btn-type
|
||||||
|
:action-btn action-btn
|
||||||
|
:confirm-title confirm-title
|
||||||
|
:confirm-text confirm-text
|
||||||
|
:label label
|
||||||
|
:is-btn is-btn)))
|
||||||
|
(or buttons (list))))))
|
||||||
|
|||||||
@@ -211,18 +211,28 @@
|
|||||||
;; ---------------------------------------------------------------------------
|
;; ---------------------------------------------------------------------------
|
||||||
|
|
||||||
;; My tickets panel from data
|
;; My tickets panel from data
|
||||||
(defcomp ~tickets/panel-from-data (&key (list-container :as string) (tickets :as list?))
|
(defcomp
|
||||||
|
~tickets/panel-from-data
|
||||||
|
(&key (list-container :as string) (tickets :as list?))
|
||||||
(~tickets/panel
|
(~tickets/panel
|
||||||
:list-container list-container
|
:list-container list-container
|
||||||
:has-tickets (not (empty? (or tickets (list))))
|
:has-tickets (not (empty? (or tickets (list))))
|
||||||
:cards (<> (map (lambda (t)
|
:cards (<>
|
||||||
(~tickets/card
|
(map
|
||||||
:href (get t "href") :entry-name (get t "entry-name")
|
(lambda
|
||||||
:type-name (get t "type-name") :time-str (get t "time-str")
|
(t)
|
||||||
:cal-name (get t "cal-name")
|
(let-match
|
||||||
:badge (~entries/ticket-state-badge :state (get t "state"))
|
{:time-str time-str :href href :type-name type-name :code-prefix code-prefix :entry-name entry-name :cal-name cal-name :state state}
|
||||||
:code-prefix (get t "code-prefix")))
|
t
|
||||||
(or tickets (list))))))
|
(~tickets/card
|
||||||
|
:href href
|
||||||
|
:entry-name entry-name
|
||||||
|
:type-name type-name
|
||||||
|
:time-str time-str
|
||||||
|
:cal-name cal-name
|
||||||
|
:badge (~entries/ticket-state-badge :state state)
|
||||||
|
:code-prefix code-prefix)))
|
||||||
|
(or tickets (list))))))
|
||||||
|
|
||||||
;; Ticket detail from data — uses lg badge variant
|
;; Ticket detail from data — uses lg badge variant
|
||||||
(defcomp ~tickets/detail-from-data (&key (list-container :as string) (back-href :as string) (header-bg :as string) (entry-name :as string)
|
(defcomp ~tickets/detail-from-data (&key (list-container :as string) (back-href :as string) (header-bg :as string) (entry-name :as string)
|
||||||
@@ -256,54 +266,106 @@
|
|||||||
(true nil))))
|
(true nil))))
|
||||||
|
|
||||||
;; Ticket admin panel from data
|
;; Ticket admin panel from data
|
||||||
(defcomp ~tickets/admin-panel-from-data (&key (list-container :as string) (lookup-url :as string) (tickets :as list?)
|
(defcomp
|
||||||
(total :as number?) (confirmed :as number?) (checked-in :as number?) (reserved :as number?))
|
~tickets/admin-panel-from-data
|
||||||
|
(&key
|
||||||
|
(list-container :as string)
|
||||||
|
(lookup-url :as string)
|
||||||
|
(tickets :as list?)
|
||||||
|
(total :as number?)
|
||||||
|
(confirmed :as number?)
|
||||||
|
(checked-in :as number?)
|
||||||
|
(reserved :as number?))
|
||||||
(~tickets/admin-panel
|
(~tickets/admin-panel
|
||||||
:list-container list-container
|
:list-container list-container
|
||||||
:stats (<>
|
:stats (<>
|
||||||
(~tickets/admin-stat :border "border-stone-200" :bg ""
|
(~tickets/admin-stat
|
||||||
:text-cls "text-stone-900" :label-cls "text-stone-500"
|
:border "border-stone-200"
|
||||||
:value (str (or total 0)) :label "Total")
|
:bg ""
|
||||||
(~tickets/admin-stat :border "border-emerald-200" :bg "bg-emerald-50"
|
:text-cls "text-stone-900"
|
||||||
:text-cls "text-emerald-700" :label-cls "text-emerald-600"
|
:label-cls "text-stone-500"
|
||||||
:value (str (or confirmed 0)) :label "Confirmed")
|
:value (str (or total 0))
|
||||||
(~tickets/admin-stat :border "border-blue-200" :bg "bg-blue-50"
|
:label "Total")
|
||||||
:text-cls "text-blue-700" :label-cls "text-blue-600"
|
(~tickets/admin-stat
|
||||||
:value (str (or checked-in 0)) :label "Checked In")
|
:border "border-emerald-200"
|
||||||
(~tickets/admin-stat :border "border-amber-200" :bg "bg-amber-50"
|
:bg "bg-emerald-50"
|
||||||
:text-cls "text-amber-700" :label-cls "text-amber-600"
|
:text-cls "text-emerald-700"
|
||||||
:value (str (or reserved 0)) :label "Reserved"))
|
:label-cls "text-emerald-600"
|
||||||
|
:value (str (or confirmed 0))
|
||||||
|
:label "Confirmed")
|
||||||
|
(~tickets/admin-stat
|
||||||
|
:border "border-blue-200"
|
||||||
|
:bg "bg-blue-50"
|
||||||
|
:text-cls "text-blue-700"
|
||||||
|
:label-cls "text-blue-600"
|
||||||
|
:value (str (or checked-in 0))
|
||||||
|
:label "Checked In")
|
||||||
|
(~tickets/admin-stat
|
||||||
|
:border "border-amber-200"
|
||||||
|
:bg "bg-amber-50"
|
||||||
|
:text-cls "text-amber-700"
|
||||||
|
:label-cls "text-amber-600"
|
||||||
|
:value (str (or reserved 0))
|
||||||
|
:label "Reserved"))
|
||||||
:lookup-url lookup-url
|
:lookup-url lookup-url
|
||||||
:has-tickets (not (empty? (or tickets (list))))
|
:has-tickets (not (empty? (or tickets (list))))
|
||||||
:rows (<> (map (lambda (t)
|
:rows (<>
|
||||||
(~tickets/admin-row-from-data
|
(map
|
||||||
:code (get t "code") :code-short (get t "code-short")
|
(lambda
|
||||||
:entry-name (get t "entry-name") :date-str (get t "date-str")
|
(t)
|
||||||
:type-name (get t "type-name") :state (get t "state")
|
(let-match
|
||||||
:checkin-url (get t "checkin-url") :csrf (get t "csrf")
|
{:date-str date-str :csrf csrf :type-name type-name :code-short code-short :entry-name entry-name :code code :checkin-url checkin-url :checked-in-time checked-in-time :state state}
|
||||||
:checked-in-time (get t "checked-in-time")))
|
t
|
||||||
(or tickets (list))))))
|
(~tickets/admin-row-from-data
|
||||||
|
:code code
|
||||||
|
:code-short code-short
|
||||||
|
:entry-name entry-name
|
||||||
|
:date-str date-str
|
||||||
|
:type-name type-name
|
||||||
|
:state state
|
||||||
|
:checkin-url checkin-url
|
||||||
|
:csrf csrf
|
||||||
|
:checked-in-time checked-in-time)))
|
||||||
|
(or tickets (list))))))
|
||||||
|
|
||||||
;; Entry tickets admin from data
|
;; Entry tickets admin from data
|
||||||
(defcomp ~tickets/entry-tickets-admin-from-data (&key (entry-name :as string) (count-label :as string) (tickets :as list?) (csrf :as string))
|
(defcomp
|
||||||
|
~tickets/entry-tickets-admin-from-data
|
||||||
|
(&key
|
||||||
|
(entry-name :as string)
|
||||||
|
(count-label :as string)
|
||||||
|
(tickets :as list?)
|
||||||
|
(csrf :as string))
|
||||||
(~tickets/entry-tickets-admin-panel
|
(~tickets/entry-tickets-admin-panel
|
||||||
:entry-name entry-name :count-label count-label
|
:entry-name entry-name
|
||||||
:body (if (empty? (or tickets (list)))
|
:count-label count-label
|
||||||
|
:body (if
|
||||||
|
(empty? (or tickets (list)))
|
||||||
(~tickets/entry-tickets-admin-empty)
|
(~tickets/entry-tickets-admin-empty)
|
||||||
(~tickets/entry-tickets-admin-table
|
(~tickets/entry-tickets-admin-table
|
||||||
:rows (<> (map (lambda (t)
|
:rows (<>
|
||||||
(~tickets/entry-tickets-admin-row
|
(map
|
||||||
:code (get t "code") :code-short (get t "code-short")
|
(lambda
|
||||||
:type-name (get t "type-name")
|
(t)
|
||||||
:badge (~entries/ticket-state-badge :state (get t "state"))
|
(let-match
|
||||||
:action (cond
|
{:type-name type-name :code-short code-short :code code :checkin-url checkin-url :checked-in-time checked-in-time :state state}
|
||||||
((or (= (get t "state") "confirmed") (= (get t "state") "reserved"))
|
t
|
||||||
(~tickets/entry-tickets-admin-checkin
|
(~tickets/entry-tickets-admin-row
|
||||||
:checkin-url (get t "checkin-url") :code (get t "code") :csrf csrf))
|
:code code
|
||||||
((= (get t "state") "checked_in")
|
:code-short code-short
|
||||||
(~tickets/admin-checked-in :time-str (or (get t "checked-in-time") "")))
|
:type-name type-name
|
||||||
(true nil))))
|
:badge (~entries/ticket-state-badge :state state)
|
||||||
(or tickets (list))))))))
|
:action (cond
|
||||||
|
((or (= state "confirmed") (= state "paid"))
|
||||||
|
(~tickets/entry-tickets-admin-checkin
|
||||||
|
:checkin-url checkin-url
|
||||||
|
:code code
|
||||||
|
:csrf csrf))
|
||||||
|
((= state "checked-in")
|
||||||
|
(~tickets/admin-checked-in
|
||||||
|
:time-str (or checked-in-time "")))
|
||||||
|
(true nil)))))
|
||||||
|
(or tickets (list))))))))
|
||||||
|
|
||||||
;; Checkin success row from data
|
;; Checkin success row from data
|
||||||
(defcomp ~tickets/checkin-success-row-from-data (&key (code :as string) (code-short :as string) (entry-name :as string) (date-str :as string?) (type-name :as string) (time-str :as string))
|
(defcomp ~tickets/checkin-success-row-from-data (&key (code :as string) (code-short :as string) (entry-name :as string) (date-str :as string?) (type-name :as string) (time-str :as string))
|
||||||
@@ -316,21 +378,43 @@
|
|||||||
:time-str time-str))
|
:time-str time-str))
|
||||||
|
|
||||||
;; Ticket types table from data
|
;; Ticket types table from data
|
||||||
(defcomp ~tickets/types-table-from-data (&key (list-container :as string) (ticket-types :as list?) (action-btn :as string) (add-url :as string)
|
(defcomp
|
||||||
(tr-cls :as string) (pill-cls :as string) (hx-select :as string) (csrf-hdr :as string))
|
~tickets/types-table-from-data
|
||||||
|
(&key
|
||||||
|
(list-container :as string)
|
||||||
|
(ticket-types :as list?)
|
||||||
|
(action-btn :as string)
|
||||||
|
(add-url :as string)
|
||||||
|
(tr-cls :as string)
|
||||||
|
(pill-cls :as string)
|
||||||
|
(hx-select :as string)
|
||||||
|
(csrf-hdr :as string))
|
||||||
(~page/ticket-types-table
|
(~page/ticket-types-table
|
||||||
:list-container list-container
|
:list-container list-container
|
||||||
:rows (if (empty? (or ticket-types (list)))
|
:rows (if
|
||||||
|
(empty? (or ticket-types (list)))
|
||||||
(~page/ticket-types-empty-row)
|
(~page/ticket-types-empty-row)
|
||||||
(<> (map (lambda (tt)
|
(<>
|
||||||
(~page/ticket-types-row
|
(map
|
||||||
:tr-cls tr-cls :tt-href (get tt "tt-href")
|
(lambda
|
||||||
:pill-cls pill-cls :hx-select hx-select
|
(tt)
|
||||||
:tt-name (get tt "tt-name") :cost-str (get tt "cost-str")
|
(let-match
|
||||||
:count (get tt "count") :action-btn action-btn
|
{:tt-href tt-href :count count :cost-str cost-str :tt-name tt-name :del-url del-url}
|
||||||
:del-url (get tt "del-url") :csrf-hdr csrf-hdr))
|
tt
|
||||||
(or ticket-types (list)))))
|
(~page/ticket-types-row
|
||||||
:action-btn action-btn :add-url add-url))
|
:tr-cls tr-cls
|
||||||
|
:tt-href tt-href
|
||||||
|
:pill-cls pill-cls
|
||||||
|
:hx-select hx-select
|
||||||
|
:tt-name tt-name
|
||||||
|
:cost-str cost-str
|
||||||
|
:count count
|
||||||
|
:action-btn action-btn
|
||||||
|
:del-url del-url
|
||||||
|
:csrf-hdr csrf-hdr)))
|
||||||
|
(or ticket-types (list)))))
|
||||||
|
:action-btn action-btn
|
||||||
|
:add-url add-url))
|
||||||
|
|
||||||
;; Lookup result from data
|
;; Lookup result from data
|
||||||
(defcomp ~tickets/lookup-result-from-data (&key (entry-name :as string) (type-name :as string?) (date-str :as string?) (cal-name :as string?)
|
(defcomp ~tickets/lookup-result-from-data (&key (entry-name :as string) (type-name :as string?) (date-str :as string?) (cal-name :as string?)
|
||||||
|
|||||||
@@ -92,52 +92,95 @@
|
|||||||
|
|
||||||
;; --- Data-driven post card (replaces Python _post_card_sx loop) ---
|
;; --- Data-driven post card (replaces Python _post_card_sx loop) ---
|
||||||
|
|
||||||
(defcomp ~social/post-card-from-data (&key (d :as dict) (has-actor :as boolean) (csrf :as string)
|
(defcomp
|
||||||
(like-url :as string) (unlike-url :as string)
|
~social/post-card-from-data
|
||||||
(boost-url :as string) (unboost-url :as string))
|
(&key
|
||||||
(let* ((boosted-by (get d "boosted_by"))
|
(d :as dict)
|
||||||
(actor-icon (get d "actor_icon"))
|
(has-actor :as boolean)
|
||||||
(actor-name (get d "actor_name"))
|
(csrf :as string)
|
||||||
(initial (or (get d "initial") "?"))
|
(like-url :as string)
|
||||||
(avatar (~shared:misc/avatar
|
(unlike-url :as string)
|
||||||
:src actor-icon
|
(boost-url :as string)
|
||||||
:cls (if actor-icon "w-10 h-10 rounded-full"
|
(unboost-url :as string))
|
||||||
"w-10 h-10 rounded-full bg-stone-300 flex items-center justify-center text-stone-600 font-bold text-sm")
|
(let-match
|
||||||
:initial (when (not actor-icon) initial)))
|
{:actor_name actor-name :liked_by_me liked :boosted_by_me boosted-me :time time :actor_username actor-username :domain domain :content content :object_id oid :boosted_by boosted-by :summary summary :original_url original-url :safe_id safe-id :author_inbox ainbox :reply_url reply-url :like_count like-count :boost_count boost-count :actor_icon actor-icon :initial initial*}
|
||||||
(boost (when boosted-by (~social/boost-label :name boosted-by)))
|
d
|
||||||
(content-sx (if (get d "summary")
|
(let*
|
||||||
(~social/content :content (get d "content") :summary (get d "summary"))
|
((initial (or initial* "?"))
|
||||||
(~social/content :content (get d "content"))))
|
(avatar
|
||||||
(original (when (get d "original_url")
|
(~shared:misc/avatar
|
||||||
(~social/original-link :url (get d "original_url"))))
|
:src actor-icon
|
||||||
(safe-id (get d "safe_id"))
|
:cls (if
|
||||||
(interactions (when has-actor
|
actor-icon
|
||||||
(let* ((oid (get d "object_id"))
|
"w-10 h-10 rounded-full"
|
||||||
(ainbox (get d "author_inbox"))
|
"w-10 h-10 rounded-full bg-stone-300 flex items-center justify-center text-stone-600 font-bold text-sm")
|
||||||
(target (str "#interactions-" safe-id))
|
:initial (when (not actor-icon) initial)))
|
||||||
(liked (get d "liked_by_me"))
|
(boost (when boosted-by (~social/boost-label :name boosted-by)))
|
||||||
(boosted-me (get d "boosted_by_me"))
|
(content-sx
|
||||||
(l-action (if liked unlike-url like-url))
|
(if
|
||||||
(l-cls (str "flex items-center gap-1 " (if liked "text-red-500 hover:text-red-600" "hover:text-red-500")))
|
summary
|
||||||
(l-icon (if liked "\u2665" "\u2661"))
|
(~social/content :content content :summary summary)
|
||||||
(b-action (if boosted-me unboost-url boost-url))
|
(~social/content :content content)))
|
||||||
(b-cls (str "flex items-center gap-1 " (if boosted-me "text-green-600 hover:text-green-700" "hover:text-green-600")))
|
(original
|
||||||
(reply-url (get d "reply_url"))
|
(when original-url (~social/original-link :url original-url)))
|
||||||
(reply (when reply-url (~social/reply-link :url reply-url)))
|
(interactions
|
||||||
(like-form (~social/like-form
|
(when
|
||||||
:action l-action :target target :oid oid :ainbox ainbox
|
has-actor
|
||||||
:csrf csrf :cls l-cls :icon l-icon :count (get d "like_count")))
|
(let*
|
||||||
(boost-form (~social/boost-form
|
((target (str "#interactions-" safe-id))
|
||||||
:action b-action :target target :oid oid :ainbox ainbox
|
(l-action (if liked unlike-url like-url))
|
||||||
:csrf csrf :cls b-cls :count (get d "boost_count"))))
|
(l-cls
|
||||||
(div :id (str "interactions-" safe-id)
|
(str
|
||||||
(~social/interaction-buttons :like like-form :boost boost-form :reply reply))))))
|
"flex items-center gap-1 "
|
||||||
(~social/post-card
|
(if
|
||||||
:boost boost :avatar avatar
|
liked
|
||||||
:actor-name actor-name :actor-username (get d "actor_username")
|
"text-red-500 hover:text-red-600"
|
||||||
:domain (get d "domain") :time (get d "time")
|
"hover:text-red-500")))
|
||||||
:content content-sx :original original
|
(l-icon (if liked "♥" "♡"))
|
||||||
:interactions interactions)))
|
(b-action (if boosted-me unboost-url boost-url))
|
||||||
|
(b-cls
|
||||||
|
(str
|
||||||
|
"flex items-center gap-1 "
|
||||||
|
(if
|
||||||
|
boosted-me
|
||||||
|
"text-green-600 hover:text-green-700"
|
||||||
|
"hover:text-green-600")))
|
||||||
|
(reply (when reply-url (~social/reply-link :url reply-url)))
|
||||||
|
(like-form
|
||||||
|
(~social/like-form
|
||||||
|
:action l-action
|
||||||
|
:target target
|
||||||
|
:oid oid
|
||||||
|
:ainbox ainbox
|
||||||
|
:csrf csrf
|
||||||
|
:cls l-cls
|
||||||
|
:icon l-icon
|
||||||
|
:count like-count))
|
||||||
|
(boost-form
|
||||||
|
(~social/boost-form
|
||||||
|
:action b-action
|
||||||
|
:target target
|
||||||
|
:oid oid
|
||||||
|
:ainbox ainbox
|
||||||
|
:csrf csrf
|
||||||
|
:cls b-cls
|
||||||
|
:count boost-count)))
|
||||||
|
(div
|
||||||
|
:id (str "interactions-" safe-id)
|
||||||
|
(~social/interaction-buttons
|
||||||
|
:like like-form
|
||||||
|
:boost boost-form
|
||||||
|
:reply reply))))))
|
||||||
|
(~social/post-card
|
||||||
|
:boost boost
|
||||||
|
:avatar avatar
|
||||||
|
:actor-name actor-name
|
||||||
|
:actor-username actor-username
|
||||||
|
:domain domain
|
||||||
|
:time time
|
||||||
|
:content content-sx
|
||||||
|
:original original
|
||||||
|
:interactions interactions))))
|
||||||
|
|
||||||
;; Data-driven timeline items (replaces Python _timeline_items_sx loop)
|
;; Data-driven timeline items (replaces Python _timeline_items_sx loop)
|
||||||
(defcomp ~social/timeline-items-from-data (&key (items :as list) (next-url :as string?) (has-actor :as boolean) (csrf :as string)
|
(defcomp ~social/timeline-items-from-data (&key (items :as list) (next-url :as string?) (has-actor :as boolean) (csrf :as string)
|
||||||
@@ -174,35 +217,53 @@
|
|||||||
;; Assembled social nav — replaces Python _social_nav_sx
|
;; Assembled social nav — replaces Python _social_nav_sx
|
||||||
;; ---------------------------------------------------------------------------
|
;; ---------------------------------------------------------------------------
|
||||||
|
|
||||||
(defcomp ~social/nav (&key actor)
|
(defcomp
|
||||||
(if (not actor)
|
~social/nav
|
||||||
(~social/nav-choose-username :url (url-for "identity.choose_username_form"))
|
(&key actor)
|
||||||
(let* ((rp (request-path))
|
(if
|
||||||
(links (list
|
(not actor)
|
||||||
(dict :endpoint "social.defpage_home_timeline" :label "Timeline")
|
(~social/nav-choose-username
|
||||||
(dict :endpoint "social.defpage_public_timeline" :label "Public")
|
:url (url-for "identity.choose_username_form"))
|
||||||
(dict :endpoint "social.defpage_compose_form" :label "Compose")
|
(let*
|
||||||
(dict :endpoint "social.defpage_following_list" :label "Following")
|
((rp (request-path))
|
||||||
(dict :endpoint "social.defpage_followers_list" :label "Followers")
|
(links
|
||||||
(dict :endpoint "social.defpage_search" :label "Search"))))
|
(list
|
||||||
|
(dict :endpoint "social.defpage_home_timeline" :label "Timeline")
|
||||||
|
(dict :endpoint "social.defpage_public_timeline" :label "Public")
|
||||||
|
(dict :endpoint "social.defpage_compose_form" :label "Compose")
|
||||||
|
(dict :endpoint "social.defpage_following_list" :label "Following")
|
||||||
|
(dict :endpoint "social.defpage_followers_list" :label "Followers")
|
||||||
|
(dict :endpoint "social.defpage_search" :label "Search"))))
|
||||||
(~social/nav-bar
|
(~social/nav-bar
|
||||||
:items (<>
|
:items (<>
|
||||||
(map (lambda (lnk)
|
(map
|
||||||
(let* ((href (url-for (get lnk "endpoint")))
|
(lambda
|
||||||
(bold (if (= rp href) " font-bold" "")))
|
(lnk)
|
||||||
(a :href href
|
(let-match
|
||||||
:class (str "px-2 py-1 rounded hover:bg-stone-200" bold)
|
{:label label :endpoint endpoint}
|
||||||
(get lnk "label"))))
|
lnk
|
||||||
|
(let*
|
||||||
|
((href (url-for endpoint))
|
||||||
|
(bold (if (= rp href) " font-bold" "")))
|
||||||
|
(a
|
||||||
|
:href href
|
||||||
|
:class (str "px-2 py-1 rounded hover:bg-stone-200" bold)
|
||||||
|
label))))
|
||||||
links)
|
links)
|
||||||
(let* ((notif-url (url-for "social.defpage_notifications"))
|
(let*
|
||||||
(notif-bold (if (= rp notif-url) " font-bold" "")))
|
((notif-url (url-for "social.defpage_notifications"))
|
||||||
|
(notif-bold (if (= rp notif-url) " font-bold" "")))
|
||||||
(~social/nav-notification-link
|
(~social/nav-notification-link
|
||||||
:href notif-url
|
:href notif-url
|
||||||
:cls (str "px-2 py-1 rounded hover:bg-stone-200 relative" notif-bold)
|
:cls (str "px-2 py-1 rounded hover:bg-stone-200 relative" notif-bold)
|
||||||
:count-url (url-for "social.notification_count")))
|
:count-url (url-for "social.notification_count")))
|
||||||
(a :href (url-for "activitypub.actor_profile" :username (get actor "preferred_username"))
|
(let-match
|
||||||
:class "px-2 py-1 rounded hover:bg-stone-200"
|
{:preferred_username username}
|
||||||
(str "@" (get actor "preferred_username"))))))))
|
actor
|
||||||
|
(a
|
||||||
|
:href (url-for "activitypub.actor_profile" :username username)
|
||||||
|
:class "px-2 py-1 rounded hover:bg-stone-200"
|
||||||
|
(str "@" username))))))))
|
||||||
|
|
||||||
;; ---------------------------------------------------------------------------
|
;; ---------------------------------------------------------------------------
|
||||||
;; Assembled post card — replaces Python _post_card_sx
|
;; Assembled post card — replaces Python _post_card_sx
|
||||||
|
|||||||
169
lib/freeze.sx
169
lib/freeze.sx
@@ -21,7 +21,8 @@
|
|||||||
|
|
||||||
;; Registry of freeze scopes: name → list of {name signal} entries
|
;; Registry of freeze scopes: name → list of {name signal} entries
|
||||||
|
|
||||||
(define-library (sx freeze)
|
(define-library
|
||||||
|
(sx freeze)
|
||||||
(export
|
(export
|
||||||
freeze-registry
|
freeze-registry
|
||||||
freeze-signal
|
freeze-signal
|
||||||
@@ -33,82 +34,96 @@
|
|||||||
freeze-to-sx
|
freeze-to-sx
|
||||||
thaw-from-sx)
|
thaw-from-sx)
|
||||||
(begin
|
(begin
|
||||||
|
(define freeze-registry (dict))
|
||||||
(define freeze-registry (dict))
|
(define
|
||||||
|
freeze-signal
|
||||||
;; Register a signal in the current freeze scope
|
:effects (mutation)
|
||||||
(define freeze-signal :effects [mutation]
|
(fn
|
||||||
(fn (name sig)
|
(name sig)
|
||||||
(let ((scope-name (context "sx-freeze-scope" nil)))
|
(let
|
||||||
(when scope-name
|
((scope-name (context "sx-freeze-scope" nil)))
|
||||||
(let ((entries (or (get freeze-registry scope-name) (list))))
|
(when
|
||||||
(append! entries (dict "name" name "signal" sig))
|
scope-name
|
||||||
(dict-set! freeze-registry scope-name entries))))))
|
(let
|
||||||
|
((entries (or (get freeze-registry scope-name) (list))))
|
||||||
;; Freeze scope delimiter — collects signals registered within body
|
(append! entries (dict "name" name "signal" sig))
|
||||||
(define freeze-scope :effects [mutation]
|
(dict-set! freeze-registry scope-name entries))))))
|
||||||
(fn (name body-fn)
|
(define
|
||||||
(scope-push! "sx-freeze-scope" name)
|
freeze-scope
|
||||||
;; Initialize empty entry list for this scope
|
:effects (mutation)
|
||||||
(dict-set! freeze-registry name (list))
|
(fn
|
||||||
(cek-call body-fn nil)
|
(name body-fn)
|
||||||
(scope-pop! "sx-freeze-scope")
|
(scope-push! "sx-freeze-scope" name)
|
||||||
nil))
|
(dict-set! freeze-registry name (list))
|
||||||
|
(cek-call body-fn nil)
|
||||||
;; Freeze a named scope → SX dict of signal values
|
(scope-pop! "sx-freeze-scope")
|
||||||
(define cek-freeze-scope :effects []
|
nil))
|
||||||
(fn (name)
|
(define
|
||||||
(let ((entries (or (get freeze-registry name) (list)))
|
cek-freeze-scope
|
||||||
(signals-dict (dict)))
|
:effects ()
|
||||||
(for-each (fn (entry)
|
(fn
|
||||||
(dict-set! signals-dict
|
(name)
|
||||||
(get entry "name")
|
(let
|
||||||
(signal-value (get entry "signal"))))
|
((entries (or (get freeze-registry name) (list)))
|
||||||
entries)
|
(signals-dict (dict)))
|
||||||
(dict "name" name "signals" signals-dict))))
|
(for-each
|
||||||
|
(fn
|
||||||
;; Freeze all scopes
|
(entry)
|
||||||
(define cek-freeze-all :effects []
|
(dict-set!
|
||||||
(fn ()
|
signals-dict
|
||||||
(map (fn (name) (cek-freeze-scope name))
|
(get entry "name")
|
||||||
(keys freeze-registry))))
|
(signal-value (get entry "signal"))))
|
||||||
|
entries)
|
||||||
;; Thaw a named scope — restore signal values from frozen data
|
(dict "name" name "signals" signals-dict))))
|
||||||
(define cek-thaw-scope :effects [mutation]
|
(define
|
||||||
(fn (name frozen)
|
cek-freeze-all
|
||||||
(let ((entries (or (get freeze-registry name) (list)))
|
:effects ()
|
||||||
(values (get frozen "signals")))
|
(fn
|
||||||
(when values
|
()
|
||||||
(for-each (fn (entry)
|
(map (fn (name) (cek-freeze-scope name)) (keys freeze-registry))))
|
||||||
(let ((sig-name (get entry "name"))
|
(define
|
||||||
(sig (get entry "signal"))
|
cek-thaw-scope
|
||||||
(val (get values sig-name)))
|
:effects (mutation)
|
||||||
(when (not (nil? val))
|
(fn
|
||||||
(reset! sig val))))
|
(name frozen)
|
||||||
entries)))))
|
(let
|
||||||
|
((entries (or (get freeze-registry name) (list)))
|
||||||
;; Thaw all scopes from a list of frozen scope dicts
|
(values (get frozen "signals")))
|
||||||
(define cek-thaw-all :effects [mutation]
|
(when
|
||||||
(fn (frozen-list)
|
values
|
||||||
(for-each (fn (frozen)
|
(for-each
|
||||||
(cek-thaw-scope (get frozen "name") frozen))
|
(fn
|
||||||
frozen-list)))
|
(entry)
|
||||||
|
(let
|
||||||
;; Serialize a frozen scope to SX text
|
((sig-name (get entry "name"))
|
||||||
(define freeze-to-sx :effects []
|
(sig (get entry "signal"))
|
||||||
(fn (name)
|
(val (get values sig-name)))
|
||||||
(sx-serialize (cek-freeze-scope name))))
|
(when (not (nil? val)) (reset! sig val))))
|
||||||
|
entries)))))
|
||||||
;; Restore from SX text
|
(define
|
||||||
(define thaw-from-sx :effects [mutation]
|
cek-thaw-all
|
||||||
(fn (sx-text)
|
:effects (mutation)
|
||||||
(let ((parsed (sx-parse sx-text)))
|
(fn
|
||||||
(when (not (empty? parsed))
|
(frozen-list)
|
||||||
(let ((frozen (first parsed)))
|
(for-each
|
||||||
(cek-thaw-scope (get frozen "name") frozen))))))
|
(fn (frozen) (cek-thaw-scope (get frozen "name") frozen))
|
||||||
|
frozen-list)))
|
||||||
|
(define
|
||||||
)) ;; end define-library
|
freeze-to-sx
|
||||||
|
:effects ()
|
||||||
|
(fn (name) (sx-serialize (cek-freeze-scope name))))
|
||||||
|
(define
|
||||||
|
thaw-from-sx
|
||||||
|
:effects (mutation)
|
||||||
|
(fn
|
||||||
|
(sx-text)
|
||||||
|
(let
|
||||||
|
((parsed (sx-parse sx-text)))
|
||||||
|
(when
|
||||||
|
(not (empty? parsed))
|
||||||
|
(let
|
||||||
|
((frozen (first parsed)))
|
||||||
|
(cek-thaw-scope (get frozen "name") frozen)))))))) ;; end define-library
|
||||||
|
|
||||||
;; Re-export to global namespace for backward compatibility
|
;; Re-export to global namespace for backward compatibility
|
||||||
(import (sx freeze))
|
(import (sx freeze))
|
||||||
|
|||||||
509
lib/highlight.sx
509
lib/highlight.sx
@@ -1,6 +1,7 @@
|
|||||||
|
|
||||||
|
|
||||||
(define-library (sx highlight)
|
(define-library
|
||||||
|
(sx highlight)
|
||||||
(export
|
(export
|
||||||
sx-specials
|
sx-specials
|
||||||
sx-special?
|
sx-special?
|
||||||
@@ -16,204 +17,184 @@
|
|||||||
highlight-sx
|
highlight-sx
|
||||||
highlight)
|
highlight)
|
||||||
(begin
|
(begin
|
||||||
|
(define
|
||||||
(define
|
sx-specials
|
||||||
sx-specials
|
(list
|
||||||
(list
|
"defcomp"
|
||||||
"defcomp"
|
"defrelation"
|
||||||
"defrelation"
|
"defisland"
|
||||||
"defisland"
|
"defpage"
|
||||||
"defpage"
|
"defhelper"
|
||||||
"defhelper"
|
"define"
|
||||||
"define"
|
"defmacro"
|
||||||
"defmacro"
|
"defconfig"
|
||||||
"defconfig"
|
"deftest"
|
||||||
"deftest"
|
"if"
|
||||||
"if"
|
"when"
|
||||||
"when"
|
"cond"
|
||||||
"cond"
|
"case"
|
||||||
"case"
|
"and"
|
||||||
"and"
|
"or"
|
||||||
"or"
|
"not"
|
||||||
"not"
|
"let"
|
||||||
"let"
|
"let*"
|
||||||
"let*"
|
"lambda"
|
||||||
"lambda"
|
"fn"
|
||||||
"fn"
|
"do"
|
||||||
"do"
|
"begin"
|
||||||
"begin"
|
"quote"
|
||||||
"quote"
|
"quasiquote"
|
||||||
"quasiquote"
|
"->"
|
||||||
"->"
|
"map"
|
||||||
"map"
|
"filter"
|
||||||
"filter"
|
"reduce"
|
||||||
"reduce"
|
"some"
|
||||||
"some"
|
"every?"
|
||||||
"every?"
|
"map-indexed"
|
||||||
"map-indexed"
|
"for-each"
|
||||||
"for-each"
|
"&key"
|
||||||
"&key"
|
"&rest"
|
||||||
"&rest"
|
"set!"
|
||||||
"set!"))
|
"satisfies?"
|
||||||
|
"match"
|
||||||
(define sx-special? (fn (s) (some (fn (x) (= x s)) sx-specials)))
|
"let-match"
|
||||||
|
"define-protocol"
|
||||||
(define hl-digit? (fn (c) (and (>= c "0") (<= c "9"))))
|
"implement"
|
||||||
|
"->>"
|
||||||
(define
|
"|>"
|
||||||
hl-alpha?
|
"as->"
|
||||||
(fn (c) (or (and (>= c "a") (<= c "z")) (and (>= c "A") (<= c "Z")))))
|
"define-library"
|
||||||
|
"import"
|
||||||
(define
|
"perform"
|
||||||
hl-sym-char?
|
"guard"
|
||||||
(fn
|
"call/cc"
|
||||||
(c)
|
"raise"
|
||||||
(or
|
"define-syntax"
|
||||||
(hl-alpha? c)
|
"syntax-rules"
|
||||||
(hl-digit? c)
|
"make-parameter"
|
||||||
(= c "_")
|
"parameterize"))
|
||||||
(= c "-")
|
(define sx-special? (fn (s) (some (fn (x) (= x s)) sx-specials)))
|
||||||
(= c "?")
|
(define hl-digit? (fn (c) (and (>= c "0") (<= c "9"))))
|
||||||
(= c "!")
|
(define
|
||||||
(= c "+")
|
hl-alpha?
|
||||||
(= c "*")
|
(fn
|
||||||
(= c "/")
|
(c)
|
||||||
(= c "<")
|
(or (and (>= c "a") (<= c "z")) (and (>= c "A") (<= c "Z")))))
|
||||||
(= c ">")
|
(define
|
||||||
(= c "=")
|
hl-sym-char?
|
||||||
(= c "&")
|
(fn
|
||||||
(= c "."))))
|
(c)
|
||||||
|
(or
|
||||||
(define hl-ws? (fn (c) (or (= c " ") (= c "\n") (= c "\t") (= c "\r"))))
|
(hl-alpha? c)
|
||||||
|
(hl-digit? c)
|
||||||
(define hl-escape (fn (s) s))
|
(= c "_")
|
||||||
|
(= c "-")
|
||||||
(define
|
(= c "?")
|
||||||
hl-span
|
(= c "!")
|
||||||
(fn
|
(= c "+")
|
||||||
(class text)
|
(= c "*")
|
||||||
(if
|
(= c "/")
|
||||||
(= class "")
|
(= c "<")
|
||||||
(list (quote span) text)
|
(= c ">")
|
||||||
(list (quote span) (make-keyword "class") class text))))
|
(= c "=")
|
||||||
|
(= c "&")
|
||||||
(define
|
(= c "."))))
|
||||||
tokenize-sx
|
(define
|
||||||
(fn
|
hl-ws?
|
||||||
(code)
|
(fn (c) (or (= c " ") (= c "\n") (= c "\t") (= c "\r"))))
|
||||||
(let
|
(define hl-escape (fn (s) s))
|
||||||
((tokens (list)) (i 0) (len (string-length code)))
|
(define
|
||||||
(let
|
hl-span
|
||||||
loop
|
(fn
|
||||||
()
|
(class text)
|
||||||
(when
|
(if
|
||||||
(< i len)
|
(= class "")
|
||||||
|
(list (quote span) text)
|
||||||
|
(list (quote span) (make-keyword "class") class text))))
|
||||||
|
(define
|
||||||
|
tokenize-sx
|
||||||
|
(fn
|
||||||
|
(code)
|
||||||
|
(let
|
||||||
|
((tokens (list)) (i 0) (len (string-length code)))
|
||||||
(let
|
(let
|
||||||
((c (substring code i (+ i 1))))
|
loop
|
||||||
(if
|
()
|
||||||
(= c ";")
|
(when
|
||||||
|
(< i len)
|
||||||
(let
|
(let
|
||||||
((start i))
|
((c (substring code i (+ i 1))))
|
||||||
(set! i (+ i 1))
|
|
||||||
(let
|
|
||||||
scan
|
|
||||||
()
|
|
||||||
(when
|
|
||||||
(and
|
|
||||||
(< i len)
|
|
||||||
(not (= (substring code i (+ i 1)) "\n")))
|
|
||||||
(set! i (+ i 1))
|
|
||||||
(scan)))
|
|
||||||
(set!
|
|
||||||
tokens
|
|
||||||
(append
|
|
||||||
tokens
|
|
||||||
(list (list "comment" (substring code start i))))))
|
|
||||||
(if
|
|
||||||
(= c "\"")
|
|
||||||
(let
|
|
||||||
((start i))
|
|
||||||
(set! i (+ i 1))
|
|
||||||
(let
|
|
||||||
sloop
|
|
||||||
()
|
|
||||||
(when
|
|
||||||
(< i len)
|
|
||||||
(let
|
|
||||||
((sc (substring code i (+ i 1))))
|
|
||||||
(if
|
|
||||||
(= sc "\\")
|
|
||||||
(do (set! i (+ i 2)) (sloop))
|
|
||||||
(if
|
|
||||||
(= sc "\"")
|
|
||||||
(set! i (+ i 1))
|
|
||||||
(do (set! i (+ i 1)) (sloop)))))))
|
|
||||||
(set!
|
|
||||||
tokens
|
|
||||||
(append
|
|
||||||
tokens
|
|
||||||
(list (list "string" (substring code start i))))))
|
|
||||||
(if
|
(if
|
||||||
(= c ":")
|
(= c ";")
|
||||||
(let
|
(let
|
||||||
((start i))
|
((start i))
|
||||||
(set! i (+ i 1))
|
(set! i (+ i 1))
|
||||||
(when
|
(let
|
||||||
(and
|
scan
|
||||||
(< i len)
|
()
|
||||||
(hl-alpha? (substring code i (+ i 1))))
|
(when
|
||||||
(let
|
(and
|
||||||
scan
|
(< i len)
|
||||||
()
|
(not (= (substring code i (+ i 1)) "\n")))
|
||||||
(when
|
(set! i (+ i 1))
|
||||||
(and
|
(scan)))
|
||||||
(< i len)
|
|
||||||
(hl-sym-char? (substring code i (+ i 1))))
|
|
||||||
(set! i (+ i 1))
|
|
||||||
(scan))))
|
|
||||||
(set!
|
(set!
|
||||||
tokens
|
tokens
|
||||||
(append
|
(append
|
||||||
tokens
|
tokens
|
||||||
(list (list "keyword" (substring code start i))))))
|
(list (list "comment" (substring code start i))))))
|
||||||
(if
|
(if
|
||||||
(= c "~")
|
(= c "\"")
|
||||||
(let
|
(let
|
||||||
((start i))
|
((start i))
|
||||||
(set! i (+ i 1))
|
(set! i (+ i 1))
|
||||||
(let
|
(let
|
||||||
scan
|
sloop
|
||||||
()
|
()
|
||||||
(when
|
(when
|
||||||
(and
|
(< i len)
|
||||||
(< i len)
|
(let
|
||||||
(let
|
((sc (substring code i (+ i 1))))
|
||||||
((x (substring code i (+ i 1))))
|
(if
|
||||||
(or (hl-sym-char? x) (= x "/"))))
|
(= sc "\\")
|
||||||
(set! i (+ i 1))
|
(do (set! i (+ i 2)) (sloop))
|
||||||
(scan)))
|
(if
|
||||||
|
(= sc "\"")
|
||||||
|
(set! i (+ i 1))
|
||||||
|
(do (set! i (+ i 1)) (sloop)))))))
|
||||||
(set!
|
(set!
|
||||||
tokens
|
tokens
|
||||||
(append
|
(append
|
||||||
tokens
|
tokens
|
||||||
(list (list "component" (substring code start i))))))
|
(list (list "string" (substring code start i))))))
|
||||||
(if
|
(if
|
||||||
(or
|
(= c ":")
|
||||||
(= c "(")
|
(let
|
||||||
(= c ")")
|
((start i))
|
||||||
(= c "[")
|
(set! i (+ i 1))
|
||||||
(= c "]")
|
(when
|
||||||
(= c "{")
|
(and
|
||||||
(= c "}"))
|
(< i len)
|
||||||
(do
|
(hl-alpha? (substring code i (+ i 1))))
|
||||||
|
(let
|
||||||
|
scan
|
||||||
|
()
|
||||||
|
(when
|
||||||
|
(and
|
||||||
|
(< i len)
|
||||||
|
(hl-sym-char? (substring code i (+ i 1))))
|
||||||
|
(set! i (+ i 1))
|
||||||
|
(scan))))
|
||||||
(set!
|
(set!
|
||||||
tokens
|
tokens
|
||||||
(append tokens (list (list "paren" c))))
|
(append
|
||||||
(set! i (+ i 1)))
|
tokens
|
||||||
|
(list (list "keyword" (substring code start i))))))
|
||||||
(if
|
(if
|
||||||
(hl-digit? c)
|
(= c "~")
|
||||||
(let
|
(let
|
||||||
((start i))
|
((start i))
|
||||||
|
(set! i (+ i 1))
|
||||||
(let
|
(let
|
||||||
scan
|
scan
|
||||||
()
|
()
|
||||||
@@ -222,53 +203,30 @@
|
|||||||
(< i len)
|
(< i len)
|
||||||
(let
|
(let
|
||||||
((x (substring code i (+ i 1))))
|
((x (substring code i (+ i 1))))
|
||||||
(or (hl-digit? x) (= x "."))))
|
(or (hl-sym-char? x) (= x "/"))))
|
||||||
(set! i (+ i 1))
|
(set! i (+ i 1))
|
||||||
(scan)))
|
(scan)))
|
||||||
(set!
|
(set!
|
||||||
tokens
|
tokens
|
||||||
(append
|
(append
|
||||||
tokens
|
tokens
|
||||||
(list (list "number" (substring code start i))))))
|
(list
|
||||||
|
(list "component" (substring code start i))))))
|
||||||
(if
|
(if
|
||||||
(hl-sym-char? c)
|
(or
|
||||||
(let
|
(= c "(")
|
||||||
((start i))
|
(= c ")")
|
||||||
(let
|
(= c "[")
|
||||||
scan
|
(= c "]")
|
||||||
()
|
(= c "{")
|
||||||
(when
|
(= c "}"))
|
||||||
(and
|
(do
|
||||||
(< i len)
|
(set!
|
||||||
(hl-sym-char? (substring code i (+ i 1))))
|
tokens
|
||||||
(set! i (+ i 1))
|
(append tokens (list (list "paren" c))))
|
||||||
(scan)))
|
(set! i (+ i 1)))
|
||||||
(let
|
|
||||||
((text (substring code start i)))
|
|
||||||
(if
|
|
||||||
(or
|
|
||||||
(= text "true")
|
|
||||||
(= text "false")
|
|
||||||
(= text "nil"))
|
|
||||||
(set!
|
|
||||||
tokens
|
|
||||||
(append
|
|
||||||
tokens
|
|
||||||
(list (list "boolean" text))))
|
|
||||||
(if
|
|
||||||
(sx-special? text)
|
|
||||||
(set!
|
|
||||||
tokens
|
|
||||||
(append
|
|
||||||
tokens
|
|
||||||
(list (list "special" text))))
|
|
||||||
(set!
|
|
||||||
tokens
|
|
||||||
(append
|
|
||||||
tokens
|
|
||||||
(list (list "symbol" text))))))))
|
|
||||||
(if
|
(if
|
||||||
(hl-ws? c)
|
(hl-digit? c)
|
||||||
(let
|
(let
|
||||||
((start i))
|
((start i))
|
||||||
(let
|
(let
|
||||||
@@ -277,49 +235,106 @@
|
|||||||
(when
|
(when
|
||||||
(and
|
(and
|
||||||
(< i len)
|
(< i len)
|
||||||
(hl-ws? (substring code i (+ i 1))))
|
(let
|
||||||
|
((x (substring code i (+ i 1))))
|
||||||
|
(or (hl-digit? x) (= x "."))))
|
||||||
(set! i (+ i 1))
|
(set! i (+ i 1))
|
||||||
(scan)))
|
(scan)))
|
||||||
(set!
|
(set!
|
||||||
tokens
|
tokens
|
||||||
(append
|
(append
|
||||||
tokens
|
tokens
|
||||||
(list (list "ws" (substring code start i))))))
|
(list
|
||||||
(do
|
(list "number" (substring code start i))))))
|
||||||
(set!
|
(if
|
||||||
tokens
|
(hl-sym-char? c)
|
||||||
(append tokens (list (list "other" c))))
|
(let
|
||||||
(set! i (+ i 1))))))))))))
|
((start i))
|
||||||
(loop)))
|
(let
|
||||||
tokens)))
|
scan
|
||||||
|
()
|
||||||
(define sx-token-classes {:boolean "text-orange-600" :component "text-rose-600 font-semibold" :number "text-amber-700" :string "text-emerald-700" :special "text-sky-700 font-semibold" :paren "text-stone-400" :keyword "text-violet-600" :comment "text-stone-400 italic"})
|
(when
|
||||||
|
(and
|
||||||
(define
|
(< i len)
|
||||||
render-sx-tokens
|
(hl-sym-char?
|
||||||
(fn
|
(substring code i (+ i 1))))
|
||||||
(tokens)
|
(set! i (+ i 1))
|
||||||
(map
|
(scan)))
|
||||||
|
(let
|
||||||
|
((text (substring code start i)))
|
||||||
|
(if
|
||||||
|
(or
|
||||||
|
(= text "true")
|
||||||
|
(= text "false")
|
||||||
|
(= text "nil"))
|
||||||
|
(set!
|
||||||
|
tokens
|
||||||
|
(append
|
||||||
|
tokens
|
||||||
|
(list (list "boolean" text))))
|
||||||
|
(if
|
||||||
|
(sx-special? text)
|
||||||
|
(set!
|
||||||
|
tokens
|
||||||
|
(append
|
||||||
|
tokens
|
||||||
|
(list (list "special" text))))
|
||||||
|
(set!
|
||||||
|
tokens
|
||||||
|
(append
|
||||||
|
tokens
|
||||||
|
(list (list "symbol" text))))))))
|
||||||
|
(if
|
||||||
|
(hl-ws? c)
|
||||||
|
(let
|
||||||
|
((start i))
|
||||||
|
(let
|
||||||
|
scan
|
||||||
|
()
|
||||||
|
(when
|
||||||
|
(and
|
||||||
|
(< i len)
|
||||||
|
(hl-ws? (substring code i (+ i 1))))
|
||||||
|
(set! i (+ i 1))
|
||||||
|
(scan)))
|
||||||
|
(set!
|
||||||
|
tokens
|
||||||
|
(append
|
||||||
|
tokens
|
||||||
|
(list
|
||||||
|
(list "ws" (substring code start i))))))
|
||||||
|
(do
|
||||||
|
(set!
|
||||||
|
tokens
|
||||||
|
(append tokens (list (list "other" c))))
|
||||||
|
(set! i (+ i 1))))))))))))
|
||||||
|
(loop)))
|
||||||
|
tokens)))
|
||||||
|
(define sx-token-classes {:boolean "text-orange-600" :component "text-rose-600 font-semibold" :number "text-amber-700" :string "text-emerald-700" :special "text-sky-700 font-semibold" :paren "text-stone-400" :keyword "text-violet-600" :comment "text-stone-400 italic"})
|
||||||
|
(define
|
||||||
|
render-sx-tokens
|
||||||
(fn
|
(fn
|
||||||
(tok)
|
(tokens)
|
||||||
(let
|
(map
|
||||||
((cls (or (dict-get sx-token-classes (first tok)) "")))
|
(fn
|
||||||
(hl-span cls (nth tok 1))))
|
(tok)
|
||||||
tokens)))
|
(let
|
||||||
|
((cls (or (dict-get sx-token-classes (first tok)) "")))
|
||||||
(define highlight-sx (fn (code) (render-sx-tokens (tokenize-sx code))))
|
(hl-span cls (nth tok 1))))
|
||||||
|
tokens)))
|
||||||
(define
|
(define highlight-sx (fn (code) (-> code tokenize-sx render-sx-tokens)))
|
||||||
highlight
|
(define
|
||||||
(fn
|
highlight
|
||||||
(code lang)
|
(fn
|
||||||
(if
|
(code lang)
|
||||||
(or (= lang "lisp") (= lang "sx") (= lang "sexp") (= lang "scheme"))
|
(if
|
||||||
(highlight-sx code)
|
(or
|
||||||
(list (quote code) code))))
|
(= lang "lisp")
|
||||||
|
(= lang "sx")
|
||||||
|
(= lang "sexp")
|
||||||
)) ;; end define-library
|
(= lang "scheme"))
|
||||||
|
(highlight-sx code)
|
||||||
|
(list (quote code) code)))))) ;; end define-library
|
||||||
|
|
||||||
;; Re-export to global namespace for backward compatibility
|
;; Re-export to global namespace for backward compatibility
|
||||||
(import (sx highlight))
|
(import (sx highlight))
|
||||||
|
|||||||
554
lib/sx-swap.sx
554
lib/sx-swap.sx
@@ -1,6 +1,7 @@
|
|||||||
|
|
||||||
|
|
||||||
(define-library (sx swap)
|
(define-library
|
||||||
|
(sx swap)
|
||||||
(export
|
(export
|
||||||
_skip-string
|
_skip-string
|
||||||
_find-close
|
_find-close
|
||||||
@@ -16,310 +17,311 @@
|
|||||||
strip-oob
|
strip-oob
|
||||||
apply-response)
|
apply-response)
|
||||||
(begin
|
(begin
|
||||||
|
(define
|
||||||
(define
|
_skip-string
|
||||||
_skip-string
|
(fn
|
||||||
(fn
|
(src i)
|
||||||
(src i)
|
|
||||||
(if
|
|
||||||
(>= i (len src))
|
|
||||||
i
|
|
||||||
(let
|
|
||||||
((ch (nth src i)))
|
|
||||||
(cond
|
|
||||||
(= ch "\\")
|
|
||||||
(_skip-string src (+ i 2))
|
|
||||||
(= ch "\"")
|
|
||||||
(+ i 1)
|
|
||||||
:else (_skip-string src (+ i 1)))))))
|
|
||||||
|
|
||||||
(define
|
|
||||||
_find-close
|
|
||||||
(fn
|
|
||||||
(src i depth in-str)
|
|
||||||
(if
|
|
||||||
(>= i (len src))
|
|
||||||
-1
|
|
||||||
(let
|
|
||||||
((ch (nth src i)))
|
|
||||||
(cond
|
|
||||||
in-str
|
|
||||||
(cond
|
|
||||||
(= ch "\\")
|
|
||||||
(_find-close src (+ i 2) depth true)
|
|
||||||
(= ch "\"")
|
|
||||||
(_find-close src (+ i 1) depth false)
|
|
||||||
:else (_find-close src (+ i 1) depth true))
|
|
||||||
(= ch "\"")
|
|
||||||
(_find-close src (+ i 1) depth true)
|
|
||||||
(= ch "(")
|
|
||||||
(_find-close src (+ i 1) (+ depth 1) false)
|
|
||||||
(= ch ")")
|
|
||||||
(if (= depth 1) i (_find-close src (+ i 1) (- depth 1) false))
|
|
||||||
:else (_find-close src (+ i 1) depth false))))))
|
|
||||||
|
|
||||||
(define
|
|
||||||
_skip-ws
|
|
||||||
(fn
|
|
||||||
(src i)
|
|
||||||
(if
|
|
||||||
(>= i (len src))
|
|
||||||
i
|
|
||||||
(let
|
|
||||||
((ch (nth src i)))
|
|
||||||
(if
|
(if
|
||||||
(or (= ch " ") (= ch "\n") (= ch "\t") (= ch "\r"))
|
(>= i (len src))
|
||||||
(_skip-ws src (+ i 1))
|
|
||||||
i)))))
|
|
||||||
|
|
||||||
(define
|
|
||||||
_skip-token
|
|
||||||
(fn
|
|
||||||
(src i)
|
|
||||||
(if
|
|
||||||
(>= i (len src))
|
|
||||||
i
|
|
||||||
(let
|
|
||||||
((ch (nth src i)))
|
|
||||||
(if
|
|
||||||
(or
|
|
||||||
(= ch " ")
|
|
||||||
(= ch "\n")
|
|
||||||
(= ch "\t")
|
|
||||||
(= ch "\r")
|
|
||||||
(= ch "(")
|
|
||||||
(= ch ")")
|
|
||||||
(= ch "\""))
|
|
||||||
i
|
i
|
||||||
(_skip-token src (+ i 1)))))))
|
|
||||||
|
|
||||||
(define
|
|
||||||
_skip-value
|
|
||||||
(fn
|
|
||||||
(src i)
|
|
||||||
(if
|
|
||||||
(>= i (len src))
|
|
||||||
i
|
|
||||||
(let
|
|
||||||
((ch (nth src i)))
|
|
||||||
(cond
|
|
||||||
(= ch "\"")
|
|
||||||
(_skip-string src (+ i 1))
|
|
||||||
(= ch "(")
|
|
||||||
(let
|
(let
|
||||||
((close (_find-close src (+ i 1) 1 false)))
|
((ch (nth src i)))
|
||||||
(if (= close -1) (len src) (+ close 1)))
|
(cond
|
||||||
:else (_skip-token src i))))))
|
(= ch "\\")
|
||||||
|
(_skip-string src (+ i 2))
|
||||||
(define
|
(= ch "\"")
|
||||||
_find-children-start
|
(+ i 1)
|
||||||
(fn
|
:else (_skip-string src (+ i 1)))))))
|
||||||
(src elem-start elem-end)
|
(define
|
||||||
(let
|
_find-close
|
||||||
((after-open (+ elem-start 1)))
|
(fn
|
||||||
(let
|
(src i depth in-str)
|
||||||
((after-tag (_skip-token src (_skip-ws src after-open))))
|
|
||||||
(define
|
|
||||||
_skip-attrs
|
|
||||||
(fn
|
|
||||||
(j)
|
|
||||||
(let
|
|
||||||
((pos (_skip-ws src j)))
|
|
||||||
(if
|
|
||||||
(>= pos elem-end)
|
|
||||||
pos
|
|
||||||
(if
|
|
||||||
(= (nth src pos) ":")
|
|
||||||
(let
|
|
||||||
((after-kw (_skip-token src pos)))
|
|
||||||
(_skip-attrs (_skip-value src (_skip-ws src after-kw))))
|
|
||||||
pos)))))
|
|
||||||
(_skip-attrs after-tag)))))
|
|
||||||
|
|
||||||
(define
|
|
||||||
_scan-back
|
|
||||||
(fn
|
|
||||||
(src i)
|
|
||||||
(if (< i 0) -1 (if (= (nth src i) "(") i (_scan-back src (- i 1))))))
|
|
||||||
|
|
||||||
(define
|
|
||||||
find-element-by-id
|
|
||||||
(fn
|
|
||||||
(src target-id)
|
|
||||||
(let
|
|
||||||
((pattern (str ":id \"" target-id "\"")))
|
|
||||||
(let
|
|
||||||
((pos (index-of src pattern)))
|
|
||||||
(if
|
(if
|
||||||
(= pos -1)
|
(>= i (len src))
|
||||||
nil
|
-1
|
||||||
(let
|
(let
|
||||||
((elem-start (_scan-back src (- pos 1))))
|
((ch (nth src i)))
|
||||||
|
(cond
|
||||||
|
in-str
|
||||||
|
(cond
|
||||||
|
(= ch "\\")
|
||||||
|
(_find-close src (+ i 2) depth true)
|
||||||
|
(= ch "\"")
|
||||||
|
(_find-close src (+ i 1) depth false)
|
||||||
|
:else (_find-close src (+ i 1) depth true))
|
||||||
|
(= ch "\"")
|
||||||
|
(_find-close src (+ i 1) depth true)
|
||||||
|
(= ch "(")
|
||||||
|
(_find-close src (+ i 1) (+ depth 1) false)
|
||||||
|
(= ch ")")
|
||||||
|
(if
|
||||||
|
(= depth 1)
|
||||||
|
i
|
||||||
|
(_find-close src (+ i 1) (- depth 1) false))
|
||||||
|
:else (_find-close src (+ i 1) depth false))))))
|
||||||
|
(define
|
||||||
|
_skip-ws
|
||||||
|
(fn
|
||||||
|
(src i)
|
||||||
|
(if
|
||||||
|
(>= i (len src))
|
||||||
|
i
|
||||||
|
(let
|
||||||
|
((ch (nth src i)))
|
||||||
(if
|
(if
|
||||||
(= elem-start -1)
|
(or (= ch " ") (= ch "\n") (= ch "\t") (= ch "\r"))
|
||||||
|
(_skip-ws src (+ i 1))
|
||||||
|
i)))))
|
||||||
|
(define
|
||||||
|
_skip-token
|
||||||
|
(fn
|
||||||
|
(src i)
|
||||||
|
(if
|
||||||
|
(>= i (len src))
|
||||||
|
i
|
||||||
|
(let
|
||||||
|
((ch (nth src i)))
|
||||||
|
(if
|
||||||
|
(or
|
||||||
|
(= ch " ")
|
||||||
|
(= ch "\n")
|
||||||
|
(= ch "\t")
|
||||||
|
(= ch "\r")
|
||||||
|
(= ch "(")
|
||||||
|
(= ch ")")
|
||||||
|
(= ch "\""))
|
||||||
|
i
|
||||||
|
(_skip-token src (+ i 1)))))))
|
||||||
|
(define
|
||||||
|
_skip-value
|
||||||
|
(fn
|
||||||
|
(src i)
|
||||||
|
(if
|
||||||
|
(>= i (len src))
|
||||||
|
i
|
||||||
|
(let
|
||||||
|
((ch (nth src i)))
|
||||||
|
(cond
|
||||||
|
(= ch "\"")
|
||||||
|
(_skip-string src (+ i 1))
|
||||||
|
(= ch "(")
|
||||||
|
(let
|
||||||
|
((close (_find-close src (+ i 1) 1 false)))
|
||||||
|
(if (= close -1) (len src) (+ close 1)))
|
||||||
|
:else (_skip-token src i))))))
|
||||||
|
(define
|
||||||
|
_find-children-start
|
||||||
|
(fn
|
||||||
|
(src elem-start elem-end)
|
||||||
|
(let
|
||||||
|
((after-open (+ elem-start 1)))
|
||||||
|
(let
|
||||||
|
((after-tag (_skip-token src (_skip-ws src after-open))))
|
||||||
|
(define
|
||||||
|
_skip-attrs
|
||||||
|
(fn
|
||||||
|
(j)
|
||||||
|
(let
|
||||||
|
((pos (_skip-ws src j)))
|
||||||
|
(if
|
||||||
|
(>= pos elem-end)
|
||||||
|
pos
|
||||||
|
(if
|
||||||
|
(= (nth src pos) ":")
|
||||||
|
(let
|
||||||
|
((after-kw (_skip-token src pos)))
|
||||||
|
(_skip-attrs
|
||||||
|
(_skip-value src (_skip-ws src after-kw))))
|
||||||
|
pos)))))
|
||||||
|
(_skip-attrs after-tag)))))
|
||||||
|
(define
|
||||||
|
_scan-back
|
||||||
|
(fn
|
||||||
|
(src i)
|
||||||
|
(if
|
||||||
|
(< i 0)
|
||||||
|
-1
|
||||||
|
(if (= (nth src i) "(") i (_scan-back src (- i 1))))))
|
||||||
|
(define
|
||||||
|
find-element-by-id
|
||||||
|
(fn
|
||||||
|
(src target-id)
|
||||||
|
(let
|
||||||
|
((pattern (str ":id \"" target-id "\"")))
|
||||||
|
(let
|
||||||
|
((pos (index-of src pattern)))
|
||||||
|
(if
|
||||||
|
(= pos -1)
|
||||||
nil
|
nil
|
||||||
(let
|
(let
|
||||||
((elem-end (_find-close src (+ elem-start 1) 1 false)))
|
((elem-start (_scan-back src (- pos 1))))
|
||||||
(if
|
(if
|
||||||
(= elem-end -1)
|
(= elem-start -1)
|
||||||
nil
|
nil
|
||||||
(let
|
(let
|
||||||
((cs (_find-children-start src elem-start elem-end)))
|
((elem-end (_find-close src (+ elem-start 1) 1 false)))
|
||||||
{:end elem-end :start elem-start :children-start cs}))))))))))
|
(if
|
||||||
|
(= elem-end -1)
|
||||||
(define
|
nil
|
||||||
sx-swap
|
(let
|
||||||
(fn
|
((cs (_find-children-start src elem-start elem-end)))
|
||||||
(src mode target-id new-content)
|
{:end elem-end :start elem-start :children-start cs}))))))))))
|
||||||
(let
|
|
||||||
((info (find-element-by-id src target-id)))
|
|
||||||
(if
|
|
||||||
(nil? info)
|
|
||||||
src
|
|
||||||
(let
|
|
||||||
((s (get info "start"))
|
|
||||||
(e (get info "end"))
|
|
||||||
(cs (get info "children-start")))
|
|
||||||
(case
|
|
||||||
mode
|
|
||||||
"innerHTML"
|
|
||||||
(str (slice src 0 cs) new-content (slice src e (len src)))
|
|
||||||
"outerHTML"
|
|
||||||
(str (slice src 0 s) new-content (slice src (+ e 1) (len src)))
|
|
||||||
"beforeend"
|
|
||||||
(str (slice src 0 e) " " new-content (slice src e (len src)))
|
|
||||||
"afterbegin"
|
|
||||||
(str (slice src 0 cs) new-content " " (slice src cs (len src)))
|
|
||||||
"beforebegin"
|
|
||||||
(str (slice src 0 s) new-content (slice src s (len src)))
|
|
||||||
"afterend"
|
|
||||||
(str
|
|
||||||
(slice src 0 (+ e 1))
|
|
||||||
new-content
|
|
||||||
(slice src (+ e 1) (len src)))
|
|
||||||
"delete"
|
|
||||||
(str (slice src 0 s) (slice src (+ e 1) (len src)))
|
|
||||||
"none"
|
|
||||||
src
|
|
||||||
:else src))))))
|
|
||||||
|
|
||||||
(define
|
|
||||||
_extract-attr-value
|
|
||||||
(fn
|
|
||||||
(src keyword-end)
|
|
||||||
(let
|
|
||||||
((val-start (_skip-ws src keyword-end)))
|
|
||||||
(if
|
|
||||||
(= (nth src val-start) "\"")
|
|
||||||
(let
|
|
||||||
((str-end (_skip-string src (+ val-start 1))))
|
|
||||||
(slice src (+ val-start 1) (- str-end 1)))
|
|
||||||
(let
|
|
||||||
((tok-end (_skip-token src val-start)))
|
|
||||||
(slice src val-start tok-end))))))
|
|
||||||
|
|
||||||
(define
|
|
||||||
find-oob-elements
|
|
||||||
(fn
|
|
||||||
(src)
|
|
||||||
(define
|
(define
|
||||||
_scan
|
sx-swap
|
||||||
(fn
|
(fn
|
||||||
(from results)
|
(src mode target-id new-content)
|
||||||
(let
|
(let
|
||||||
((rel-pos (index-of (slice src from (len src)) ":sx-swap-oob")))
|
((info (find-element-by-id src target-id)))
|
||||||
(if
|
(if
|
||||||
(= rel-pos -1)
|
(nil? info)
|
||||||
results
|
src
|
||||||
|
(let-match
|
||||||
|
{:end e :start s :children-start cs}
|
||||||
|
info
|
||||||
|
(case
|
||||||
|
mode
|
||||||
|
"innerHTML"
|
||||||
|
(str (slice src 0 cs) new-content (slice src e (len src)))
|
||||||
|
"outerHTML"
|
||||||
|
(str
|
||||||
|
(slice src 0 s)
|
||||||
|
new-content
|
||||||
|
(slice src (+ e 1) (len src)))
|
||||||
|
"beforeend"
|
||||||
|
(str
|
||||||
|
(slice src 0 e)
|
||||||
|
" "
|
||||||
|
new-content
|
||||||
|
(slice src e (len src)))
|
||||||
|
"afterbegin"
|
||||||
|
(str
|
||||||
|
(slice src 0 cs)
|
||||||
|
new-content
|
||||||
|
" "
|
||||||
|
(slice src cs (len src)))
|
||||||
|
"beforebegin"
|
||||||
|
(str (slice src 0 s) new-content (slice src s (len src)))
|
||||||
|
"afterend"
|
||||||
|
(str
|
||||||
|
(slice src 0 (+ e 1))
|
||||||
|
new-content
|
||||||
|
(slice src (+ e 1) (len src)))
|
||||||
|
"delete"
|
||||||
|
(str (slice src 0 s) (slice src (+ e 1) (len src)))
|
||||||
|
"none"
|
||||||
|
src
|
||||||
|
:else src))))))
|
||||||
|
(define
|
||||||
|
_extract-attr-value
|
||||||
|
(fn
|
||||||
|
(src keyword-end)
|
||||||
|
(let
|
||||||
|
((val-start (_skip-ws src keyword-end)))
|
||||||
|
(if
|
||||||
|
(= (nth src val-start) "\"")
|
||||||
(let
|
(let
|
||||||
((abs-pos (+ from rel-pos)))
|
((str-end (_skip-string src (+ val-start 1))))
|
||||||
(let
|
(slice src (+ val-start 1) (- str-end 1)))
|
||||||
((mode (_extract-attr-value src (+ abs-pos 12))))
|
(let
|
||||||
|
((tok-end (_skip-token src val-start)))
|
||||||
|
(slice src val-start tok-end))))))
|
||||||
|
(define
|
||||||
|
find-oob-elements
|
||||||
|
(fn
|
||||||
|
(src)
|
||||||
|
(define
|
||||||
|
_scan
|
||||||
|
(fn
|
||||||
|
(from results)
|
||||||
|
(let
|
||||||
|
((rel-pos (index-of (slice src from (len src)) ":sx-swap-oob")))
|
||||||
|
(if
|
||||||
|
(= rel-pos -1)
|
||||||
|
results
|
||||||
(let
|
(let
|
||||||
((elem-start (_scan-back src (- abs-pos 1))))
|
((abs-pos (+ from rel-pos)))
|
||||||
(if
|
(let
|
||||||
(= elem-start -1)
|
((mode (_extract-attr-value src (+ abs-pos 12))))
|
||||||
results
|
|
||||||
(let
|
(let
|
||||||
((elem-end (_find-close src (+ elem-start 1) 1 false)))
|
((elem-start (_scan-back src (- abs-pos 1))))
|
||||||
(if
|
(if
|
||||||
(= elem-end -1)
|
(= elem-start -1)
|
||||||
results
|
results
|
||||||
(let
|
(let
|
||||||
((id-pattern ":id \""))
|
((elem-end (_find-close src (+ elem-start 1) 1 false)))
|
||||||
(let
|
(if
|
||||||
((id-pos (index-of (slice src elem-start (+ elem-end 1)) id-pattern)))
|
(= elem-end -1)
|
||||||
(if
|
results
|
||||||
(= id-pos -1)
|
(let
|
||||||
(_scan (+ elem-end 1) results)
|
((id-pattern ":id \""))
|
||||||
(let
|
(let
|
||||||
((id-abs (+ elem-start id-pos)))
|
((id-pos (index-of (slice src elem-start (+ elem-end 1)) id-pattern)))
|
||||||
(let
|
(if
|
||||||
((id-val (_extract-attr-value src (+ id-abs 3))))
|
(= id-pos -1)
|
||||||
|
(_scan (+ elem-end 1) results)
|
||||||
(let
|
(let
|
||||||
((cs (_find-children-start src elem-start elem-end)))
|
((id-abs (+ elem-start id-pos)))
|
||||||
(let
|
(let
|
||||||
((children-str (slice src cs elem-end)))
|
((id-val (_extract-attr-value src (+ id-abs 3))))
|
||||||
(_scan
|
(let
|
||||||
(+ elem-end 1)
|
((cs (_find-children-start src elem-start elem-end)))
|
||||||
(append results (list {:end elem-end :mode mode :content children-str :start elem-start :id id-val}))))))))))))))))))))
|
(let
|
||||||
(_scan 0 (list))))
|
((children-str (slice src cs elem-end)))
|
||||||
|
(_scan
|
||||||
(define
|
(+ elem-end 1)
|
||||||
strip-oob
|
(append results (list {:end elem-end :mode mode :content children-str :start elem-start :id id-val}))))))))))))))))))))
|
||||||
(fn
|
(_scan 0 (list))))
|
||||||
(src oob-list)
|
(define
|
||||||
(if
|
strip-oob
|
||||||
(empty? oob-list)
|
(fn
|
||||||
src
|
(src oob-list)
|
||||||
(let
|
(if
|
||||||
((sorted (reverse oob-list)))
|
(empty? oob-list)
|
||||||
(define
|
src
|
||||||
_strip
|
(let
|
||||||
(fn
|
((sorted (reverse oob-list)))
|
||||||
(s items)
|
|
||||||
(if
|
|
||||||
(empty? items)
|
|
||||||
s
|
|
||||||
(let
|
|
||||||
((item (first items)))
|
|
||||||
(let
|
|
||||||
((before (slice s 0 (get item "start")))
|
|
||||||
(after (slice s (+ (get item "end") 1) (len s))))
|
|
||||||
(_strip (str before after) (rest items)))))))
|
|
||||||
(_strip src sorted)))))
|
|
||||||
|
|
||||||
(define
|
|
||||||
apply-response
|
|
||||||
(fn
|
|
||||||
(page response primary-mode primary-target)
|
|
||||||
(let
|
|
||||||
((oobs (find-oob-elements response)))
|
|
||||||
(let
|
|
||||||
((main-content (strip-oob response oobs)))
|
|
||||||
(let
|
|
||||||
((result (sx-swap page primary-mode primary-target main-content)))
|
|
||||||
(do
|
|
||||||
(define
|
(define
|
||||||
_apply-oobs
|
_strip
|
||||||
(fn
|
(fn
|
||||||
(page-acc items)
|
(s items)
|
||||||
(if
|
(if
|
||||||
(empty? items)
|
(empty? items)
|
||||||
page-acc
|
s
|
||||||
(let
|
(let
|
||||||
((oob (first items)))
|
((item (first items)))
|
||||||
(_apply-oobs
|
(let
|
||||||
(sx-swap
|
((before (slice s 0 (get item "start")))
|
||||||
page-acc
|
(after (slice s (+ (get item "end") 1) (len s))))
|
||||||
(get oob "mode")
|
(_strip (str before after) (rest items)))))))
|
||||||
(get oob "id")
|
(_strip src sorted)))))
|
||||||
(get oob "content"))
|
(define
|
||||||
(rest items))))))
|
apply-response
|
||||||
(_apply-oobs result oobs)))))))
|
(fn
|
||||||
|
(page response primary-mode primary-target)
|
||||||
|
(let
|
||||||
)) ;; end define-library
|
((oobs (find-oob-elements response)))
|
||||||
|
(let
|
||||||
|
((main-content (strip-oob response oobs)))
|
||||||
|
(let
|
||||||
|
((result (sx-swap page primary-mode primary-target main-content)))
|
||||||
|
(do
|
||||||
|
(define
|
||||||
|
_apply-oobs
|
||||||
|
(fn
|
||||||
|
(page-acc items)
|
||||||
|
(if
|
||||||
|
(empty? items)
|
||||||
|
page-acc
|
||||||
|
(let
|
||||||
|
((oob (first items)))
|
||||||
|
(_apply-oobs
|
||||||
|
(sx-swap
|
||||||
|
page-acc
|
||||||
|
(get oob "mode")
|
||||||
|
(get oob "id")
|
||||||
|
(get oob "content"))
|
||||||
|
(rest items))))))
|
||||||
|
(_apply-oobs result oobs))))))))) ;; end define-library
|
||||||
|
|
||||||
;; Re-export to global namespace for backward compatibility
|
;; Re-export to global namespace for backward compatibility
|
||||||
(import (sx swap))
|
(import (sx swap))
|
||||||
|
|||||||
@@ -63,33 +63,28 @@
|
|||||||
:effects ()
|
:effects ()
|
||||||
(fn
|
(fn
|
||||||
(node)
|
(node)
|
||||||
(cond
|
(match
|
||||||
(nil? node)
|
(type-of node)
|
||||||
"nil"
|
("nil" "nil")
|
||||||
(= (type-of node) "symbol")
|
("symbol" (symbol-name node))
|
||||||
(symbol-name node)
|
("keyword" (str ":" (keyword-name node)))
|
||||||
(= (type-of node) "keyword")
|
("string"
|
||||||
(str ":" (keyword-name node))
|
(let
|
||||||
(= (type-of node) "string")
|
((s (if (> (len node) 40) (str (slice node 0 37) "...") node)))
|
||||||
(let
|
(str "\"" s "\"")))
|
||||||
((s (if (> (len node) 40) (str (slice node 0 37) "...") node)))
|
("number" (str node))
|
||||||
(str "\"" s "\""))
|
("boolean" (if node "true" "false"))
|
||||||
(= (type-of node) "number")
|
("list"
|
||||||
(str node)
|
(if
|
||||||
(= (type-of node) "boolean")
|
(empty? node)
|
||||||
(if node "true" "false")
|
"()"
|
||||||
(list? node)
|
(str
|
||||||
(if
|
"("
|
||||||
(empty? node)
|
(node-display (first node))
|
||||||
"()"
|
(if (> (len node) 1) " ..." "")
|
||||||
(str
|
")")))
|
||||||
"("
|
("dict" "{...}")
|
||||||
(node-display (first node))
|
(_ (str node)))))
|
||||||
(if (> (len node) 1) " ..." "")
|
|
||||||
")"))
|
|
||||||
(= (type-of node) "dict")
|
|
||||||
"{...}"
|
|
||||||
:else (str node))))
|
|
||||||
|
|
||||||
(define
|
(define
|
||||||
summarise
|
summarise
|
||||||
@@ -244,17 +239,16 @@
|
|||||||
:effects ()
|
:effects ()
|
||||||
(fn
|
(fn
|
||||||
(node pattern)
|
(node pattern)
|
||||||
(cond
|
(match
|
||||||
(= (type-of node) "symbol")
|
(type-of node)
|
||||||
(contains? (symbol-name node) pattern)
|
("symbol" (contains? (symbol-name node) pattern))
|
||||||
(string? node)
|
("string" (contains? node pattern))
|
||||||
(contains? node pattern)
|
("list"
|
||||||
(and
|
(if
|
||||||
(list? node)
|
(empty? node)
|
||||||
(not (empty? node))
|
false
|
||||||
(= (type-of (first node)) "symbol"))
|
(some (fn (child) (node-matches? child pattern)) node)))
|
||||||
(contains? (symbol-name (first node)) pattern)
|
(_ false))))
|
||||||
:else false)))
|
|
||||||
|
|
||||||
(define
|
(define
|
||||||
node-summary-short
|
node-summary-short
|
||||||
@@ -546,33 +540,33 @@
|
|||||||
:effects ()
|
:effects ()
|
||||||
(fn
|
(fn
|
||||||
(node replacement)
|
(node replacement)
|
||||||
(cond
|
(match
|
||||||
(and (= (type-of node) "symbol") (= (symbol-name node) "_"))
|
(type-of node)
|
||||||
replacement
|
("symbol" (if (= (symbol-name node) "_") replacement nil))
|
||||||
(list? node)
|
("list"
|
||||||
(let
|
(let
|
||||||
((found false)
|
((found false)
|
||||||
(result
|
(result
|
||||||
(map
|
(map
|
||||||
(fn
|
(fn
|
||||||
(child)
|
(child)
|
||||||
(if
|
|
||||||
found
|
|
||||||
child
|
|
||||||
(if
|
(if
|
||||||
(and
|
found
|
||||||
(= (type-of child) "symbol")
|
child
|
||||||
(= (symbol-name child) "_"))
|
|
||||||
(do (set! found true) replacement)
|
|
||||||
(if
|
(if
|
||||||
(list? child)
|
(and
|
||||||
(let
|
(= (type-of child) "symbol")
|
||||||
((sub (replace-placeholder child replacement)))
|
(= (symbol-name child) "_"))
|
||||||
(if (nil? sub) child (do (set! found true) sub)))
|
(do (set! found true) replacement)
|
||||||
child))))
|
(if
|
||||||
node)))
|
(list? child)
|
||||||
(if found result nil))
|
(let
|
||||||
:else nil)))
|
((sub (replace-placeholder child replacement)))
|
||||||
|
(if (nil? sub) child (do (set! found true) sub)))
|
||||||
|
child))))
|
||||||
|
node)))
|
||||||
|
(if found result nil)))
|
||||||
|
(_ nil))))
|
||||||
|
|
||||||
(define
|
(define
|
||||||
tree-set
|
tree-set
|
||||||
@@ -851,12 +845,13 @@
|
|||||||
:effects ()
|
:effects ()
|
||||||
(fn
|
(fn
|
||||||
(node old-name new-name)
|
(node old-name new-name)
|
||||||
(cond
|
(match
|
||||||
(and (= (type-of node) "symbol") (= (symbol-name node) old-name))
|
(type-of node)
|
||||||
(make-symbol new-name)
|
("symbol"
|
||||||
(list? node)
|
(if (= (symbol-name node) old-name) (make-symbol new-name) node))
|
||||||
(map (fn (child) (rename-in-node child old-name new-name)) node)
|
("list"
|
||||||
:else node)))
|
(map (fn (child) (rename-in-node child old-name new-name)) node))
|
||||||
|
(_ node))))
|
||||||
|
|
||||||
(define
|
(define
|
||||||
count-renames
|
count-renames
|
||||||
@@ -873,12 +868,12 @@
|
|||||||
:effects ()
|
:effects ()
|
||||||
(fn
|
(fn
|
||||||
(node old-name hits)
|
(node old-name hits)
|
||||||
(cond
|
(match
|
||||||
(and (= (type-of node) "symbol") (= (symbol-name node) old-name))
|
(type-of node)
|
||||||
(append! hits true)
|
("symbol" (when (= (symbol-name node) old-name) (append! hits true)))
|
||||||
(list? node)
|
("list"
|
||||||
(for-each (fn (child) (count-in-node child old-name hits)) node)
|
(for-each (fn (child) (count-in-node child old-name hits)) node))
|
||||||
:else nil)))
|
(_ nil))))
|
||||||
|
|
||||||
(define
|
(define
|
||||||
replace-by-pattern
|
replace-by-pattern
|
||||||
@@ -1341,17 +1336,30 @@
|
|||||||
(walk node (dict))
|
(walk node (dict))
|
||||||
result)))
|
result)))
|
||||||
|
|
||||||
(define find-use-declarations :effects ()
|
(define
|
||||||
(fn (nodes)
|
find-use-declarations
|
||||||
(let ((uses (list)))
|
:effects ()
|
||||||
(for-each (fn (node)
|
(fn
|
||||||
(when (and (list? node) (>= (len node) 2)
|
(nodes)
|
||||||
(= (type-of (first node)) "symbol")
|
(let
|
||||||
(= (symbol-name (first node)) "use"))
|
((uses (list)))
|
||||||
(for-each (fn (arg)
|
(for-each
|
||||||
(cond
|
(fn
|
||||||
(= (type-of arg) "symbol") (append! uses (symbol-name arg))
|
(node)
|
||||||
(= (type-of arg) "string") (append! uses arg)))
|
(when
|
||||||
(rest node))))
|
(and
|
||||||
|
(list? node)
|
||||||
|
(>= (len node) 2)
|
||||||
|
(= (type-of (first node)) "symbol")
|
||||||
|
(= (symbol-name (first node)) "use"))
|
||||||
|
(for-each
|
||||||
|
(fn
|
||||||
|
(arg)
|
||||||
|
(cond
|
||||||
|
(= (type-of arg) "symbol")
|
||||||
|
(append! uses (symbol-name arg))
|
||||||
|
(= (type-of arg) "string")
|
||||||
|
(append! uses arg)))
|
||||||
|
(rest node))))
|
||||||
(if (list? nodes) nodes (list nodes)))
|
(if (list? nodes) nodes (list nodes)))
|
||||||
uses)))
|
uses)))
|
||||||
|
|||||||
1583
lib/types.sx
1583
lib/types.sx
File diff suppressed because it is too large
Load Diff
133
lib/vm.sx
133
lib/vm.sx
@@ -79,35 +79,35 @@
|
|||||||
(fn
|
(fn
|
||||||
(vm value)
|
(vm value)
|
||||||
(let
|
(let
|
||||||
((sp (get vm "sp")) (stack (get vm "stack")))
|
((sp (vm-sp vm)) (stack (vm-stack vm)))
|
||||||
(when
|
(when
|
||||||
(>= sp (vm-stack-length stack))
|
(>= sp (vm-stack-length stack))
|
||||||
(let
|
(let
|
||||||
((new-stack (make-vm-stack (* sp 2))))
|
((new-stack (vm-stack-grow stack sp)))
|
||||||
(vm-stack-copy! stack new-stack sp)
|
(vm-stack-copy! stack new-stack sp)
|
||||||
(dict-set! vm "stack" new-stack)
|
(vm-set-stack! vm new-stack)
|
||||||
(set! stack new-stack)))
|
(set! stack new-stack)))
|
||||||
(vm-stack-set! stack sp value)
|
(vm-stack-set! stack sp value)
|
||||||
(dict-set! vm "sp" (+ sp 1)))))
|
(vm-set-sp! vm (+ sp 1)))))
|
||||||
(define
|
(define
|
||||||
vm-pop
|
vm-pop
|
||||||
(fn
|
(fn
|
||||||
(vm)
|
(vm)
|
||||||
(let
|
(let
|
||||||
((sp (- (get vm "sp") 1)))
|
((sp (- (vm-sp vm) 1)))
|
||||||
(dict-set! vm "sp" sp)
|
(vm-set-sp! vm sp)
|
||||||
(vm-stack-get (get vm "stack") sp))))
|
(vm-stack-get (vm-stack vm) sp))))
|
||||||
(define
|
(define
|
||||||
vm-peek
|
vm-peek
|
||||||
(fn (vm) (vm-stack-get (get vm "stack") (- (get vm "sp") 1))))
|
(fn (vm) (vm-stack-get (vm-stack vm) (- (vm-sp vm) 1))))
|
||||||
(define
|
(define
|
||||||
frame-read-u8
|
frame-read-u8
|
||||||
(fn
|
(fn
|
||||||
(frame)
|
(frame)
|
||||||
(let
|
(let
|
||||||
((ip (get frame "ip"))
|
((ip (frame-ip frame))
|
||||||
(bc (get (get (get frame "closure") "vm-code") "vc-bytecode")))
|
(bc (-> frame frame-closure closure-code code-bytecode)))
|
||||||
(let ((v (nth bc ip))) (dict-set! frame "ip" (+ ip 1)) v))))
|
(let ((v (nth bc ip))) (frame-set-ip! frame (+ ip 1)) v))))
|
||||||
(define
|
(define
|
||||||
frame-read-u16
|
frame-read-u16
|
||||||
(fn
|
(fn
|
||||||
@@ -206,31 +206,28 @@
|
|||||||
(if
|
(if
|
||||||
(has-key? cells key)
|
(has-key? cells key)
|
||||||
(uv-get (get cells key))
|
(uv-get (get cells key))
|
||||||
(vm-stack-get (get vm "stack") (+ (get frame "base") slot))))))
|
(vm-stack-get (vm-stack vm) (+ (frame-base frame) slot))))))
|
||||||
(define
|
(define
|
||||||
frame-local-set
|
frame-local-set
|
||||||
(fn
|
(fn
|
||||||
(vm frame slot value)
|
(vm frame slot value)
|
||||||
"Write a local variable — to shared cell if captured, else to stack."
|
"Write a local variable — to shared cell or stack."
|
||||||
(let
|
(let
|
||||||
((cells (get frame "local-cells")) (key (str slot)))
|
((cells (get frame "local-cells")) (key (str slot)))
|
||||||
(if
|
(if
|
||||||
(has-key? cells key)
|
(has-key? cells key)
|
||||||
(uv-set! (get cells key) value)
|
(uv-set! (get cells key) value)
|
||||||
(vm-stack-set!
|
(vm-stack-set! (vm-stack vm) (+ (frame-base frame) slot) value)))))
|
||||||
(get vm "stack")
|
|
||||||
(+ (get frame "base") slot)
|
|
||||||
value)))))
|
|
||||||
(define
|
(define
|
||||||
frame-upvalue-get
|
frame-upvalue-get
|
||||||
(fn
|
(fn
|
||||||
(frame idx)
|
(frame idx)
|
||||||
(uv-get (nth (get (get frame "closure") "vm-upvalues") idx))))
|
(uv-get (nth (-> frame frame-closure closure-upvalues) idx))))
|
||||||
(define
|
(define
|
||||||
frame-upvalue-set
|
frame-upvalue-set
|
||||||
(fn
|
(fn
|
||||||
(frame idx value)
|
(frame idx value)
|
||||||
(uv-set! (nth (get (get frame "closure") "vm-upvalues") idx) value)))
|
(uv-set! (nth (-> frame frame-closure closure-upvalues) idx) value)))
|
||||||
(define frame-ip (fn (frame) (get frame "ip")))
|
(define frame-ip (fn (frame) (get frame "ip")))
|
||||||
(define frame-set-ip! (fn (frame val) (dict-set! frame "ip" val)))
|
(define frame-set-ip! (fn (frame val) (dict-set! frame "ip" val)))
|
||||||
(define frame-base (fn (frame) (get frame "base")))
|
(define frame-base (fn (frame) (get frame "base")))
|
||||||
@@ -302,12 +299,12 @@
|
|||||||
(vm frame name)
|
(vm frame name)
|
||||||
"Look up a global: globals table → closure env → primitives → HO wrappers"
|
"Look up a global: globals table → closure env → primitives → HO wrappers"
|
||||||
(let
|
(let
|
||||||
((globals (get vm "globals")))
|
((globals (vm-globals-ref vm)))
|
||||||
(if
|
(if
|
||||||
(has-key? globals name)
|
(has-key? globals name)
|
||||||
(get globals name)
|
(get globals name)
|
||||||
(let
|
(let
|
||||||
((closure-env (get (get frame "closure") "closure-env")))
|
((closure-env (-> frame frame-closure closure-env)))
|
||||||
(if
|
(if
|
||||||
(nil? closure-env)
|
(nil? closure-env)
|
||||||
(cek-try
|
(cek-try
|
||||||
@@ -325,41 +322,42 @@
|
|||||||
vm-resolve-ho-form
|
vm-resolve-ho-form
|
||||||
(fn
|
(fn
|
||||||
(vm name)
|
(vm name)
|
||||||
(cond
|
(match
|
||||||
(= name "for-each")
|
name
|
||||||
(fn
|
("for-each"
|
||||||
(f coll)
|
(fn
|
||||||
(for-each (fn (x) (vm-call-external vm f (list x))) coll))
|
(f coll)
|
||||||
(= name "map")
|
(for-each (fn (x) (vm-call-external vm f (list x))) coll)))
|
||||||
(fn
|
("map"
|
||||||
(f coll)
|
(fn
|
||||||
(map (fn (x) (vm-call-external vm f (list x))) coll))
|
(f coll)
|
||||||
(= name "map-indexed")
|
(map (fn (x) (vm-call-external vm f (list x))) coll)))
|
||||||
(fn
|
("map-indexed"
|
||||||
(f coll)
|
(fn
|
||||||
(map-indexed
|
(f coll)
|
||||||
(fn (i x) (vm-call-external vm f (list i x)))
|
(map-indexed
|
||||||
coll))
|
(fn (i x) (vm-call-external vm f (list i x)))
|
||||||
(= name "filter")
|
coll)))
|
||||||
(fn
|
("filter"
|
||||||
(f coll)
|
(fn
|
||||||
(filter (fn (x) (vm-call-external vm f (list x))) coll))
|
(f coll)
|
||||||
(= name "reduce")
|
(filter (fn (x) (vm-call-external vm f (list x))) coll)))
|
||||||
(fn
|
("reduce"
|
||||||
(f init coll)
|
(fn
|
||||||
(reduce
|
(f init coll)
|
||||||
(fn (acc x) (vm-call-external vm f (list acc x)))
|
(reduce
|
||||||
init
|
(fn (acc x) (vm-call-external vm f (list acc x)))
|
||||||
coll))
|
init
|
||||||
(= name "some")
|
coll)))
|
||||||
(fn
|
("some"
|
||||||
(f coll)
|
(fn
|
||||||
(some (fn (x) (vm-call-external vm f (list x))) coll))
|
(f coll)
|
||||||
(= name "every?")
|
(some (fn (x) (vm-call-external vm f (list x))) coll)))
|
||||||
(fn
|
("every?"
|
||||||
(f coll)
|
(fn
|
||||||
(every? (fn (x) (vm-call-external vm f (list x))) coll))
|
(f coll)
|
||||||
:else (error (str "VM undefined: " name)))))
|
(every? (fn (x) (vm-call-external vm f (list x))) coll)))
|
||||||
|
(_ (error (str "VM undefined: " name))))))
|
||||||
(define
|
(define
|
||||||
vm-call-external
|
vm-call-external
|
||||||
(fn
|
(fn
|
||||||
@@ -372,14 +370,14 @@
|
|||||||
vm-global-set
|
vm-global-set
|
||||||
(fn
|
(fn
|
||||||
(vm frame name value)
|
(vm frame name value)
|
||||||
"Set a global: write to closure env if name exists there, else globals."
|
"Set a global: write to closure env if found, else globals table."
|
||||||
(let
|
(let
|
||||||
((closure-env (get (get frame "closure") "vm-closure-env"))
|
((closure-env (get (frame-closure frame) "vm-closure-env"))
|
||||||
(written false))
|
(written false))
|
||||||
(when
|
(when
|
||||||
(not (nil? closure-env))
|
(not (nil? closure-env))
|
||||||
(set! written (env-walk-set! closure-env name value)))
|
(set! written (env-walk-set! closure-env name value)))
|
||||||
(when (not written) (dict-set! (get vm "globals") name value)))))
|
(when (not written) (dict-set! (vm-globals-ref vm) name value)))))
|
||||||
(define
|
(define
|
||||||
env-walk
|
env-walk
|
||||||
(fn
|
(fn
|
||||||
@@ -414,20 +412,15 @@
|
|||||||
(let
|
(let
|
||||||
((code (code-from-value code-val))
|
((code (code-from-value code-val))
|
||||||
(uv-count
|
(uv-count
|
||||||
(if
|
(if (dict? code-val) (or (get code-val "upvalue-count") 0) 0)))
|
||||||
(dict? code-val)
|
|
||||||
(let
|
|
||||||
((n (get code-val "upvalue-count")))
|
|
||||||
(if (nil? n) 0 n))
|
|
||||||
0)))
|
|
||||||
(let
|
(let
|
||||||
((upvalues (let ((result (list)) (i 0)) (define capture-loop (fn () (when (< i uv-count) (let ((is-local (frame-read-u8 frame)) (index (frame-read-u8 frame))) (let ((cell (if (= is-local 1) (let ((cells (get frame "local-cells")) (key (str index))) (if (has-key? cells key) (get cells key) (let ((c (make-upvalue-cell (vm-stack-get (get vm "stack") (+ (get frame "base") index))))) (dict-set! cells key c) c))) (nth (get (get frame "closure") "vm-upvalues") index)))) (append! result cell) (set! i (+ i 1)) (capture-loop)))))) (capture-loop) result)))
|
((upvalues (let ((result (list)) (i 0)) (define capture-loop (fn () (when (< i uv-count) (let ((is-local (frame-read-u8 frame)) (index (frame-read-u8 frame))) (let ((cell (if (= is-local 1) (let ((cells (get frame "local-cells")) (key (str index))) (if (has-key? cells key) (get cells key) (let ((c (make-upvalue-cell (vm-stack-get (vm-stack vm) (+ (frame-base frame) index))))) (dict-set! cells key c) c))) (nth (-> frame frame-closure closure-upvalues) index)))) (append! result cell) (set! i (+ i 1)) (capture-loop)))))) (capture-loop) result)))
|
||||||
(make-vm-closure code upvalues nil (get vm "globals") nil)))))
|
(make-vm-closure code upvalues nil (vm-globals-ref vm) nil)))))
|
||||||
(define
|
(define
|
||||||
vm-run
|
vm-run
|
||||||
(fn
|
(fn
|
||||||
(vm)
|
(vm)
|
||||||
"Execute bytecode until all frames are consumed."
|
"Execute bytecode until all frames are done or IO suspension."
|
||||||
(define
|
(define
|
||||||
loop
|
loop
|
||||||
(fn
|
(fn
|
||||||
@@ -438,9 +431,9 @@
|
|||||||
((frame (first (vm-frames vm)))
|
((frame (first (vm-frames vm)))
|
||||||
(rest-frames (rest (vm-frames vm))))
|
(rest-frames (rest (vm-frames vm))))
|
||||||
(let
|
(let
|
||||||
((bc (code-bytecode (closure-code (frame-closure frame))))
|
((bc (-> frame frame-closure closure-code code-bytecode))
|
||||||
(consts
|
(consts
|
||||||
(code-constants (closure-code (frame-closure frame)))))
|
(-> frame frame-closure closure-code code-constants)))
|
||||||
(if
|
(if
|
||||||
(>= (frame-ip frame) (len bc))
|
(>= (frame-ip frame) (len bc))
|
||||||
(vm-set-frames! vm (list))
|
(vm-set-frames! vm (list))
|
||||||
|
|||||||
@@ -121,57 +121,63 @@
|
|||||||
(case
|
(case
|
||||||
slug
|
slug
|
||||||
"bundle-analyzer"
|
"bundle-analyzer"
|
||||||
(let
|
(let-match
|
||||||
((data (helper "bundle-analyzer-data")))
|
{:total-macros total-macros :pages pages :io-count io-count :pure-count pure-count :total-components total-components}
|
||||||
|
(helper "bundle-analyzer-data")
|
||||||
(quasiquote
|
(quasiquote
|
||||||
(~analyzer/bundle-analyzer-content
|
(~analyzer/bundle-analyzer-content
|
||||||
:pages (unquote (get data "pages"))
|
:pages (unquote pages)
|
||||||
:total-components (unquote (get data "total-components"))
|
:total-components (unquote total-components)
|
||||||
:total-macros (unquote (get data "total-macros"))
|
:total-macros (unquote total-macros)
|
||||||
:pure-count (unquote (get data "pure-count"))
|
:pure-count (unquote pure-count)
|
||||||
:io-count (unquote (get data "io-count")))))
|
:io-count (unquote io-count))))
|
||||||
"routing-analyzer"
|
"routing-analyzer"
|
||||||
(let
|
(let-match
|
||||||
((data (helper "routing-analyzer-data")))
|
{:pages pages :total-pages total-pages :server-count server-count :registry-sample registry-sample :client-count client-count}
|
||||||
|
(helper "routing-analyzer-data")
|
||||||
(quasiquote
|
(quasiquote
|
||||||
(~routing-analyzer/content
|
(~routing-analyzer/content
|
||||||
:pages (unquote (get data "pages"))
|
:pages (unquote pages)
|
||||||
:total-pages (unquote (get data "total-pages"))
|
:total-pages (unquote total-pages)
|
||||||
:client-count (unquote (get data "client-count"))
|
:client-count (unquote client-count)
|
||||||
:server-count (unquote (get data "server-count"))
|
:server-count (unquote server-count)
|
||||||
:registry-sample (unquote (get data "registry-sample")))))
|
:registry-sample (unquote registry-sample))))
|
||||||
"data-test"
|
"data-test"
|
||||||
(let
|
(let-match
|
||||||
((data (helper "data-test-data")))
|
{:server-time server-time :transport transport :phase phase :items items}
|
||||||
|
(helper "data-test-data")
|
||||||
(quasiquote
|
(quasiquote
|
||||||
(~data-test/content
|
(~data-test/content
|
||||||
:server-time (unquote (get data "server-time"))
|
:server-time (unquote server-time)
|
||||||
:items (unquote (get data "items"))
|
:items (unquote items)
|
||||||
:phase (unquote (get data "phase"))
|
:phase (unquote phase)
|
||||||
:transport (unquote (get data "transport")))))
|
:transport (unquote transport))))
|
||||||
"async-io"
|
"async-io"
|
||||||
(quote (~async-io-demo/content))
|
(quote (~async-io-demo/content))
|
||||||
"affinity"
|
"affinity"
|
||||||
(let
|
(let-match
|
||||||
((data (helper "affinity-demo-data")))
|
{:components components :page-plans page-plans}
|
||||||
|
(helper "affinity-demo-data")
|
||||||
(quasiquote
|
(quasiquote
|
||||||
(~affinity-demo/content
|
(~affinity-demo/content
|
||||||
:components (unquote (get data "components"))
|
:components (unquote components)
|
||||||
:page-plans (unquote (get data "page-plans")))))
|
:page-plans (unquote page-plans))))
|
||||||
"optimistic"
|
"optimistic"
|
||||||
(let
|
(let-match
|
||||||
((data (helper "optimistic-demo-data")))
|
{:server-time server-time :items items}
|
||||||
|
(helper "optimistic-demo-data")
|
||||||
(quasiquote
|
(quasiquote
|
||||||
(~optimistic-demo/content
|
(~optimistic-demo/content
|
||||||
:items (unquote (get data "items"))
|
:items (unquote items)
|
||||||
:server-time (unquote (get data "server-time")))))
|
:server-time (unquote server-time))))
|
||||||
"offline"
|
"offline"
|
||||||
(let
|
(let-match
|
||||||
((data (helper "offline-demo-data")))
|
{:server-time server-time :notes notes}
|
||||||
|
(helper "offline-demo-data")
|
||||||
(quasiquote
|
(quasiquote
|
||||||
(~offline-demo/content
|
(~offline-demo/content
|
||||||
:notes (unquote (get data "notes"))
|
:notes (unquote notes)
|
||||||
:server-time (unquote (get data "server-time")))))
|
:server-time (unquote server-time))))
|
||||||
:else (quote (~plans/isomorphic/plan-isomorphic-content))))))
|
:else (quote (~plans/isomorphic/plan-isomorphic-content))))))
|
||||||
|
|
||||||
(define
|
(define
|
||||||
@@ -262,15 +268,18 @@
|
|||||||
((found-spec (find-spec slug)))
|
((found-spec (find-spec slug)))
|
||||||
(if
|
(if
|
||||||
found-spec
|
found-spec
|
||||||
(let
|
(let-match
|
||||||
((src (helper "read-spec-file" (get found-spec "filename"))))
|
{:desc desc :prose prose :title title :filename filename}
|
||||||
(quasiquote
|
found-spec
|
||||||
(~specs/detail-content
|
(let
|
||||||
:spec-title (unquote (get found-spec "title"))
|
((src (helper "read-spec-file" filename)))
|
||||||
:spec-desc (unquote (get found-spec "desc"))
|
(quasiquote
|
||||||
:spec-filename (unquote (get found-spec "filename"))
|
(~specs/detail-content
|
||||||
:spec-source (unquote src)
|
:spec-title (unquote title)
|
||||||
:spec-prose (unquote (get found-spec "prose")))))
|
:spec-desc (unquote desc)
|
||||||
|
:spec-filename (unquote filename)
|
||||||
|
:spec-source (unquote src)
|
||||||
|
:spec-prose (unquote prose)))))
|
||||||
(quasiquote (~specs/not-found :slug (unquote slug)))))))))
|
(quasiquote (~specs/not-found :slug (unquote slug)))))))))
|
||||||
|
|
||||||
(define
|
(define
|
||||||
@@ -324,54 +333,67 @@
|
|||||||
(case
|
(case
|
||||||
slug
|
slug
|
||||||
"self-hosting"
|
"self-hosting"
|
||||||
(quasiquote
|
(let-match
|
||||||
(~specs/bootstrapper-self-hosting-content
|
{:g1-output g1-output :py-sx-source py-sx-source :g0-bytes g0-bytes :verification-status verification-status :g0-output g0-output :defines-total defines-total :defines-matched defines-matched :g0-lines g0-lines}
|
||||||
:py-sx-source (unquote (get data "py-sx-source"))
|
data
|
||||||
:g0-output (unquote (get data "g0-output"))
|
(quasiquote
|
||||||
:g1-output (unquote (get data "g1-output"))
|
(~specs/bootstrapper-self-hosting-content
|
||||||
:defines-matched (unquote (get data "defines-matched"))
|
:py-sx-source (unquote py-sx-source)
|
||||||
:defines-total (unquote (get data "defines-total"))
|
:g0-output (unquote g0-output)
|
||||||
:g0-lines (unquote (get data "g0-lines"))
|
:g1-output (unquote g1-output)
|
||||||
:g0-bytes (unquote (get data "g0-bytes"))
|
:defines-matched (unquote defines-matched)
|
||||||
:verification-status (unquote (get data "verification-status"))))
|
:defines-total (unquote defines-total)
|
||||||
|
:g0-lines (unquote g0-lines)
|
||||||
|
:g0-bytes (unquote g0-bytes)
|
||||||
|
:verification-status (unquote verification-status))))
|
||||||
"self-hosting-js"
|
"self-hosting-js"
|
||||||
(quasiquote
|
(let-match
|
||||||
(~specs/bootstrapper-self-hosting-js-content
|
{:js-sx-source js-sx-source :verification-status verification-status :js-sx-lines js-sx-lines :defines-total defines-total :defines-matched defines-matched}
|
||||||
:js-sx-source (unquote (get data "js-sx-source"))
|
data
|
||||||
:defines-matched (unquote (get data "defines-matched"))
|
(quasiquote
|
||||||
:defines-total (unquote (get data "defines-total"))
|
(~specs/bootstrapper-self-hosting-js-content
|
||||||
:js-sx-lines (unquote (get data "js-sx-lines"))
|
:js-sx-source (unquote js-sx-source)
|
||||||
:verification-status (unquote (get data "verification-status"))))
|
:defines-matched (unquote defines-matched)
|
||||||
|
:defines-total (unquote defines-total)
|
||||||
|
:js-sx-lines (unquote js-sx-lines)
|
||||||
|
:verification-status (unquote verification-status))))
|
||||||
"python"
|
"python"
|
||||||
(quasiquote
|
(let-match
|
||||||
(~specs/bootstrapper-py-content
|
{:bootstrapper-source bootstrapper-source :bootstrapped-output bootstrapped-output}
|
||||||
:bootstrapper-source (unquote (get data "bootstrapper-source"))
|
data
|
||||||
:bootstrapped-output (unquote (get data "bootstrapped-output"))))
|
(quasiquote
|
||||||
|
(~specs/bootstrapper-py-content
|
||||||
|
:bootstrapper-source (unquote bootstrapper-source)
|
||||||
|
:bootstrapped-output (unquote bootstrapped-output))))
|
||||||
"page-helpers"
|
"page-helpers"
|
||||||
(let
|
(let-match
|
||||||
((ph-data (helper "page-helpers-demo-data")))
|
{:attr-result attr-result :sf-source sf-source :ref-ms ref-ms :req-attrs req-attrs :attr-detail attr-detail :attr-keys attr-keys :server-total-ms server-total-ms :attr-ms attr-ms :comp-ms comp-ms :routing-ms routing-ms :comp-source comp-source :routing-result routing-result :sf-categories sf-categories :sf-total sf-total :sf-ms sf-ms :ref-sample ref-sample}
|
||||||
|
(helper "page-helpers-demo-data")
|
||||||
(quasiquote
|
(quasiquote
|
||||||
(~page-helpers-demo/content
|
(~page-helpers-demo/content
|
||||||
:sf-categories (unquote (get ph-data "sf-categories"))
|
:sf-categories (unquote sf-categories)
|
||||||
:sf-total (unquote (get ph-data "sf-total"))
|
:sf-total (unquote sf-total)
|
||||||
:sf-ms (unquote (get ph-data "sf-ms"))
|
:sf-ms (unquote sf-ms)
|
||||||
:ref-sample (unquote (get ph-data "ref-sample"))
|
:ref-sample (unquote ref-sample)
|
||||||
:ref-ms (unquote (get ph-data "ref-ms"))
|
:ref-ms (unquote ref-ms)
|
||||||
:attr-result (unquote (get ph-data "attr-result"))
|
:attr-result (unquote attr-result)
|
||||||
:attr-ms (unquote (get ph-data "attr-ms"))
|
:attr-ms (unquote attr-ms)
|
||||||
:comp-source (unquote (get ph-data "comp-source"))
|
:comp-source (unquote comp-source)
|
||||||
:comp-ms (unquote (get ph-data "comp-ms"))
|
:comp-ms (unquote comp-ms)
|
||||||
:routing-result (unquote (get ph-data "routing-result"))
|
:routing-result (unquote routing-result)
|
||||||
:routing-ms (unquote (get ph-data "routing-ms"))
|
:routing-ms (unquote routing-ms)
|
||||||
:server-total-ms (unquote (get ph-data "server-total-ms"))
|
:server-total-ms (unquote server-total-ms)
|
||||||
:sf-source (unquote (get ph-data "sf-source"))
|
:sf-source (unquote sf-source)
|
||||||
:attr-detail (unquote (get ph-data "attr-detail"))
|
:attr-detail (unquote attr-detail)
|
||||||
:req-attrs (unquote (get ph-data "req-attrs"))
|
:req-attrs (unquote req-attrs)
|
||||||
:attr-keys (unquote (get ph-data "attr-keys")))))
|
:attr-keys (unquote attr-keys))))
|
||||||
:else (quasiquote
|
:else (let-match
|
||||||
(~specs/bootstrapper-js-content
|
{:bootstrapper-source bootstrapper-source :bootstrapped-output bootstrapped-output}
|
||||||
:bootstrapper-source (unquote (get data "bootstrapper-source"))
|
data
|
||||||
:bootstrapped-output (unquote (get data "bootstrapped-output"))))))))))
|
(quasiquote
|
||||||
|
(~specs/bootstrapper-js-content
|
||||||
|
:bootstrapper-source (unquote bootstrapper-source)
|
||||||
|
:bootstrapped-output (unquote bootstrapped-output))))))))))
|
||||||
|
|
||||||
(define
|
(define
|
||||||
test
|
test
|
||||||
@@ -379,24 +401,26 @@
|
|||||||
(slug)
|
(slug)
|
||||||
(if
|
(if
|
||||||
(nil? slug)
|
(nil? slug)
|
||||||
(let
|
(let-match
|
||||||
((data (helper "run-modular-tests" "all")))
|
{:server-results server-results :parser-source parser-source :framework-source framework-source :eval-source eval-source :router-source router-source :engine-source engine-source :render-source render-source :deps-source deps-source}
|
||||||
|
(helper "run-modular-tests" "all")
|
||||||
(quasiquote
|
(quasiquote
|
||||||
(~testing/overview-content
|
(~testing/overview-content
|
||||||
:server-results (unquote (get data "server-results"))
|
:server-results (unquote server-results)
|
||||||
:framework-source (unquote (get data "framework-source"))
|
:framework-source (unquote framework-source)
|
||||||
:eval-source (unquote (get data "eval-source"))
|
:eval-source (unquote eval-source)
|
||||||
:parser-source (unquote (get data "parser-source"))
|
:parser-source (unquote parser-source)
|
||||||
:router-source (unquote (get data "router-source"))
|
:router-source (unquote router-source)
|
||||||
:render-source (unquote (get data "render-source"))
|
:render-source (unquote render-source)
|
||||||
:deps-source (unquote (get data "deps-source"))
|
:deps-source (unquote deps-source)
|
||||||
:engine-source (unquote (get data "engine-source")))))
|
:engine-source (unquote engine-source))))
|
||||||
(case
|
(case
|
||||||
slug
|
slug
|
||||||
"runners"
|
"runners"
|
||||||
(quote (~testing/runners-content))
|
(quote (~testing/runners-content))
|
||||||
:else (let
|
:else (let-match
|
||||||
((data (helper "run-modular-tests" slug)))
|
{:server-results server-results :spec-source spec-source :framework-source framework-source}
|
||||||
|
(helper "run-modular-tests" slug)
|
||||||
(case
|
(case
|
||||||
slug
|
slug
|
||||||
"eval"
|
"eval"
|
||||||
@@ -404,67 +428,67 @@
|
|||||||
(~testing/spec-content
|
(~testing/spec-content
|
||||||
:spec-name "eval"
|
:spec-name "eval"
|
||||||
:spec-title "Evaluator Tests"
|
:spec-title "Evaluator Tests"
|
||||||
:spec-desc "81 tests covering the core evaluator and all primitives."
|
:spec-desc "81 tests covering the core evaluator — literals, symbols, special forms, closures."
|
||||||
:spec-source (unquote (get data "spec-source"))
|
:spec-source (unquote spec-source)
|
||||||
:framework-source (unquote (get data "framework-source"))
|
:framework-source (unquote framework-source)
|
||||||
:server-results (unquote (get data "server-results"))))
|
:server-results (unquote server-results)))
|
||||||
"parser"
|
"parser"
|
||||||
(quasiquote
|
(quasiquote
|
||||||
(~testing/spec-content
|
(~testing/spec-content
|
||||||
:spec-name "parser"
|
:spec-name "parser"
|
||||||
:spec-title "Parser Tests"
|
:spec-title "Parser Tests"
|
||||||
:spec-desc "39 tests covering tokenization and parsing."
|
:spec-desc "39 tests covering tokenization and parsing."
|
||||||
:spec-source (unquote (get data "spec-source"))
|
:spec-source (unquote spec-source)
|
||||||
:framework-source (unquote (get data "framework-source"))
|
:framework-source (unquote framework-source)
|
||||||
:server-results (unquote (get data "server-results"))))
|
:server-results (unquote server-results)))
|
||||||
"router"
|
"router"
|
||||||
(quasiquote
|
(quasiquote
|
||||||
(~testing/spec-content
|
(~testing/spec-content
|
||||||
:spec-name "router"
|
:spec-name "router"
|
||||||
:spec-title "Router Tests"
|
:spec-title "Router Tests"
|
||||||
:spec-desc "18 tests covering client-side route matching."
|
:spec-desc "18 tests covering client-side route matching."
|
||||||
:spec-source (unquote (get data "spec-source"))
|
:spec-source (unquote spec-source)
|
||||||
:framework-source (unquote (get data "framework-source"))
|
:framework-source (unquote framework-source)
|
||||||
:server-results (unquote (get data "server-results"))))
|
:server-results (unquote server-results)))
|
||||||
"render"
|
"render"
|
||||||
(quasiquote
|
(quasiquote
|
||||||
(~testing/spec-content
|
(~testing/spec-content
|
||||||
:spec-name "render"
|
:spec-name "render"
|
||||||
:spec-title "Renderer Tests"
|
:spec-title "Renderer Tests"
|
||||||
:spec-desc "23 tests covering HTML rendering."
|
:spec-desc "23 tests covering HTML rendering."
|
||||||
:spec-source (unquote (get data "spec-source"))
|
:spec-source (unquote spec-source)
|
||||||
:framework-source (unquote (get data "framework-source"))
|
:framework-source (unquote framework-source)
|
||||||
:server-results (unquote (get data "server-results"))))
|
:server-results (unquote server-results)))
|
||||||
"deps"
|
"deps"
|
||||||
(quasiquote
|
(quasiquote
|
||||||
(~testing/spec-content
|
(~testing/spec-content
|
||||||
:spec-name "deps"
|
:spec-name "deps"
|
||||||
:spec-title "Dependency Analysis Tests"
|
:spec-title "Dependency Analysis Tests"
|
||||||
:spec-desc "33 tests covering component dependency analysis."
|
:spec-desc "33 tests covering component dependency analysis."
|
||||||
:spec-source (unquote (get data "spec-source"))
|
:spec-source (unquote spec-source)
|
||||||
:framework-source (unquote (get data "framework-source"))
|
:framework-source (unquote framework-source)
|
||||||
:server-results (unquote (get data "server-results"))))
|
:server-results (unquote server-results)))
|
||||||
"engine"
|
"engine"
|
||||||
(quasiquote
|
(quasiquote
|
||||||
(~testing/spec-content
|
(~testing/spec-content
|
||||||
:spec-name "engine"
|
:spec-name "engine"
|
||||||
:spec-title "Engine Tests"
|
:spec-title "Engine Tests"
|
||||||
:spec-desc "37 tests covering engine pure functions."
|
:spec-desc "37 tests covering engine pure functions."
|
||||||
:spec-source (unquote (get data "spec-source"))
|
:spec-source (unquote spec-source)
|
||||||
:framework-source (unquote (get data "framework-source"))
|
:framework-source (unquote framework-source)
|
||||||
:server-results (unquote (get data "server-results"))))
|
:server-results (unquote server-results)))
|
||||||
"orchestration"
|
"orchestration"
|
||||||
(quasiquote
|
(quasiquote
|
||||||
(~testing/spec-content
|
(~testing/spec-content
|
||||||
:spec-name "orchestration"
|
:spec-name "orchestration"
|
||||||
:spec-title "Orchestration Tests"
|
:spec-title "Orchestration Tests"
|
||||||
:spec-desc "17 tests covering orchestration."
|
:spec-desc "17 tests covering orchestration."
|
||||||
:spec-source (unquote (get data "spec-source"))
|
:spec-source (unquote spec-source)
|
||||||
:framework-source (unquote (get data "framework-source"))
|
:framework-source (unquote framework-source)
|
||||||
:server-results (unquote (get data "server-results"))))
|
:server-results (unquote server-results)))
|
||||||
:else (quasiquote
|
:else (quasiquote
|
||||||
(~testing/overview-content
|
(~testing/overview-content
|
||||||
:server-results (unquote (get data "server-results"))))))))))
|
:server-results (unquote server-results)))))))))
|
||||||
|
|
||||||
(define
|
(define
|
||||||
reference
|
reference
|
||||||
@@ -478,26 +502,32 @@
|
|||||||
(case
|
(case
|
||||||
slug
|
slug
|
||||||
"attributes"
|
"attributes"
|
||||||
(quasiquote
|
(let-match
|
||||||
(~reference/attrs-content
|
{:req-attrs req-attrs :beh-attrs beh-attrs :uniq-attrs uniq-attrs}
|
||||||
:req-table (~docs/attr-table-from-data
|
data
|
||||||
:title "Request Attributes"
|
(quasiquote
|
||||||
:attrs (unquote (get data "req-attrs")))
|
(~reference/attrs-content
|
||||||
:beh-table (~docs/attr-table-from-data
|
:req-table (~docs/attr-table-from-data
|
||||||
:title "Behavior Attributes"
|
:title "Request Attributes"
|
||||||
:attrs (unquote (get data "beh-attrs")))
|
:attrs (unquote req-attrs))
|
||||||
:uniq-table (~docs/attr-table-from-data
|
:beh-table (~docs/attr-table-from-data
|
||||||
:title "Unique to sx"
|
:title "Behavior Attributes"
|
||||||
:attrs (unquote (get data "uniq-attrs")))))
|
:attrs (unquote beh-attrs))
|
||||||
|
:uniq-table (~docs/attr-table-from-data
|
||||||
|
:title "Unique to sx"
|
||||||
|
:attrs (unquote uniq-attrs)))))
|
||||||
"headers"
|
"headers"
|
||||||
(quasiquote
|
(let-match
|
||||||
(~reference/headers-content
|
{:req-headers req-headers :resp-headers resp-headers}
|
||||||
:req-table (~docs/headers-table-from-data
|
data
|
||||||
:title "Request Headers"
|
(quasiquote
|
||||||
:headers (unquote (get data "req-headers")))
|
(~reference/headers-content
|
||||||
:resp-table (~docs/headers-table-from-data
|
:req-table (~docs/headers-table-from-data
|
||||||
:title "Response Headers"
|
:title "Request Headers"
|
||||||
:headers (unquote (get data "resp-headers")))))
|
:headers (unquote req-headers))
|
||||||
|
:resp-table (~docs/headers-table-from-data
|
||||||
|
:title "Response Headers"
|
||||||
|
:headers (unquote resp-headers)))))
|
||||||
"events"
|
"events"
|
||||||
(quasiquote
|
(quasiquote
|
||||||
(~reference/events-content
|
(~reference/events-content
|
||||||
@@ -514,17 +544,20 @@
|
|||||||
:col1 "Method"
|
:col1 "Method"
|
||||||
:col2 "Description"
|
:col2 "Description"
|
||||||
:items (unquote (get data "js-api-list")))))
|
:items (unquote (get data "js-api-list")))))
|
||||||
:else (quasiquote
|
:else (let-match
|
||||||
(~reference/attrs-content
|
{:req-attrs req-attrs :beh-attrs beh-attrs :uniq-attrs uniq-attrs}
|
||||||
:req-table (~docs/attr-table-from-data
|
data
|
||||||
:title "Request Attributes"
|
(quasiquote
|
||||||
:attrs (unquote (get data "req-attrs")))
|
(~reference/attrs-content
|
||||||
:beh-table (~docs/attr-table-from-data
|
:req-table (~docs/attr-table-from-data
|
||||||
:title "Behavior Attributes"
|
:title "Request Attributes"
|
||||||
:attrs (unquote (get data "beh-attrs")))
|
:attrs (unquote req-attrs))
|
||||||
:uniq-table (~docs/attr-table-from-data
|
:beh-table (~docs/attr-table-from-data
|
||||||
:title "Unique to sx"
|
:title "Behavior Attributes"
|
||||||
:attrs (unquote (get data "uniq-attrs"))))))))))
|
:attrs (unquote beh-attrs))
|
||||||
|
:uniq-table (~docs/attr-table-from-data
|
||||||
|
:title "Unique to sx"
|
||||||
|
:attrs (unquote uniq-attrs))))))))))
|
||||||
|
|
||||||
(define
|
(define
|
||||||
reference-detail
|
reference-detail
|
||||||
@@ -541,39 +574,48 @@
|
|||||||
(if
|
(if
|
||||||
(get data "attr-not-found")
|
(get data "attr-not-found")
|
||||||
(quasiquote (~reference/attr-not-found :slug (unquote slug)))
|
(quasiquote (~reference/attr-not-found :slug (unquote slug)))
|
||||||
(quasiquote
|
(let-match
|
||||||
(~reference/attr-detail-content
|
{:attr-handler attr-handler :attr-title attr-title :attr-example attr-example :attr-description attr-description :attr-demo attr-demo :attr-wire-id attr-wire-id}
|
||||||
:title (unquote (get data "attr-title"))
|
data
|
||||||
:description (unquote (get data "attr-description"))
|
(quasiquote
|
||||||
:demo (unquote (get data "attr-demo"))
|
(~reference/attr-detail-content
|
||||||
:example-code (unquote (get data "attr-example"))
|
:title (unquote attr-title)
|
||||||
:handler-code (unquote (get data "attr-handler"))
|
:description (unquote attr-description)
|
||||||
:wire-placeholder-id (unquote (get data "attr-wire-id"))))))
|
:demo (unquote attr-demo)
|
||||||
|
:example-code (unquote attr-example)
|
||||||
|
:handler-code (unquote attr-handler)
|
||||||
|
:wire-placeholder-id (unquote attr-wire-id))))))
|
||||||
"headers"
|
"headers"
|
||||||
(let
|
(let
|
||||||
((data (helper "header-detail-data" slug)))
|
((data (helper "header-detail-data" slug)))
|
||||||
(if
|
(if
|
||||||
(get data "header-not-found")
|
(get data "header-not-found")
|
||||||
(quasiquote (~reference/attr-not-found :slug (unquote slug)))
|
(quasiquote (~reference/attr-not-found :slug (unquote slug)))
|
||||||
(quasiquote
|
(let-match
|
||||||
(~reference/header-detail-content
|
{:header-description header-description :header-demo header-demo :header-title header-title :header-example header-example :header-direction header-direction}
|
||||||
:title (unquote (get data "header-title"))
|
data
|
||||||
:direction (unquote (get data "header-direction"))
|
(quasiquote
|
||||||
:description (unquote (get data "header-description"))
|
(~reference/header-detail-content
|
||||||
:example-code (unquote (get data "header-example"))
|
:title (unquote header-title)
|
||||||
:demo (unquote (get data "header-demo"))))))
|
:direction (unquote header-direction)
|
||||||
|
:description (unquote header-description)
|
||||||
|
:example-code (unquote header-example)
|
||||||
|
:demo (unquote header-demo))))))
|
||||||
"events"
|
"events"
|
||||||
(let
|
(let
|
||||||
((data (helper "event-detail-data" slug)))
|
((data (helper "event-detail-data" slug)))
|
||||||
(if
|
(if
|
||||||
(get data "event-not-found")
|
(get data "event-not-found")
|
||||||
(quasiquote (~reference/attr-not-found :slug (unquote slug)))
|
(quasiquote (~reference/attr-not-found :slug (unquote slug)))
|
||||||
(quasiquote
|
(let-match
|
||||||
(~reference/event-detail-content
|
{:event-example event-example :event-demo event-demo :event-description event-description :event-title event-title}
|
||||||
:title (unquote (get data "event-title"))
|
data
|
||||||
:description (unquote (get data "event-description"))
|
(quasiquote
|
||||||
:example-code (unquote (get data "event-example"))
|
(~reference/event-detail-content
|
||||||
:demo (unquote (get data "event-demo"))))))
|
:title (unquote event-title)
|
||||||
|
:description (unquote event-description)
|
||||||
|
:example-code (unquote event-example)
|
||||||
|
:demo (unquote event-demo))))))
|
||||||
:else nil))))
|
:else nil))))
|
||||||
|
|
||||||
(define
|
(define
|
||||||
|
|||||||
371
spec/signals.sx
371
spec/signals.sx
@@ -1,6 +1,7 @@
|
|||||||
|
|
||||||
|
|
||||||
(define-library (sx signals)
|
(define-library
|
||||||
|
(sx signals)
|
||||||
(export
|
(export
|
||||||
make-signal
|
make-signal
|
||||||
signal?
|
signal?
|
||||||
@@ -26,205 +27,193 @@
|
|||||||
with-island-scope
|
with-island-scope
|
||||||
register-in-scope)
|
register-in-scope)
|
||||||
(begin
|
(begin
|
||||||
|
(define
|
||||||
(define
|
make-signal
|
||||||
make-signal
|
(fn
|
||||||
(fn
|
(value)
|
||||||
(value)
|
(dict
|
||||||
(dict "__signal" true "value" value "subscribers" (list) "deps" (list))))
|
"__signal"
|
||||||
|
true
|
||||||
(define signal? (fn (x) (and (dict? x) (has-key? x "__signal"))))
|
"value"
|
||||||
|
value
|
||||||
(define signal-value (fn (s) (get s "value")))
|
"subscribers"
|
||||||
|
(list)
|
||||||
(define signal-set-value! (fn (s v) (dict-set! s "value" v)))
|
"deps"
|
||||||
|
(list))))
|
||||||
(define signal-subscribers (fn (s) (get s "subscribers")))
|
(define signal? (fn (x) (and (dict? x) (has-key? x "__signal"))))
|
||||||
|
(define signal-value (fn (s) (get s "value")))
|
||||||
(define
|
(define signal-set-value! (fn (s v) (dict-set! s "value" v)))
|
||||||
signal-add-sub!
|
(define signal-subscribers (fn (s) (get s "subscribers")))
|
||||||
(fn
|
(define
|
||||||
(s f)
|
signal-add-sub!
|
||||||
(when
|
(fn
|
||||||
(not (contains? (get s "subscribers") f))
|
(s f)
|
||||||
(dict-set! s "subscribers" (append (get s "subscribers") (list f))))))
|
|
||||||
|
|
||||||
(define
|
|
||||||
signal-remove-sub!
|
|
||||||
(fn
|
|
||||||
(s f)
|
|
||||||
(dict-set!
|
|
||||||
s
|
|
||||||
"subscribers"
|
|
||||||
(filter (fn (sub) (not (identical? sub f))) (get s "subscribers")))))
|
|
||||||
|
|
||||||
(define signal-deps (fn (s) (get s "deps")))
|
|
||||||
|
|
||||||
(define signal-set-deps! (fn (s deps) (dict-set! s "deps" deps)))
|
|
||||||
|
|
||||||
(define
|
|
||||||
signal
|
|
||||||
:effects ()
|
|
||||||
(fn ((initial-value :as any)) (make-signal initial-value)))
|
|
||||||
|
|
||||||
(define
|
|
||||||
deref
|
|
||||||
:effects ()
|
|
||||||
(fn
|
|
||||||
((s :as any))
|
|
||||||
(if
|
|
||||||
(not (signal? s))
|
|
||||||
s
|
|
||||||
(let
|
|
||||||
((ctx (context "sx-reactive" nil)))
|
|
||||||
(when
|
(when
|
||||||
ctx
|
(not (contains? (get s "subscribers") f))
|
||||||
|
(dict-set!
|
||||||
|
s
|
||||||
|
"subscribers"
|
||||||
|
(append (get s "subscribers") (list f))))))
|
||||||
|
(define
|
||||||
|
signal-remove-sub!
|
||||||
|
(fn
|
||||||
|
(s f)
|
||||||
|
(dict-set!
|
||||||
|
s
|
||||||
|
"subscribers"
|
||||||
|
(filter
|
||||||
|
(fn (sub) (not (identical? sub f)))
|
||||||
|
(get s "subscribers")))))
|
||||||
|
(define signal-deps (fn (s) (get s "deps")))
|
||||||
|
(define signal-set-deps! (fn (s deps) (dict-set! s "deps" deps)))
|
||||||
|
(define
|
||||||
|
signal
|
||||||
|
:effects ()
|
||||||
|
(fn ((initial-value :as any)) (make-signal initial-value)))
|
||||||
|
(define
|
||||||
|
deref
|
||||||
|
:effects ()
|
||||||
|
(fn
|
||||||
|
((s :as any))
|
||||||
|
(if
|
||||||
|
(not (signal? s))
|
||||||
|
s
|
||||||
(let
|
(let
|
||||||
((dep-list (get ctx "deps")) (notify-fn (get ctx "notify")))
|
((ctx (context "sx-reactive" nil)))
|
||||||
(when
|
(when
|
||||||
(not (contains? dep-list s))
|
ctx
|
||||||
(append! dep-list s)
|
(let
|
||||||
(signal-add-sub! s notify-fn))))
|
{:notify notify-fn :deps dep-list}
|
||||||
(signal-value s)))))
|
ctx
|
||||||
|
(when
|
||||||
(define
|
(not (contains? dep-list s))
|
||||||
reset!
|
(append! dep-list s)
|
||||||
:effects (mutation)
|
(signal-add-sub! s notify-fn))))
|
||||||
(fn
|
(signal-value s)))))
|
||||||
((s :as signal) value)
|
(define
|
||||||
(when
|
reset!
|
||||||
(signal? s)
|
:effects (mutation)
|
||||||
(let
|
(fn
|
||||||
((old (signal-value s)))
|
((s :as signal) value)
|
||||||
(when
|
(when
|
||||||
(not (identical? old value))
|
(signal? s)
|
||||||
(signal-set-value! s value)
|
(let
|
||||||
(notify-subscribers s))))))
|
((old (signal-value s)))
|
||||||
|
(when
|
||||||
(define
|
(not (identical? old value))
|
||||||
swap!
|
(signal-set-value! s value)
|
||||||
:effects (mutation)
|
(notify-subscribers s))))))
|
||||||
(fn
|
(define
|
||||||
((s :as signal) (f :as callable) &rest args)
|
swap!
|
||||||
(when
|
:effects (mutation)
|
||||||
(signal? s)
|
(fn
|
||||||
(let
|
((s :as signal) (f :as callable) &rest args)
|
||||||
((old (signal-value s))
|
|
||||||
(new-val (trampoline (apply f (cons old args)))))
|
|
||||||
(when
|
(when
|
||||||
(not (identical? old new-val))
|
(signal? s)
|
||||||
(signal-set-value! s new-val)
|
(let
|
||||||
(notify-subscribers s))))))
|
((old (signal-value s))
|
||||||
|
(new-val (trampoline (apply f (cons old args)))))
|
||||||
(define
|
(when
|
||||||
computed
|
(not (identical? old new-val))
|
||||||
:effects (mutation)
|
(signal-set-value! s new-val)
|
||||||
(fn
|
(notify-subscribers s))))))
|
||||||
((compute-fn :as lambda))
|
(define
|
||||||
(let
|
computed
|
||||||
((s (make-signal nil)) (deps (list)) (compute-ctx nil))
|
:effects (mutation)
|
||||||
(let
|
(fn
|
||||||
((recompute (fn () (for-each (fn ((dep :as signal)) (signal-remove-sub! dep recompute)) (signal-deps s)) (signal-set-deps! s (list)) (let ((ctx (dict "deps" (list) "notify" recompute))) (scope-push! "sx-reactive" ctx) (let ((new-val (cek-call compute-fn nil))) (scope-pop! "sx-reactive") (signal-set-deps! s (get ctx "deps")) (let ((old (signal-value s))) (signal-set-value! s new-val) (when (not (identical? old new-val)) (notify-subscribers s))))))))
|
((compute-fn :as lambda))
|
||||||
(recompute)
|
|
||||||
(register-in-scope (fn () (dispose-computed s)))
|
|
||||||
s))))
|
|
||||||
|
|
||||||
(define
|
|
||||||
effect
|
|
||||||
:effects (mutation)
|
|
||||||
(fn
|
|
||||||
((effect-fn :as lambda))
|
|
||||||
(let
|
|
||||||
((deps (list)) (disposed false) (cleanup-fn nil))
|
|
||||||
(let
|
|
||||||
((run-effect (fn () (when (not disposed) (when cleanup-fn (cek-call cleanup-fn nil)) (for-each (fn ((dep :as signal)) (signal-remove-sub! dep run-effect)) deps) (set! deps (list)) (let ((ctx (dict "deps" (list) "notify" run-effect))) (scope-push! "sx-reactive" ctx) (let ((result (cek-call effect-fn nil))) (scope-pop! "sx-reactive") (set! deps (get ctx "deps")) (when (callable? result) (set! cleanup-fn result))))))))
|
|
||||||
(run-effect)
|
|
||||||
(let
|
(let
|
||||||
((dispose-fn (fn () (set! disposed true) (when cleanup-fn (cek-call cleanup-fn nil)) (for-each (fn ((dep :as signal)) (signal-remove-sub! dep run-effect)) deps) (set! deps (list)))))
|
((s (make-signal nil)) (deps (list)) (compute-ctx nil))
|
||||||
(register-in-scope dispose-fn)
|
(let
|
||||||
dispose-fn)))))
|
((recompute (fn () (for-each (fn ((dep :as signal)) (signal-remove-sub! dep recompute)) (signal-deps s)) (signal-set-deps! s (list)) (let ((ctx (dict "deps" (list) "notify" recompute))) (scope-push! "sx-reactive" ctx) (let ((new-val (cek-call compute-fn nil))) (scope-pop! "sx-reactive") (signal-set-deps! s (get ctx "deps")) (let ((old (signal-value s))) (signal-set-value! s new-val) (when (not (identical? old new-val)) (notify-subscribers s))))))))
|
||||||
|
(recompute)
|
||||||
(define *batch-depth* 0)
|
(register-in-scope (fn () (dispose-computed s)))
|
||||||
|
s))))
|
||||||
(define *batch-queue* (list))
|
(define
|
||||||
|
effect
|
||||||
(define
|
:effects (mutation)
|
||||||
batch
|
(fn
|
||||||
:effects (mutation)
|
((effect-fn :as lambda))
|
||||||
(fn
|
|
||||||
((thunk :as lambda))
|
|
||||||
(set! *batch-depth* (+ *batch-depth* 1))
|
|
||||||
(cek-call thunk nil)
|
|
||||||
(set! *batch-depth* (- *batch-depth* 1))
|
|
||||||
(when
|
|
||||||
(= *batch-depth* 0)
|
|
||||||
(let
|
|
||||||
((queue *batch-queue*))
|
|
||||||
(set! *batch-queue* (list))
|
|
||||||
(let
|
(let
|
||||||
((seen (list)) (pending (list)))
|
((deps (list)) (disposed false) (cleanup-fn nil))
|
||||||
(for-each
|
(let
|
||||||
(fn
|
((run-effect (fn () (when (not disposed) (when cleanup-fn (cek-call cleanup-fn nil)) (for-each (fn ((dep :as signal)) (signal-remove-sub! dep run-effect)) deps) (set! deps (list)) (let ((ctx (dict "deps" (list) "notify" run-effect))) (scope-push! "sx-reactive" ctx) (let ((result (cek-call effect-fn nil))) (scope-pop! "sx-reactive") (set! deps (get ctx "deps")) (when (callable? result) (set! cleanup-fn result))))))))
|
||||||
((s :as signal))
|
(run-effect)
|
||||||
|
(let
|
||||||
|
((dispose-fn (fn () (set! disposed true) (when cleanup-fn (cek-call cleanup-fn nil)) (for-each (fn ((dep :as signal)) (signal-remove-sub! dep run-effect)) deps) (set! deps (list)))))
|
||||||
|
(register-in-scope dispose-fn)
|
||||||
|
dispose-fn)))))
|
||||||
|
(define *batch-depth* 0)
|
||||||
|
(define *batch-queue* (list))
|
||||||
|
(define
|
||||||
|
batch
|
||||||
|
:effects (mutation)
|
||||||
|
(fn
|
||||||
|
((thunk :as lambda))
|
||||||
|
(set! *batch-depth* (+ *batch-depth* 1))
|
||||||
|
(cek-call thunk nil)
|
||||||
|
(set! *batch-depth* (- *batch-depth* 1))
|
||||||
|
(when
|
||||||
|
(= *batch-depth* 0)
|
||||||
|
(let
|
||||||
|
((queue *batch-queue*))
|
||||||
|
(set! *batch-queue* (list))
|
||||||
|
(let
|
||||||
|
((seen (list)) (pending (list)))
|
||||||
(for-each
|
(for-each
|
||||||
(fn
|
(fn
|
||||||
((sub :as lambda))
|
((s :as signal))
|
||||||
(when
|
(for-each
|
||||||
(not (contains? seen sub))
|
(fn
|
||||||
(append! seen sub)
|
((sub :as lambda))
|
||||||
(append! pending sub)))
|
(when
|
||||||
(signal-subscribers s)))
|
(not (contains? seen sub))
|
||||||
queue)
|
(append! seen sub)
|
||||||
(for-each (fn ((sub :as lambda)) (sub)) pending))))))
|
(append! pending sub)))
|
||||||
|
(signal-subscribers s)))
|
||||||
(define
|
queue)
|
||||||
notify-subscribers
|
(for-each (fn ((sub :as lambda)) (sub)) pending))))))
|
||||||
:effects (mutation)
|
(define
|
||||||
(fn
|
notify-subscribers
|
||||||
((s :as signal))
|
:effects (mutation)
|
||||||
(if
|
(fn
|
||||||
(> *batch-depth* 0)
|
((s :as signal))
|
||||||
(when (not (contains? *batch-queue* s)) (append! *batch-queue* s))
|
(if
|
||||||
(flush-subscribers s))))
|
(> *batch-depth* 0)
|
||||||
|
(when (not (contains? *batch-queue* s)) (append! *batch-queue* s))
|
||||||
(define
|
(flush-subscribers s))))
|
||||||
flush-subscribers
|
(define
|
||||||
:effects (mutation)
|
flush-subscribers
|
||||||
(fn
|
:effects (mutation)
|
||||||
((s :as dict))
|
(fn
|
||||||
(for-each (fn (sub) (cek-call sub nil)) (signal-subscribers s))))
|
((s :as dict))
|
||||||
|
(for-each (fn (sub) (cek-call sub nil)) (signal-subscribers s))))
|
||||||
(define
|
(define
|
||||||
dispose-computed
|
dispose-computed
|
||||||
:effects (mutation)
|
:effects (mutation)
|
||||||
(fn
|
(fn
|
||||||
((s :as signal))
|
((s :as signal))
|
||||||
(when
|
(when
|
||||||
(signal? s)
|
(signal? s)
|
||||||
(for-each
|
(for-each
|
||||||
(fn ((dep :as signal)) (signal-remove-sub! dep nil))
|
(fn ((dep :as signal)) (signal-remove-sub! dep nil))
|
||||||
(signal-deps s))
|
(signal-deps s))
|
||||||
(signal-set-deps! s (list)))))
|
(signal-set-deps! s (list)))))
|
||||||
|
(define
|
||||||
(define
|
with-island-scope
|
||||||
with-island-scope
|
:effects (mutation)
|
||||||
:effects (mutation)
|
(fn
|
||||||
(fn
|
((scope-fn :as lambda) (body-fn :as lambda))
|
||||||
((scope-fn :as lambda) (body-fn :as lambda))
|
(scope-push! "sx-island-scope" scope-fn)
|
||||||
(scope-push! "sx-island-scope" scope-fn)
|
(let ((result (body-fn))) (scope-pop! "sx-island-scope") result)))
|
||||||
(let ((result (body-fn))) (scope-pop! "sx-island-scope") result)))
|
(define
|
||||||
|
register-in-scope
|
||||||
(define
|
:effects (mutation)
|
||||||
register-in-scope
|
(fn
|
||||||
:effects (mutation)
|
((disposable :as lambda))
|
||||||
(fn
|
(let
|
||||||
((disposable :as lambda))
|
((collector (scope-peek "sx-island-scope")))
|
||||||
(let
|
(when collector (cek-call collector (list disposable)))))))) ;; end define-library
|
||||||
((collector (scope-peek "sx-island-scope")))
|
|
||||||
(when collector (cek-call collector (list disposable))))))
|
|
||||||
|
|
||||||
|
|
||||||
)) ;; end define-library
|
|
||||||
|
|
||||||
;; Re-export to global namespace for backward compatibility
|
;; Re-export to global namespace for backward compatibility
|
||||||
(import (sx signals))
|
(import (sx signals))
|
||||||
|
|||||||
@@ -9,71 +9,89 @@
|
|||||||
;; Actor
|
;; Actor
|
||||||
;; --------------------------------------------------------------------------
|
;; --------------------------------------------------------------------------
|
||||||
|
|
||||||
(defhandler pub-actor
|
(defhandler
|
||||||
|
pub-actor
|
||||||
:path "/pub/actor"
|
:path "/pub/actor"
|
||||||
:method :get
|
:method :get
|
||||||
:returns "element"
|
:returns "element"
|
||||||
(&key)
|
(&key)
|
||||||
(let ((actor (helper "pub-actor-data")))
|
(let-match
|
||||||
|
{:domain domain :summary summary :display-name display-name :public-key-pem public-key-pem}
|
||||||
|
(helper "pub-actor-data")
|
||||||
(do
|
(do
|
||||||
(set-response-header "Content-Type" "text/sx; charset=utf-8")
|
(set-response-header "Content-Type" "text/sx; charset=utf-8")
|
||||||
(str
|
(str
|
||||||
"(SxActor"
|
"(SxActor"
|
||||||
"\n :id \"https://" (get actor "domain") "/pub/actor\""
|
"\n :id \"https://"
|
||||||
|
domain
|
||||||
|
"/pub/actor\""
|
||||||
"\n :type \"SxPublisher\""
|
"\n :type \"SxPublisher\""
|
||||||
"\n :name \"" (get actor "display-name") "\""
|
"\n :name \""
|
||||||
"\n :summary \"" (get actor "summary") "\""
|
display-name
|
||||||
|
"\""
|
||||||
|
"\n :summary \""
|
||||||
|
summary
|
||||||
|
"\""
|
||||||
"\n :inbox \"/pub/inbox\""
|
"\n :inbox \"/pub/inbox\""
|
||||||
"\n :outbox \"/pub/outbox\""
|
"\n :outbox \"/pub/outbox\""
|
||||||
"\n :followers \"/pub/followers\""
|
"\n :followers \"/pub/followers\""
|
||||||
"\n :following \"/pub/following\""
|
"\n :following \"/pub/following\""
|
||||||
"\n :public-key-pem \"" (get actor "public-key-pem") "\")"))))
|
"\n :public-key-pem \""
|
||||||
|
public-key-pem
|
||||||
|
"\")"))))
|
||||||
|
|
||||||
|
|
||||||
;; --------------------------------------------------------------------------
|
;; --------------------------------------------------------------------------
|
||||||
;; Webfinger
|
;; Webfinger
|
||||||
;; --------------------------------------------------------------------------
|
;; --------------------------------------------------------------------------
|
||||||
|
|
||||||
(defhandler pub-webfinger
|
(defhandler
|
||||||
|
pub-webfinger
|
||||||
:path "/pub/webfinger"
|
:path "/pub/webfinger"
|
||||||
:method :get
|
:method :get
|
||||||
:returns "element"
|
:returns "element"
|
||||||
(&key)
|
(&key)
|
||||||
(let ((resource (helper "request-arg" "resource" ""))
|
(let
|
||||||
(actor (helper "pub-actor-data")))
|
((resource (helper "request-arg" "resource" "")))
|
||||||
(let ((expected (str "acct:" (get actor "preferred-username") "@" (get actor "domain"))))
|
(let-match
|
||||||
(if (!= resource expected)
|
{:domain domain :preferred-username preferred-username}
|
||||||
(do
|
(helper "pub-actor-data")
|
||||||
(set-response-status 404)
|
(let
|
||||||
(str "(Error :message \"Resource not found\")"))
|
((expected (str "acct:" preferred-username "@" domain)))
|
||||||
(do
|
(if
|
||||||
(set-response-header "Content-Type" "text/sx; charset=utf-8")
|
(!= resource expected)
|
||||||
(str
|
(do
|
||||||
"(SxWebfinger"
|
(set-response-status 404)
|
||||||
"\n :subject \"" expected "\""
|
(str "(Error :message \"Resource not found\")"))
|
||||||
"\n :actor \"https://" (get actor "domain") "/pub/actor\""
|
(do
|
||||||
"\n :type \"SxPublisher\")"))))))
|
(set-response-header "Content-Type" "text/sx; charset=utf-8")
|
||||||
|
(str
|
||||||
|
"(SxWebfinger"
|
||||||
|
"\n :subject \""
|
||||||
|
expected
|
||||||
|
"\""
|
||||||
|
"\n :actor \"https://"
|
||||||
|
domain
|
||||||
|
"/pub/actor\""
|
||||||
|
"\n :type \"SxPublisher\")")))))))
|
||||||
|
|
||||||
|
|
||||||
;; --------------------------------------------------------------------------
|
;; --------------------------------------------------------------------------
|
||||||
;; Collections
|
;; Collections
|
||||||
;; --------------------------------------------------------------------------
|
;; --------------------------------------------------------------------------
|
||||||
|
|
||||||
(defhandler pub-collections
|
(defhandler
|
||||||
|
pub-collections
|
||||||
:path "/pub/collections"
|
:path "/pub/collections"
|
||||||
:method :get
|
:method :get
|
||||||
:returns "element"
|
:returns "element"
|
||||||
(&key)
|
(&key)
|
||||||
(let ((collections (helper "pub-collections-data")))
|
(let
|
||||||
|
((collections (helper "pub-collections-data")))
|
||||||
(do
|
(do
|
||||||
(set-response-header "Content-Type" "text/sx; charset=utf-8")
|
(set-response-header "Content-Type" "text/sx; charset=utf-8")
|
||||||
(let ((items (map (fn (c)
|
(let
|
||||||
(str "\n (SxCollection"
|
((items (map (fn (c) (let-match {:description description :slug slug :name name} c (str "\n (SxCollection" " :slug \"" slug "\"" " :name \"" name "\"" " :description \"" description "\"" " :href \"/pub/" slug "\")"))) collections)))
|
||||||
" :slug \"" (get c "slug") "\""
|
|
||||||
" :name \"" (get c "name") "\""
|
|
||||||
" :description \"" (get c "description") "\""
|
|
||||||
" :href \"/pub/" (get c "slug") "\")"))
|
|
||||||
collections)))
|
|
||||||
(str "(SxCollections" (join "" items) ")")))))
|
(str "(SxCollections" (join "" items) ")")))))
|
||||||
|
|
||||||
|
|
||||||
@@ -81,21 +99,33 @@
|
|||||||
;; Status
|
;; Status
|
||||||
;; --------------------------------------------------------------------------
|
;; --------------------------------------------------------------------------
|
||||||
|
|
||||||
(defhandler pub-status
|
(defhandler
|
||||||
|
pub-status
|
||||||
:path "/pub/status"
|
:path "/pub/status"
|
||||||
:method :get
|
:method :get
|
||||||
:returns "element"
|
:returns "element"
|
||||||
(&key)
|
(&key)
|
||||||
(let ((status (helper "pub-status-data")))
|
(let-match
|
||||||
|
{:db db :domain domain :healthy healthy :ipfs ipfs :actor actor}
|
||||||
|
(helper "pub-status-data")
|
||||||
(do
|
(do
|
||||||
(set-response-header "Content-Type" "text/sx; charset=utf-8")
|
(set-response-header "Content-Type" "text/sx; charset=utf-8")
|
||||||
(str
|
(str
|
||||||
"(SxPubStatus"
|
"(SxPubStatus"
|
||||||
"\n :healthy " (get status "healthy")
|
"\n :healthy "
|
||||||
"\n :db \"" (get status "db") "\""
|
healthy
|
||||||
"\n :ipfs \"" (get status "ipfs") "\""
|
"\n :db \""
|
||||||
"\n :actor \"" (get status "actor") "\""
|
db
|
||||||
"\n :domain \"" (or (get status "domain") "unknown") "\")"))))
|
"\""
|
||||||
|
"\n :ipfs \""
|
||||||
|
ipfs
|
||||||
|
"\""
|
||||||
|
"\n :actor \""
|
||||||
|
actor
|
||||||
|
"\""
|
||||||
|
"\n :domain \""
|
||||||
|
(or domain "unknown")
|
||||||
|
"\")"))))
|
||||||
|
|
||||||
|
|
||||||
;; ==========================================================================
|
;; ==========================================================================
|
||||||
@@ -107,72 +137,100 @@
|
|||||||
;; Publish
|
;; Publish
|
||||||
;; --------------------------------------------------------------------------
|
;; --------------------------------------------------------------------------
|
||||||
|
|
||||||
(defhandler pub-publish
|
(defhandler
|
||||||
|
pub-publish
|
||||||
:path "/pub/publish"
|
:path "/pub/publish"
|
||||||
:method :post
|
:method :post
|
||||||
:csrf false
|
:csrf false
|
||||||
:returns "element"
|
:returns "element"
|
||||||
(&key)
|
(&key)
|
||||||
(let ((collection (helper "request-form" "collection" ""))
|
(let
|
||||||
(slug (helper "request-form" "slug" ""))
|
((collection (helper "request-form" "collection" ""))
|
||||||
(content (helper "request-form" "content" ""))
|
(slug (helper "request-form" "slug" ""))
|
||||||
(title (helper "request-form" "title" ""))
|
(content (helper "request-form" "content" ""))
|
||||||
(summary (helper "request-form" "summary" "")))
|
(title (helper "request-form" "title" ""))
|
||||||
(if (or (= collection "") (= slug "") (= content ""))
|
(summary (helper "request-form" "summary" "")))
|
||||||
|
(if
|
||||||
|
(or (= collection "") (= slug "") (= content ""))
|
||||||
(do
|
(do
|
||||||
(set-response-status 400)
|
(set-response-status 400)
|
||||||
(set-response-header "Content-Type" "text/sx; charset=utf-8")
|
(set-response-header "Content-Type" "text/sx; charset=utf-8")
|
||||||
"(Error :message \"Missing collection, slug, or content\")")
|
"(Error :message \"Missing collection, slug, or content\")")
|
||||||
(let ((result (helper "pub-publish" collection slug content title summary)))
|
(let
|
||||||
(if (get result "error")
|
((result (helper "pub-publish" collection slug content title summary)))
|
||||||
|
(if
|
||||||
|
(get result "error")
|
||||||
(do
|
(do
|
||||||
(set-response-status 500)
|
(set-response-status 500)
|
||||||
(set-response-header "Content-Type" "text/sx; charset=utf-8")
|
(set-response-header "Content-Type" "text/sx; charset=utf-8")
|
||||||
(str "(Error :message \"" (get result "error") "\")"))
|
(str "(Error :message \"" (get result "error") "\")"))
|
||||||
(do
|
(let-match
|
||||||
(set-response-header "Content-Type" "text/sx; charset=utf-8")
|
{:cid cid :hash hash :size size :title title :path path :slug slug :collection collection}
|
||||||
(str
|
result
|
||||||
"(Published"
|
(do
|
||||||
"\n :path \"" (get result "path") "\""
|
(set-response-header "Content-Type" "text/sx; charset=utf-8")
|
||||||
"\n :cid \"" (get result "cid") "\""
|
(str
|
||||||
"\n :hash \"" (get result "hash") "\""
|
"(Published"
|
||||||
"\n :size " (get result "size")
|
"\n :path \""
|
||||||
"\n :collection \"" (get result "collection") "\""
|
path
|
||||||
"\n :slug \"" (get result "slug") "\""
|
"\""
|
||||||
"\n :title \"" (get result "title") "\")")))))))
|
"\n :cid \""
|
||||||
|
cid
|
||||||
|
"\""
|
||||||
|
"\n :hash \""
|
||||||
|
hash
|
||||||
|
"\""
|
||||||
|
"\n :size "
|
||||||
|
size
|
||||||
|
"\n :collection \""
|
||||||
|
collection
|
||||||
|
"\""
|
||||||
|
"\n :slug \""
|
||||||
|
slug
|
||||||
|
"\""
|
||||||
|
"\n :title \""
|
||||||
|
title
|
||||||
|
"\")"))))))))
|
||||||
|
|
||||||
|
|
||||||
;; --------------------------------------------------------------------------
|
;; --------------------------------------------------------------------------
|
||||||
;; Browse collection
|
;; Browse collection
|
||||||
;; --------------------------------------------------------------------------
|
;; --------------------------------------------------------------------------
|
||||||
|
|
||||||
(defhandler pub-browse-collection
|
(defhandler
|
||||||
|
pub-browse-collection
|
||||||
:path "/pub/browse/<collection_slug>"
|
:path "/pub/browse/<collection_slug>"
|
||||||
:method :get
|
:method :get
|
||||||
:returns "element"
|
:returns "element"
|
||||||
(&key collection_slug)
|
(&key collection_slug)
|
||||||
(let ((data (helper "pub-collection-items" collection_slug)))
|
(let
|
||||||
(if (get data "error")
|
((data (helper "pub-collection-items" collection_slug)))
|
||||||
|
(if
|
||||||
|
(get data "error")
|
||||||
(do
|
(do
|
||||||
(set-response-status 404)
|
(set-response-status 404)
|
||||||
(set-response-header "Content-Type" "text/sx; charset=utf-8")
|
(set-response-header "Content-Type" "text/sx; charset=utf-8")
|
||||||
(str "(Error :message \"" (get data "error") "\")"))
|
(str "(Error :message \"" (get data "error") "\")"))
|
||||||
(do
|
(let-match
|
||||||
(set-response-header "Content-Type" "text/sx; charset=utf-8")
|
{:description description :items items-data :collection collection :name name}
|
||||||
(let ((items (map (fn (d)
|
data
|
||||||
(str "\n (SxDocument"
|
(do
|
||||||
" :slug \"" (get d "slug") "\""
|
(set-response-header "Content-Type" "text/sx; charset=utf-8")
|
||||||
" :title \"" (get d "title") "\""
|
(let
|
||||||
" :summary \"" (get d "summary") "\""
|
((items (map (fn (d) (let-match {:cid cid :size size :summary summary :title title :slug slug} d (str "\n (SxDocument" " :slug \"" slug "\"" " :title \"" title "\"" " :summary \"" summary "\"" " :cid \"" cid "\"" " :size " size ")"))) items-data)))
|
||||||
" :cid \"" (get d "cid") "\""
|
(str
|
||||||
" :size " (get d "size") ")"))
|
"(SxCollection"
|
||||||
(get data "items"))))
|
"\n :slug \""
|
||||||
(str
|
collection
|
||||||
"(SxCollection"
|
"\""
|
||||||
"\n :slug \"" (get data "collection") "\""
|
"\n :name \""
|
||||||
"\n :name \"" (get data "name") "\""
|
name
|
||||||
"\n :description \"" (get data "description") "\""
|
"\""
|
||||||
(join "" items) ")"))))))
|
"\n :description \""
|
||||||
|
description
|
||||||
|
"\""
|
||||||
|
(join "" items)
|
||||||
|
")")))))))
|
||||||
|
|
||||||
|
|
||||||
;; --------------------------------------------------------------------------
|
;; --------------------------------------------------------------------------
|
||||||
@@ -226,26 +284,30 @@
|
|||||||
;; Outbox
|
;; Outbox
|
||||||
;; --------------------------------------------------------------------------
|
;; --------------------------------------------------------------------------
|
||||||
|
|
||||||
(defhandler pub-outbox
|
(defhandler
|
||||||
|
pub-outbox
|
||||||
:path "/pub/outbox"
|
:path "/pub/outbox"
|
||||||
:method :get
|
:method :get
|
||||||
:returns "element"
|
:returns "element"
|
||||||
(&key)
|
(&key)
|
||||||
(let ((page (helper "request-arg" "page" ""))
|
(let
|
||||||
(data (helper "pub-outbox-data" page)))
|
((page (helper "request-arg" "page" ""))
|
||||||
|
(data (helper "pub-outbox-data" page)))
|
||||||
(do
|
(do
|
||||||
(set-response-header "Content-Type" "text/sx; charset=utf-8")
|
(set-response-header "Content-Type" "text/sx; charset=utf-8")
|
||||||
(let ((items (map (fn (a)
|
(let-match
|
||||||
(str "\n (" (get a "type")
|
{:total total :page page :items items-data}
|
||||||
" :object-type \"" (get a "object-type") "\""
|
data
|
||||||
" :published \"" (get a "published") "\""
|
(let
|
||||||
" :cid \"" (get a "cid") "\")"))
|
((items (map (fn (a) (let-match {:cid cid :type type :object-type object-type :published published} a (str "\n (" type " :object-type \"" object-type "\"" " :published \"" published "\"" " :cid \"" cid "\")"))) items-data)))
|
||||||
(get data "items"))))
|
(str
|
||||||
(str
|
"(SxOutbox"
|
||||||
"(SxOutbox"
|
"\n :total "
|
||||||
"\n :total " (get data "total")
|
total
|
||||||
"\n :page " (get data "page")
|
"\n :page "
|
||||||
(join "" items) ")")))))
|
page
|
||||||
|
(join "" items)
|
||||||
|
")"))))))
|
||||||
|
|
||||||
|
|
||||||
;; --------------------------------------------------------------------------
|
;; --------------------------------------------------------------------------
|
||||||
@@ -275,48 +337,59 @@
|
|||||||
;; Follow a remote server
|
;; Follow a remote server
|
||||||
;; --------------------------------------------------------------------------
|
;; --------------------------------------------------------------------------
|
||||||
|
|
||||||
(defhandler pub-follow
|
(defhandler
|
||||||
|
pub-follow
|
||||||
:path "/pub/follow"
|
:path "/pub/follow"
|
||||||
:method :post
|
:method :post
|
||||||
:csrf false
|
:csrf false
|
||||||
:returns "element"
|
:returns "element"
|
||||||
(&key)
|
(&key)
|
||||||
(let ((actor-url (helper "request-form" "actor_url" "")))
|
(let
|
||||||
(if (= actor-url "")
|
((actor-url (helper "request-form" "actor_url" "")))
|
||||||
|
(if
|
||||||
|
(= actor-url "")
|
||||||
(do
|
(do
|
||||||
(set-response-status 400)
|
(set-response-status 400)
|
||||||
(set-response-header "Content-Type" "text/sx; charset=utf-8")
|
(set-response-header "Content-Type" "text/sx; charset=utf-8")
|
||||||
"(Error :message \"Missing actor_url\")")
|
"(Error :message \"Missing actor_url\")")
|
||||||
(let ((result (helper "pub-follow-remote" actor-url)))
|
(let
|
||||||
|
((result (helper "pub-follow-remote" actor-url)))
|
||||||
(do
|
(do
|
||||||
(set-response-header "Content-Type" "text/sx; charset=utf-8")
|
(set-response-header "Content-Type" "text/sx; charset=utf-8")
|
||||||
(if (get result "error")
|
(if
|
||||||
|
(get result "error")
|
||||||
(do
|
(do
|
||||||
(set-response-status 502)
|
(set-response-status 502)
|
||||||
(str "(Error :message \"" (get result "error") "\")"))
|
(str "(Error :message \"" (get result "error") "\")"))
|
||||||
(str
|
(let-match
|
||||||
"(FollowSent"
|
{:status status :actor-url actor-url}
|
||||||
"\n :actor-url \"" (get result "actor-url") "\""
|
result
|
||||||
"\n :status \"" (get result "status") "\")")))))))
|
(str
|
||||||
|
"(FollowSent"
|
||||||
|
"\n :actor-url \""
|
||||||
|
actor-url
|
||||||
|
"\""
|
||||||
|
"\n :status \""
|
||||||
|
status
|
||||||
|
"\")"))))))))
|
||||||
|
|
||||||
|
|
||||||
;; --------------------------------------------------------------------------
|
;; --------------------------------------------------------------------------
|
||||||
;; Followers
|
;; Followers
|
||||||
;; --------------------------------------------------------------------------
|
;; --------------------------------------------------------------------------
|
||||||
|
|
||||||
(defhandler pub-followers
|
(defhandler
|
||||||
|
pub-followers
|
||||||
:path "/pub/followers"
|
:path "/pub/followers"
|
||||||
:method :get
|
:method :get
|
||||||
:returns "element"
|
:returns "element"
|
||||||
(&key)
|
(&key)
|
||||||
(let ((data (helper "pub-followers-data")))
|
(let
|
||||||
|
((data (helper "pub-followers-data")))
|
||||||
(do
|
(do
|
||||||
(set-response-header "Content-Type" "text/sx; charset=utf-8")
|
(set-response-header "Content-Type" "text/sx; charset=utf-8")
|
||||||
(let ((items (map (fn (f)
|
(let
|
||||||
(str "\n (SxFollower"
|
((items (map (fn (f) (let-match {:actor-url actor-url :acct acct} f (str "\n (SxFollower" " :acct \"" acct "\"" " :actor-url \"" actor-url "\")"))) data)))
|
||||||
" :acct \"" (get f "acct") "\""
|
|
||||||
" :actor-url \"" (get f "actor-url") "\")"))
|
|
||||||
data)))
|
|
||||||
(str "(SxFollowers" (join "" items) ")")))))
|
(str "(SxFollowers" (join "" items) ")")))))
|
||||||
|
|
||||||
|
|
||||||
@@ -348,48 +421,80 @@
|
|||||||
;; Anchor pending activities
|
;; Anchor pending activities
|
||||||
;; --------------------------------------------------------------------------
|
;; --------------------------------------------------------------------------
|
||||||
|
|
||||||
(defhandler pub-anchor
|
(defhandler
|
||||||
|
pub-anchor
|
||||||
:path "/pub/anchor"
|
:path "/pub/anchor"
|
||||||
:method :post
|
:method :post
|
||||||
:csrf false
|
:csrf false
|
||||||
:returns "element"
|
:returns "element"
|
||||||
(&key)
|
(&key)
|
||||||
(let ((result (helper "pub-anchor-pending")))
|
(let-match
|
||||||
|
{:tree-cid tree-cid :status status :count count :ots-proof-cid ots-proof-cid :merkle-root merkle-root}
|
||||||
|
(helper "pub-anchor-pending")
|
||||||
(do
|
(do
|
||||||
(set-response-header "Content-Type" "text/sx; charset=utf-8")
|
(set-response-header "Content-Type" "text/sx; charset=utf-8")
|
||||||
(if (= (get result "status") "nothing-to-anchor")
|
(if
|
||||||
|
(= status "nothing-to-anchor")
|
||||||
"(Anchor :status \"nothing-to-anchor\" :count 0)"
|
"(Anchor :status \"nothing-to-anchor\" :count 0)"
|
||||||
(str
|
(str
|
||||||
"(Anchor"
|
"(Anchor"
|
||||||
"\n :status \"" (get result "status") "\""
|
"\n :status \""
|
||||||
"\n :count " (get result "count")
|
status
|
||||||
"\n :merkle-root \"" (get result "merkle-root") "\""
|
"\""
|
||||||
"\n :tree-cid \"" (get result "tree-cid") "\""
|
"\n :count "
|
||||||
"\n :ots-proof-cid \"" (get result "ots-proof-cid") "\")")))))
|
count
|
||||||
|
"\n :merkle-root \""
|
||||||
|
merkle-root
|
||||||
|
"\""
|
||||||
|
"\n :tree-cid \""
|
||||||
|
tree-cid
|
||||||
|
"\""
|
||||||
|
"\n :ots-proof-cid \""
|
||||||
|
ots-proof-cid
|
||||||
|
"\")")))))
|
||||||
|
|
||||||
|
|
||||||
;; --------------------------------------------------------------------------
|
;; --------------------------------------------------------------------------
|
||||||
;; Verify a CID's anchor
|
;; Verify a CID's anchor
|
||||||
;; --------------------------------------------------------------------------
|
;; --------------------------------------------------------------------------
|
||||||
|
|
||||||
(defhandler pub-verify
|
(defhandler
|
||||||
|
pub-verify
|
||||||
:path "/pub/verify/<cid>"
|
:path "/pub/verify/<cid>"
|
||||||
:method :get
|
:method :get
|
||||||
:returns "element"
|
:returns "element"
|
||||||
(&key cid)
|
(&key cid)
|
||||||
(let ((data (helper "pub-verify-anchor" cid)))
|
(let
|
||||||
|
((data (helper "pub-verify-anchor" cid)))
|
||||||
(do
|
(do
|
||||||
(set-response-header "Content-Type" "text/sx; charset=utf-8")
|
(set-response-header "Content-Type" "text/sx; charset=utf-8")
|
||||||
(if (get data "error")
|
(if
|
||||||
|
(get data "error")
|
||||||
(do
|
(do
|
||||||
(set-response-status 404)
|
(set-response-status 404)
|
||||||
(str "(Error :message \"" (get data "error") "\")"))
|
(str "(Error :message \"" (get data "error") "\")"))
|
||||||
(str
|
(let-match
|
||||||
"(AnchorVerification"
|
{:cid cid* :tree-cid tree-cid :status status :verified verified :ots-proof-cid ots-proof-cid :merkle-root merkle-root :published published}
|
||||||
"\n :cid \"" (get data "cid") "\""
|
data
|
||||||
"\n :status \"" (get data "status") "\""
|
(str
|
||||||
"\n :verified " (get data "verified")
|
"(AnchorVerification"
|
||||||
"\n :merkle-root \"" (get data "merkle-root") "\""
|
"\n :cid \""
|
||||||
"\n :tree-cid \"" (get data "tree-cid") "\""
|
cid*
|
||||||
"\n :ots-proof-cid \"" (get data "ots-proof-cid") "\""
|
"\""
|
||||||
"\n :published \"" (get data "published") "\")")))))
|
"\n :status \""
|
||||||
|
status
|
||||||
|
"\""
|
||||||
|
"\n :verified "
|
||||||
|
verified
|
||||||
|
"\n :merkle-root \""
|
||||||
|
merkle-root
|
||||||
|
"\""
|
||||||
|
"\n :tree-cid \""
|
||||||
|
tree-cid
|
||||||
|
"\""
|
||||||
|
"\n :ots-proof-cid \""
|
||||||
|
ots-proof-cid
|
||||||
|
"\""
|
||||||
|
"\n :published \""
|
||||||
|
published
|
||||||
|
"\")"))))))
|
||||||
|
|||||||
@@ -2,25 +2,27 @@
|
|||||||
~specs-explorer/spec-explorer-content
|
~specs-explorer/spec-explorer-content
|
||||||
(&key data)
|
(&key data)
|
||||||
:affinity :server
|
:affinity :server
|
||||||
(~docs/page
|
(let-match
|
||||||
:title (str (get data "title") " — Explorer")
|
{:stats stats :desc desc :title title :filename filename :platform-interface platform-interface :sections sections}
|
||||||
(~specs-explorer/spec-explorer-header
|
data
|
||||||
:filename (get data "filename")
|
(~docs/page
|
||||||
:title (get data "title")
|
:title (str title " — Explorer")
|
||||||
:desc (get data "desc")
|
(~specs-explorer/spec-explorer-header
|
||||||
:slug (replace (get data "filename") ".sx" ""))
|
:filename filename
|
||||||
(~specs-explorer/spec-explorer-stats :stats (get data "stats"))
|
:title title
|
||||||
(map
|
:desc desc
|
||||||
(fn
|
:slug (replace filename ".sx" ""))
|
||||||
(section)
|
(~specs-explorer/spec-explorer-stats :stats stats)
|
||||||
(~specs-explorer/spec-explorer-section
|
(map
|
||||||
:section section
|
(fn
|
||||||
:filename (get data "filename")))
|
(section)
|
||||||
(get data "sections"))
|
(~specs-explorer/spec-explorer-section
|
||||||
(when
|
:section section
|
||||||
(not (empty? (get data "platform-interface")))
|
:filename filename))
|
||||||
(~specs-explorer/spec-platform-interface
|
sections)
|
||||||
:items (get data "platform-interface")))))
|
(when
|
||||||
|
(not (empty? platform-interface))
|
||||||
|
(~specs-explorer/spec-platform-interface :items platform-interface)))))
|
||||||
|
|
||||||
(defcomp
|
(defcomp
|
||||||
~specs-explorer/spec-explorer-header
|
~specs-explorer/spec-explorer-header
|
||||||
@@ -46,80 +48,91 @@
|
|||||||
(defcomp
|
(defcomp
|
||||||
~specs-explorer/spec-explorer-stats
|
~specs-explorer/spec-explorer-stats
|
||||||
(&key stats)
|
(&key stats)
|
||||||
(div
|
(let-match
|
||||||
(~tw :tokens "flex flex-wrap gap-2 mb-6 text-xs")
|
{:lines lines :io-count io-count :render-count render-count :pure-count pure-count :mutation-count mutation-count :test-total test-total :total-defines total-defines}
|
||||||
(span
|
stats
|
||||||
(~tw :tokens "bg-stone-100 text-stone-600 px-2 py-0.5 rounded font-medium")
|
(div
|
||||||
(str (get stats "total-defines") " defines"))
|
(~tw :tokens "flex flex-wrap gap-2 mb-6 text-xs")
|
||||||
(when
|
|
||||||
(> (get stats "pure-count") 0)
|
|
||||||
(span
|
(span
|
||||||
(~tw :tokens "bg-green-100 text-green-700 px-2 py-0.5 rounded")
|
(~tw
|
||||||
(str (get stats "pure-count") " pure")))
|
:tokens "bg-stone-100 text-stone-600 px-2 py-0.5 rounded font-medium")
|
||||||
(when
|
(str total-defines " defines"))
|
||||||
(> (get stats "mutation-count") 0)
|
(when
|
||||||
|
(> pure-count 0)
|
||||||
|
(span
|
||||||
|
(~tw :tokens "bg-green-100 text-green-700 px-2 py-0.5 rounded")
|
||||||
|
(str pure-count " pure")))
|
||||||
|
(when
|
||||||
|
(> mutation-count 0)
|
||||||
|
(span
|
||||||
|
(~tw :tokens "bg-amber-100 text-amber-700 px-2 py-0.5 rounded")
|
||||||
|
(str mutation-count " mutation")))
|
||||||
|
(when
|
||||||
|
(> io-count 0)
|
||||||
|
(span
|
||||||
|
(~tw :tokens "bg-orange-100 text-orange-700 px-2 py-0.5 rounded")
|
||||||
|
(str io-count " io")))
|
||||||
|
(when
|
||||||
|
(> render-count 0)
|
||||||
|
(span
|
||||||
|
(~tw :tokens "bg-sky-100 text-sky-700 px-2 py-0.5 rounded")
|
||||||
|
(str render-count " render")))
|
||||||
|
(when
|
||||||
|
(> test-total 0)
|
||||||
|
(span
|
||||||
|
(~tw :tokens "bg-violet-100 text-violet-700 px-2 py-0.5 rounded")
|
||||||
|
(str test-total " tests")))
|
||||||
(span
|
(span
|
||||||
(~tw :tokens "bg-amber-100 text-amber-700 px-2 py-0.5 rounded")
|
(~tw :tokens "bg-stone-100 text-stone-500 px-2 py-0.5 rounded")
|
||||||
(str (get stats "mutation-count") " mutation")))
|
(str lines " lines")))))
|
||||||
(when
|
|
||||||
(> (get stats "io-count") 0)
|
|
||||||
(span
|
|
||||||
(~tw :tokens "bg-orange-100 text-orange-700 px-2 py-0.5 rounded")
|
|
||||||
(str (get stats "io-count") " io")))
|
|
||||||
(when
|
|
||||||
(> (get stats "render-count") 0)
|
|
||||||
(span
|
|
||||||
(~tw :tokens "bg-sky-100 text-sky-700 px-2 py-0.5 rounded")
|
|
||||||
(str (get stats "render-count") " render")))
|
|
||||||
(when
|
|
||||||
(> (get stats "test-total") 0)
|
|
||||||
(span
|
|
||||||
(~tw :tokens "bg-violet-100 text-violet-700 px-2 py-0.5 rounded")
|
|
||||||
(str (get stats "test-total") " tests")))
|
|
||||||
(span
|
|
||||||
(~tw :tokens "bg-stone-100 text-stone-500 px-2 py-0.5 rounded")
|
|
||||||
(str (get stats "lines") " lines"))))
|
|
||||||
|
|
||||||
(defcomp
|
(defcomp
|
||||||
~specs-explorer/spec-explorer-section
|
~specs-explorer/spec-explorer-section
|
||||||
(&key section filename)
|
(&key section filename)
|
||||||
(div
|
(let-match
|
||||||
(~tw :tokens "mb-6")
|
{:defines defines :title title :comment comment}
|
||||||
(h2
|
section
|
||||||
(~tw :tokens "text-base font-semibold text-stone-600 mb-2 border-b border-stone-200 pb-1")
|
|
||||||
:id (replace (lower (get section "title")) " " "-")
|
|
||||||
(get section "title"))
|
|
||||||
(when
|
|
||||||
(get section "comment")
|
|
||||||
(p (~tw :tokens "text-sm text-stone-500 mb-2") (get section "comment")))
|
|
||||||
(div
|
(div
|
||||||
(~tw :tokens "space-y-0.5")
|
(~tw :tokens "mb-6")
|
||||||
(map
|
(h2
|
||||||
(fn
|
(~tw
|
||||||
(d)
|
:tokens "text-base font-semibold text-stone-600 mb-2 border-b border-stone-200 pb-1")
|
||||||
(~specs-explorer/spec-explorer-define :d d :filename filename))
|
:id (replace (lower title) " " "-")
|
||||||
(get section "defines")))))
|
title)
|
||||||
|
(when comment (p (~tw :tokens "text-sm text-stone-500 mb-2") comment))
|
||||||
|
(div
|
||||||
|
(~tw :tokens "space-y-0.5")
|
||||||
|
(map
|
||||||
|
(fn
|
||||||
|
(d)
|
||||||
|
(~specs-explorer/spec-explorer-define :d d :filename filename))
|
||||||
|
defines)))))
|
||||||
|
|
||||||
(defcomp
|
(defcomp
|
||||||
~specs-explorer/spec-explorer-define
|
~specs-explorer/spec-explorer-define
|
||||||
(&key d filename)
|
(&key d filename)
|
||||||
(div
|
(let-match
|
||||||
(~tw :tokens "flex items-center gap-2 py-1.5 px-2 rounded hover:bg-stone-50 cursor-pointer group")
|
{:kind kind :name name}
|
||||||
:id (str "fn-" (get d "name"))
|
d
|
||||||
:sx-get (str
|
(div
|
||||||
"/sx/(language.(spec.(explore."
|
(~tw
|
||||||
(replace filename ".sx" "")
|
:tokens "flex items-center gap-2 py-1.5 px-2 rounded hover:bg-stone-50 cursor-pointer group")
|
||||||
"."
|
:id (str "fn-" name)
|
||||||
(get d "name")
|
:sx-get (str
|
||||||
")))")
|
"/sx/(language.(spec.(explore."
|
||||||
:sx-target "#sx-content"
|
(replace filename ".sx" "")
|
||||||
:sx-select "#sx-content"
|
"."
|
||||||
:sx-swap "innerHTML"
|
name
|
||||||
:sx-push-url "true"
|
")))")
|
||||||
(span
|
:sx-target "#sx-content"
|
||||||
(~tw :tokens "font-mono text-sm font-medium text-stone-800 group-hover:text-violet-700")
|
:sx-select "#sx-content"
|
||||||
(get d "name"))
|
:sx-swap "innerHTML"
|
||||||
(span (~tw :tokens "text-xs text-stone-400") (get d "kind"))))
|
:sx-push-url "true"
|
||||||
|
(span
|
||||||
|
(~tw
|
||||||
|
:tokens "font-mono text-sm font-medium text-stone-800 group-hover:text-violet-700")
|
||||||
|
name)
|
||||||
|
(span (~tw :tokens "text-xs text-stone-400") kind))))
|
||||||
|
|
||||||
(defcomp
|
(defcomp
|
||||||
~specs-explorer/spec-explorer-define-detail
|
~specs-explorer/spec-explorer-define-detail
|
||||||
@@ -144,33 +157,39 @@
|
|||||||
:sx-swap "innerHTML"
|
:sx-swap "innerHTML"
|
||||||
:sx-push-url "true"
|
:sx-push-url "true"
|
||||||
(str "← Back to " (replace filename ".sx" ""))))
|
(str "← Back to " (replace filename ".sx" ""))))
|
||||||
(div
|
(let-match
|
||||||
(~tw :tokens "rounded border border-violet-200 bg-violet-50/30 p-4")
|
{:kind kind :effects effects :params params :source source :name name}
|
||||||
|
d
|
||||||
(div
|
(div
|
||||||
(~tw :tokens "flex items-center gap-2 flex-wrap mb-3")
|
(~tw :tokens "rounded border border-violet-200 bg-violet-50/30 p-4")
|
||||||
(span
|
(div
|
||||||
(~tw :tokens "font-mono text-lg font-semibold text-stone-800")
|
(~tw :tokens "flex items-center gap-2 flex-wrap mb-3")
|
||||||
(get d "name"))
|
|
||||||
(span (~tw :tokens "text-xs text-stone-400") (get d "kind"))
|
|
||||||
(if
|
|
||||||
(empty? (get d "effects"))
|
|
||||||
(span
|
(span
|
||||||
(~tw :tokens "text-xs px-1.5 py-0.5 rounded bg-green-100 text-green-700")
|
(~tw :tokens "font-mono text-lg font-semibold text-stone-800")
|
||||||
"pure")
|
name)
|
||||||
(map
|
(span (~tw :tokens "text-xs text-stone-400") kind)
|
||||||
(fn (eff) (~specs-explorer/spec-effect-badge :effect eff))
|
(if
|
||||||
(get d "effects"))))
|
(empty? effects)
|
||||||
(when
|
(span
|
||||||
(not (empty? (get d "params")))
|
(~tw
|
||||||
(~specs-explorer/spec-param-list :params (get d "params")))
|
:tokens "text-xs px-1.5 py-0.5 rounded bg-green-100 text-green-700")
|
||||||
(details
|
"pure")
|
||||||
:open "true"
|
(map
|
||||||
(summary
|
(fn (eff) (~specs-explorer/spec-effect-badge :effect eff))
|
||||||
(~tw :tokens "px-3 py-1.5 bg-stone-50 text-xs font-medium text-stone-600 cursor-pointer select-none mt-3 rounded")
|
effects)))
|
||||||
"SX Source")
|
(when
|
||||||
(pre
|
(not (empty? params))
|
||||||
(~tw :tokens "text-xs p-3 overflow-x-auto bg-white rounded mt-1 border border-stone-200")
|
(~specs-explorer/spec-param-list :params params))
|
||||||
(code (~tw :tokens "language-sx") (get d "source")))))))
|
(details
|
||||||
|
:open "true"
|
||||||
|
(summary
|
||||||
|
(~tw
|
||||||
|
:tokens "px-3 py-1.5 bg-stone-50 text-xs font-medium text-stone-600 cursor-pointer select-none mt-3 rounded")
|
||||||
|
"SX Source")
|
||||||
|
(pre
|
||||||
|
(~tw
|
||||||
|
:tokens "text-xs p-3 overflow-x-auto bg-white rounded mt-1 border border-stone-200")
|
||||||
|
(code (~tw :tokens "language-sx") source)))))))
|
||||||
|
|
||||||
(defcomp
|
(defcomp
|
||||||
~specs-explorer/spec-effect-badge
|
~specs-explorer/spec-effect-badge
|
||||||
@@ -197,13 +216,15 @@
|
|||||||
(map
|
(map
|
||||||
(fn
|
(fn
|
||||||
(p)
|
(p)
|
||||||
(let
|
(let-match
|
||||||
((name (get p "name")) (typ (get p "type")))
|
{:type typ :name name}
|
||||||
|
p
|
||||||
(if
|
(if
|
||||||
(or (= name "&rest") (= name "&key"))
|
(or (= name "&rest") (= name "&key"))
|
||||||
(span (~tw :tokens "text-xs font-mono text-violet-500") name)
|
(span (~tw :tokens "text-xs font-mono text-violet-500") name)
|
||||||
(span
|
(span
|
||||||
(~tw :tokens "text-xs font-mono px-1 py-0.5 rounded bg-stone-50 border border-stone-200")
|
(~tw
|
||||||
|
:tokens "text-xs font-mono px-1 py-0.5 rounded bg-stone-50 border border-stone-200")
|
||||||
(if
|
(if
|
||||||
typ
|
typ
|
||||||
(<>
|
(<>
|
||||||
@@ -300,7 +321,8 @@
|
|||||||
(div
|
(div
|
||||||
(~tw :tokens "mt-8")
|
(~tw :tokens "mt-8")
|
||||||
(h2
|
(h2
|
||||||
(~tw :tokens "text-lg font-semibold text-stone-700 border-b border-stone-200 pb-1 mb-3")
|
(~tw
|
||||||
|
:tokens "text-lg font-semibold text-stone-700 border-b border-stone-200 pb-1 mb-3")
|
||||||
"Platform Interface")
|
"Platform Interface")
|
||||||
(p
|
(p
|
||||||
(~tw :tokens "text-sm text-stone-500 mb-3")
|
(~tw :tokens "text-sm text-stone-500 mb-3")
|
||||||
@@ -314,22 +336,29 @@
|
|||||||
(~tw :tokens "border-b border-stone-200 bg-stone-50")
|
(~tw :tokens "border-b border-stone-200 bg-stone-50")
|
||||||
(th (~tw :tokens "px-3 py-2 font-medium text-stone-600") "Name")
|
(th (~tw :tokens "px-3 py-2 font-medium text-stone-600") "Name")
|
||||||
(th (~tw :tokens "px-3 py-2 font-medium text-stone-600") "Params")
|
(th (~tw :tokens "px-3 py-2 font-medium text-stone-600") "Params")
|
||||||
(th (~tw :tokens "px-3 py-2 font-medium text-stone-600") "Returns")
|
(th
|
||||||
(th (~tw :tokens "px-3 py-2 font-medium text-stone-600") "Description")))
|
(~tw :tokens "px-3 py-2 font-medium text-stone-600")
|
||||||
|
"Returns")
|
||||||
|
(th
|
||||||
|
(~tw :tokens "px-3 py-2 font-medium text-stone-600")
|
||||||
|
"Description")))
|
||||||
(tbody
|
(tbody
|
||||||
(map
|
(map
|
||||||
(fn
|
(fn
|
||||||
(item)
|
(item)
|
||||||
(tr
|
(let-match
|
||||||
(~tw :tokens "border-b border-stone-100")
|
{:doc doc :params params :returns returns :name name}
|
||||||
(td
|
item
|
||||||
(~tw :tokens "px-3 py-2 font-mono text-sm text-violet-700")
|
(tr
|
||||||
(get item "name"))
|
(~tw :tokens "border-b border-stone-100")
|
||||||
(td
|
(td
|
||||||
(~tw :tokens "px-3 py-2 font-mono text-xs text-stone-500")
|
(~tw :tokens "px-3 py-2 font-mono text-sm text-violet-700")
|
||||||
(get item "params"))
|
name)
|
||||||
(td
|
(td
|
||||||
(~tw :tokens "px-3 py-2 font-mono text-xs text-stone-500")
|
(~tw :tokens "px-3 py-2 font-mono text-xs text-stone-500")
|
||||||
(get item "returns"))
|
params)
|
||||||
(td (~tw :tokens "px-3 py-2 text-stone-600") (get item "doc"))))
|
(td
|
||||||
|
(~tw :tokens "px-3 py-2 font-mono text-xs text-stone-500")
|
||||||
|
returns)
|
||||||
|
(td (~tw :tokens "px-3 py-2 text-stone-600") doc))))
|
||||||
items))))))
|
items))))))
|
||||||
|
|||||||
654
web/deps.sx
654
web/deps.sx
@@ -1,6 +1,7 @@
|
|||||||
|
|
||||||
|
|
||||||
(define-library (web deps)
|
(define-library
|
||||||
|
(web deps)
|
||||||
(export
|
(export
|
||||||
scan-refs
|
scan-refs
|
||||||
scan-refs-walk
|
scan-refs-walk
|
||||||
@@ -22,347 +23,340 @@
|
|||||||
page-render-plan
|
page-render-plan
|
||||||
env-components)
|
env-components)
|
||||||
(begin
|
(begin
|
||||||
|
(define
|
||||||
(define
|
scan-refs
|
||||||
scan-refs
|
:effects ()
|
||||||
:effects ()
|
(fn (node) (let ((refs (list))) (scan-refs-walk node refs) refs)))
|
||||||
(fn (node) (let ((refs (list))) (scan-refs-walk node refs) refs)))
|
(define
|
||||||
|
scan-refs-walk
|
||||||
(define
|
:effects ()
|
||||||
scan-refs-walk
|
|
||||||
:effects ()
|
|
||||||
(fn
|
|
||||||
(node (refs :as list))
|
|
||||||
(cond
|
|
||||||
(= (type-of node) "symbol")
|
|
||||||
(let
|
|
||||||
((name (symbol-name node)))
|
|
||||||
(when
|
|
||||||
(starts-with? name "~")
|
|
||||||
(when (not (contains? refs name)) (append! refs name))))
|
|
||||||
(= (type-of node) "list")
|
|
||||||
(for-each (fn (item) (scan-refs-walk item refs)) node)
|
|
||||||
(= (type-of node) "dict")
|
|
||||||
(for-each
|
|
||||||
(fn (key) (scan-refs-walk (dict-get node key) refs))
|
|
||||||
(keys node))
|
|
||||||
:else nil)))
|
|
||||||
|
|
||||||
(define
|
|
||||||
transitive-deps-walk
|
|
||||||
:effects ()
|
|
||||||
(fn
|
|
||||||
((n :as string) (seen :as list) (env :as dict))
|
|
||||||
(when
|
|
||||||
(not (contains? seen n))
|
|
||||||
(append! seen n)
|
|
||||||
(let
|
|
||||||
((val (env-get env n)))
|
|
||||||
(cond
|
|
||||||
(or (= (type-of val) "component") (= (type-of val) "island"))
|
|
||||||
(for-each
|
|
||||||
(fn ((ref :as string)) (transitive-deps-walk ref seen env))
|
|
||||||
(scan-refs (component-body val)))
|
|
||||||
(= (type-of val) "macro")
|
|
||||||
(for-each
|
|
||||||
(fn ((ref :as string)) (transitive-deps-walk ref seen env))
|
|
||||||
(scan-refs (macro-body val)))
|
|
||||||
:else nil)))))
|
|
||||||
|
|
||||||
(define
|
|
||||||
transitive-deps
|
|
||||||
:effects ()
|
|
||||||
(fn
|
|
||||||
((name :as string) (env :as dict))
|
|
||||||
(let
|
|
||||||
((seen (list))
|
|
||||||
(key (if (starts-with? name "~") name (str "~" name))))
|
|
||||||
(transitive-deps-walk key seen env)
|
|
||||||
(filter (fn ((x :as string)) (not (= x key))) seen))))
|
|
||||||
|
|
||||||
(define
|
|
||||||
compute-all-deps
|
|
||||||
:effects (mutation)
|
|
||||||
(fn
|
|
||||||
((env :as dict))
|
|
||||||
(for-each
|
|
||||||
(fn
|
(fn
|
||||||
((name :as string))
|
(node (refs :as list))
|
||||||
(let
|
(match
|
||||||
((val (env-get env name)))
|
(type-of node)
|
||||||
(when
|
("symbol"
|
||||||
(or (= (type-of val) "component") (= (type-of val) "island"))
|
|
||||||
(component-set-deps! val (transitive-deps name env)))))
|
|
||||||
(env-components env))))
|
|
||||||
|
|
||||||
(define
|
|
||||||
scan-components-from-source
|
|
||||||
:effects ()
|
|
||||||
(fn
|
|
||||||
((source :as string))
|
|
||||||
(let
|
|
||||||
((matches (regex-find-all "\\(~([a-zA-Z_][a-zA-Z0-9_\\-:/]*)" source)))
|
|
||||||
(map (fn ((m :as string)) (str "~" m)) matches))))
|
|
||||||
|
|
||||||
(define
|
|
||||||
components-needed
|
|
||||||
:effects ()
|
|
||||||
(fn
|
|
||||||
((page-source :as string) (env :as dict))
|
|
||||||
(let
|
|
||||||
((direct (scan-components-from-source page-source))
|
|
||||||
(all-needed (list)))
|
|
||||||
(for-each
|
|
||||||
(fn
|
|
||||||
((name :as string))
|
|
||||||
(when (not (contains? all-needed name)) (append! all-needed name))
|
|
||||||
(let
|
|
||||||
((val (env-get env name)))
|
|
||||||
(let
|
(let
|
||||||
((deps (if (and (= (type-of val) "component") (not (empty? (component-deps val)))) (component-deps val) (transitive-deps name env))))
|
((name (symbol-name node)))
|
||||||
(for-each
|
(when
|
||||||
(fn
|
(starts-with? name "~")
|
||||||
((dep :as string))
|
(when (not (contains? refs name)) (append! refs name)))))
|
||||||
(when
|
("list" (for-each (fn (child) (scan-refs-walk child refs)) node))
|
||||||
(not (contains? all-needed dep))
|
("dict"
|
||||||
(append! all-needed dep)))
|
|
||||||
deps))))
|
|
||||||
direct)
|
|
||||||
all-needed)))
|
|
||||||
|
|
||||||
(define
|
|
||||||
page-component-bundle
|
|
||||||
:effects ()
|
|
||||||
(fn
|
|
||||||
((page-source :as string) (env :as dict))
|
|
||||||
(components-needed page-source env)))
|
|
||||||
|
|
||||||
(define
|
|
||||||
page-css-classes
|
|
||||||
:effects ()
|
|
||||||
(fn
|
|
||||||
((page-source :as string) (env :as dict))
|
|
||||||
(let
|
|
||||||
((needed (components-needed page-source env)) (classes (list)))
|
|
||||||
(for-each
|
|
||||||
(fn
|
|
||||||
((name :as string))
|
|
||||||
(let
|
|
||||||
((val (env-get env name)))
|
|
||||||
(when
|
|
||||||
(= (type-of val) "component")
|
|
||||||
(for-each
|
|
||||||
(fn
|
|
||||||
((cls :as string))
|
|
||||||
(when (not (contains? classes cls)) (append! classes cls)))
|
|
||||||
(component-css-classes val)))))
|
|
||||||
needed)
|
|
||||||
(for-each
|
|
||||||
(fn
|
|
||||||
((cls :as string))
|
|
||||||
(when (not (contains? classes cls)) (append! classes cls)))
|
|
||||||
(scan-css-classes page-source))
|
|
||||||
classes)))
|
|
||||||
|
|
||||||
(define
|
|
||||||
scan-io-refs-walk
|
|
||||||
:effects ()
|
|
||||||
(fn
|
|
||||||
(node (io-names :as list) (refs :as list))
|
|
||||||
(cond
|
|
||||||
(= (type-of node) "symbol")
|
|
||||||
(let
|
|
||||||
((name (symbol-name node)))
|
|
||||||
(when
|
|
||||||
(contains? io-names name)
|
|
||||||
(when (not (contains? refs name)) (append! refs name))))
|
|
||||||
(= (type-of node) "list")
|
|
||||||
(for-each (fn (item) (scan-io-refs-walk item io-names refs)) node)
|
|
||||||
(= (type-of node) "dict")
|
|
||||||
(for-each
|
|
||||||
(fn (key) (scan-io-refs-walk (dict-get node key) io-names refs))
|
|
||||||
(keys node))
|
|
||||||
:else nil)))
|
|
||||||
|
|
||||||
(define
|
|
||||||
scan-io-refs
|
|
||||||
:effects ()
|
|
||||||
(fn
|
|
||||||
(node (io-names :as list))
|
|
||||||
(let ((refs (list))) (scan-io-refs-walk node io-names refs) refs)))
|
|
||||||
|
|
||||||
(define
|
|
||||||
transitive-io-refs-walk
|
|
||||||
:effects ()
|
|
||||||
(fn
|
|
||||||
((n :as string)
|
|
||||||
(seen :as list)
|
|
||||||
(all-refs :as list)
|
|
||||||
(env :as dict)
|
|
||||||
(io-names :as list))
|
|
||||||
(when
|
|
||||||
(not (contains? seen n))
|
|
||||||
(append! seen n)
|
|
||||||
(let
|
|
||||||
((val (env-get env n)))
|
|
||||||
(cond
|
|
||||||
(= (type-of val) "component")
|
|
||||||
(do
|
|
||||||
(for-each
|
(for-each
|
||||||
(fn
|
(fn (key) (scan-refs-walk (dict-get node key) refs))
|
||||||
((ref :as string))
|
(keys node)))
|
||||||
(when (not (contains? all-refs ref)) (append! all-refs ref)))
|
(_ nil))))
|
||||||
(scan-io-refs (component-body val) io-names))
|
(define
|
||||||
(for-each
|
transitive-deps-walk
|
||||||
(fn
|
:effects ()
|
||||||
((dep :as string))
|
|
||||||
(transitive-io-refs-walk dep seen all-refs env io-names))
|
|
||||||
(scan-refs (component-body val))))
|
|
||||||
(= (type-of val) "macro")
|
|
||||||
(do
|
|
||||||
(for-each
|
|
||||||
(fn
|
|
||||||
((ref :as string))
|
|
||||||
(when (not (contains? all-refs ref)) (append! all-refs ref)))
|
|
||||||
(scan-io-refs (macro-body val) io-names))
|
|
||||||
(for-each
|
|
||||||
(fn
|
|
||||||
((dep :as string))
|
|
||||||
(transitive-io-refs-walk dep seen all-refs env io-names))
|
|
||||||
(scan-refs (macro-body val))))
|
|
||||||
:else nil)))))
|
|
||||||
|
|
||||||
(define
|
|
||||||
transitive-io-refs
|
|
||||||
:effects ()
|
|
||||||
(fn
|
|
||||||
((name :as string) (env :as dict) (io-names :as list))
|
|
||||||
(let
|
|
||||||
((all-refs (list))
|
|
||||||
(seen (list))
|
|
||||||
(key (if (starts-with? name "~") name (str "~" name))))
|
|
||||||
(transitive-io-refs-walk key seen all-refs env io-names)
|
|
||||||
all-refs)))
|
|
||||||
|
|
||||||
(define
|
|
||||||
compute-all-io-refs
|
|
||||||
:effects (mutation)
|
|
||||||
(fn
|
|
||||||
((env :as dict) (io-names :as list))
|
|
||||||
(for-each
|
|
||||||
(fn
|
(fn
|
||||||
((name :as string))
|
((n :as string) (seen :as list) (env :as dict))
|
||||||
(let
|
(when
|
||||||
((val (env-get env name)))
|
(not (contains? seen n))
|
||||||
(when
|
(append! seen n)
|
||||||
(= (type-of val) "component")
|
|
||||||
(component-set-io-refs!
|
|
||||||
val
|
|
||||||
(transitive-io-refs name env io-names)))))
|
|
||||||
(env-components env))))
|
|
||||||
|
|
||||||
(define
|
|
||||||
component-io-refs-cached
|
|
||||||
:effects ()
|
|
||||||
(fn
|
|
||||||
((name :as string) (env :as dict) (io-names :as list))
|
|
||||||
(let
|
|
||||||
((key (if (starts-with? name "~") name (str "~" name))))
|
|
||||||
(let
|
|
||||||
((val (env-get env key)))
|
|
||||||
(if
|
|
||||||
(and
|
|
||||||
(= (type-of val) "component")
|
|
||||||
(not (nil? (component-io-refs val)))
|
|
||||||
(not (empty? (component-io-refs val))))
|
|
||||||
(component-io-refs val)
|
|
||||||
(transitive-io-refs name env io-names))))))
|
|
||||||
|
|
||||||
(define
|
|
||||||
component-pure?
|
|
||||||
:effects ()
|
|
||||||
(fn
|
|
||||||
(name (env :as dict) (io-names :as list))
|
|
||||||
(let
|
|
||||||
((key (if (starts-with? name "~") name (str "~" name))))
|
|
||||||
(let
|
|
||||||
((val (if (env-has? env key) (env-get env key) nil)))
|
|
||||||
(if
|
|
||||||
(and
|
|
||||||
(= (type-of val) "component")
|
|
||||||
(not (nil? (component-io-refs val)))
|
|
||||||
(not (empty? (component-io-refs val))))
|
|
||||||
false
|
|
||||||
(empty? (transitive-io-refs name env io-names)))))))
|
|
||||||
|
|
||||||
(define
|
|
||||||
render-target
|
|
||||||
:effects ()
|
|
||||||
(fn
|
|
||||||
(name (env :as dict) (io-names :as list))
|
|
||||||
(let
|
|
||||||
((key (if (starts-with? name "~") name (str "~" name))))
|
|
||||||
(let
|
|
||||||
((val (if (env-has? env key) (env-get env key) nil)))
|
|
||||||
(if
|
|
||||||
(not (= (type-of val) "component"))
|
|
||||||
"server"
|
|
||||||
(let
|
(let
|
||||||
((affinity (component-affinity val)))
|
((val (env-get env n)))
|
||||||
(cond
|
(cond
|
||||||
(= affinity "server")
|
(or (= (type-of val) "component") (= (type-of val) "island"))
|
||||||
"server"
|
(for-each
|
||||||
(= affinity "client")
|
(fn ((ref :as string)) (transitive-deps-walk ref seen env))
|
||||||
"client"
|
(scan-refs (component-body val)))
|
||||||
(not (component-pure? name env io-names))
|
(= (type-of val) "macro")
|
||||||
"server"
|
(for-each
|
||||||
:else "client")))))))
|
(fn ((ref :as string)) (transitive-deps-walk ref seen env))
|
||||||
|
(scan-refs (macro-body val)))
|
||||||
(define
|
:else nil)))))
|
||||||
page-render-plan
|
(define
|
||||||
:effects ()
|
transitive-deps
|
||||||
(fn
|
:effects ()
|
||||||
((page-source :as string) (env :as dict) (io-names :as list))
|
(fn
|
||||||
(let
|
((name :as string) (env :as dict))
|
||||||
((needed (components-needed page-source env))
|
(let
|
||||||
(comp-targets (dict))
|
((seen (list))
|
||||||
(server-list (list))
|
(key (if (starts-with? name "~") name (str "~" name))))
|
||||||
(client-list (list))
|
(transitive-deps-walk key seen env)
|
||||||
(io-deps (list)))
|
(filter (fn ((x :as string)) (not (= x key))) seen))))
|
||||||
(for-each
|
(define
|
||||||
(fn
|
compute-all-deps
|
||||||
((name :as string))
|
:effects (mutation)
|
||||||
|
(fn
|
||||||
|
((env :as dict))
|
||||||
|
(for-each
|
||||||
|
(fn
|
||||||
|
((name :as string))
|
||||||
|
(let
|
||||||
|
((val (env-get env name)))
|
||||||
|
(when
|
||||||
|
(or
|
||||||
|
(= (type-of val) "component")
|
||||||
|
(= (type-of val) "island"))
|
||||||
|
(component-set-deps! val (transitive-deps name env)))))
|
||||||
|
(env-components env))))
|
||||||
|
(define
|
||||||
|
scan-components-from-source
|
||||||
|
:effects ()
|
||||||
|
(fn
|
||||||
|
((source :as string))
|
||||||
|
(let
|
||||||
|
((matches (regex-find-all "\\(~([a-zA-Z_][a-zA-Z0-9_\\-:/]*)" source)))
|
||||||
|
(map (fn ((m :as string)) (str "~" m)) matches))))
|
||||||
|
(define
|
||||||
|
components-needed
|
||||||
|
:effects ()
|
||||||
|
(fn
|
||||||
|
((page-source :as string) (env :as dict))
|
||||||
|
(let
|
||||||
|
((direct (scan-components-from-source page-source))
|
||||||
|
(all-needed (list)))
|
||||||
|
(for-each
|
||||||
|
(fn
|
||||||
|
((name :as string))
|
||||||
|
(when
|
||||||
|
(not (contains? all-needed name))
|
||||||
|
(append! all-needed name))
|
||||||
|
(let
|
||||||
|
((val (env-get env name)))
|
||||||
|
(let
|
||||||
|
((deps (if (and (= (type-of val) "component") (not (empty? (component-deps val)))) (component-deps val) (transitive-deps name env))))
|
||||||
|
(for-each
|
||||||
|
(fn
|
||||||
|
((dep :as string))
|
||||||
|
(when
|
||||||
|
(not (contains? all-needed dep))
|
||||||
|
(append! all-needed dep)))
|
||||||
|
deps))))
|
||||||
|
direct)
|
||||||
|
all-needed)))
|
||||||
|
(define
|
||||||
|
page-component-bundle
|
||||||
|
:effects ()
|
||||||
|
(fn
|
||||||
|
((page-source :as string) (env :as dict))
|
||||||
|
(components-needed page-source env)))
|
||||||
|
(define
|
||||||
|
page-css-classes
|
||||||
|
:effects ()
|
||||||
|
(fn
|
||||||
|
((page-source :as string) (env :as dict))
|
||||||
|
(let
|
||||||
|
((needed (components-needed page-source env)) (classes (list)))
|
||||||
|
(for-each
|
||||||
|
(fn
|
||||||
|
((name :as string))
|
||||||
|
(let
|
||||||
|
((val (env-get env name)))
|
||||||
|
(when
|
||||||
|
(= (type-of val) "component")
|
||||||
|
(for-each
|
||||||
|
(fn
|
||||||
|
((cls :as string))
|
||||||
|
(when
|
||||||
|
(not (contains? classes cls))
|
||||||
|
(append! classes cls)))
|
||||||
|
(component-css-classes val)))))
|
||||||
|
needed)
|
||||||
|
(for-each
|
||||||
|
(fn
|
||||||
|
((cls :as string))
|
||||||
|
(when (not (contains? classes cls)) (append! classes cls)))
|
||||||
|
(scan-css-classes page-source))
|
||||||
|
classes)))
|
||||||
|
(define
|
||||||
|
scan-io-refs-walk
|
||||||
|
:effects ()
|
||||||
|
(fn
|
||||||
|
(node (io-names :as list) (refs :as list))
|
||||||
|
(match
|
||||||
|
(type-of node)
|
||||||
|
("symbol"
|
||||||
|
(let
|
||||||
|
((name (symbol-name node)))
|
||||||
|
(when
|
||||||
|
(contains? io-names name)
|
||||||
|
(when (not (contains? refs name)) (append! refs name)))))
|
||||||
|
("list"
|
||||||
|
(for-each
|
||||||
|
(fn (item) (scan-io-refs-walk item io-names refs))
|
||||||
|
node))
|
||||||
|
("dict"
|
||||||
|
(for-each
|
||||||
|
(fn
|
||||||
|
(key)
|
||||||
|
(scan-io-refs-walk (dict-get node key) io-names refs))
|
||||||
|
(keys node)))
|
||||||
|
(_ nil))))
|
||||||
|
(define
|
||||||
|
scan-io-refs
|
||||||
|
:effects ()
|
||||||
|
(fn
|
||||||
|
(node (io-names :as list))
|
||||||
|
(let ((refs (list))) (scan-io-refs-walk node io-names refs) refs)))
|
||||||
|
(define
|
||||||
|
transitive-io-refs-walk
|
||||||
|
:effects ()
|
||||||
|
(fn
|
||||||
|
((n :as string)
|
||||||
|
(seen :as list)
|
||||||
|
(all-refs :as list)
|
||||||
|
(env :as dict)
|
||||||
|
(io-names :as list))
|
||||||
|
(when
|
||||||
|
(not (contains? seen n))
|
||||||
|
(append! seen n)
|
||||||
(let
|
(let
|
||||||
((target (render-target name env io-names)))
|
((val (env-get env n)))
|
||||||
(dict-set! comp-targets name target)
|
(cond
|
||||||
(if
|
(= (type-of val) "component")
|
||||||
(= target "server")
|
|
||||||
(do
|
(do
|
||||||
(append! server-list name)
|
|
||||||
(for-each
|
(for-each
|
||||||
(fn
|
(fn
|
||||||
((io-ref :as string))
|
((ref :as string))
|
||||||
(when
|
(when
|
||||||
(not (contains? io-deps io-ref))
|
(not (contains? all-refs ref))
|
||||||
(append! io-deps io-ref)))
|
(append! all-refs ref)))
|
||||||
(component-io-refs-cached name env io-names)))
|
(scan-io-refs (component-body val) io-names))
|
||||||
(append! client-list name))))
|
(for-each
|
||||||
needed)
|
(fn
|
||||||
{:io-deps io-deps :server server-list :components comp-targets :client client-list})))
|
((dep :as string))
|
||||||
|
(transitive-io-refs-walk dep seen all-refs env io-names))
|
||||||
(define
|
(scan-refs (component-body val))))
|
||||||
env-components
|
(= (type-of val) "macro")
|
||||||
:effects ()
|
(do
|
||||||
(fn
|
(for-each
|
||||||
((env :as dict))
|
(fn
|
||||||
(filter
|
((ref :as string))
|
||||||
|
(when
|
||||||
|
(not (contains? all-refs ref))
|
||||||
|
(append! all-refs ref)))
|
||||||
|
(scan-io-refs (macro-body val) io-names))
|
||||||
|
(for-each
|
||||||
|
(fn
|
||||||
|
((dep :as string))
|
||||||
|
(transitive-io-refs-walk dep seen all-refs env io-names))
|
||||||
|
(scan-refs (macro-body val))))
|
||||||
|
:else nil)))))
|
||||||
|
(define
|
||||||
|
transitive-io-refs
|
||||||
|
:effects ()
|
||||||
(fn
|
(fn
|
||||||
((k :as string))
|
((name :as string) (env :as dict) (io-names :as list))
|
||||||
(let ((v (env-get env k))) (or (component? v) (macro? v))))
|
(let
|
||||||
(keys env))))
|
((all-refs (list))
|
||||||
|
(seen (list))
|
||||||
|
(key (if (starts-with? name "~") name (str "~" name))))
|
||||||
)) ;; end define-library
|
(transitive-io-refs-walk key seen all-refs env io-names)
|
||||||
|
all-refs)))
|
||||||
|
(define
|
||||||
|
compute-all-io-refs
|
||||||
|
:effects (mutation)
|
||||||
|
(fn
|
||||||
|
((env :as dict) (io-names :as list))
|
||||||
|
(for-each
|
||||||
|
(fn
|
||||||
|
((name :as string))
|
||||||
|
(let
|
||||||
|
((val (env-get env name)))
|
||||||
|
(when
|
||||||
|
(= (type-of val) "component")
|
||||||
|
(component-set-io-refs!
|
||||||
|
val
|
||||||
|
(transitive-io-refs name env io-names)))))
|
||||||
|
(env-components env))))
|
||||||
|
(define
|
||||||
|
component-io-refs-cached
|
||||||
|
:effects ()
|
||||||
|
(fn
|
||||||
|
((name :as string) (env :as dict) (io-names :as list))
|
||||||
|
(let
|
||||||
|
((key (if (starts-with? name "~") name (str "~" name))))
|
||||||
|
(let
|
||||||
|
((val (env-get env key)))
|
||||||
|
(if
|
||||||
|
(and
|
||||||
|
(= (type-of val) "component")
|
||||||
|
(not (nil? (component-io-refs val)))
|
||||||
|
(not (empty? (component-io-refs val))))
|
||||||
|
(component-io-refs val)
|
||||||
|
(transitive-io-refs name env io-names))))))
|
||||||
|
(define
|
||||||
|
component-pure?
|
||||||
|
:effects ()
|
||||||
|
(fn
|
||||||
|
(name (env :as dict) (io-names :as list))
|
||||||
|
(let
|
||||||
|
((key (if (starts-with? name "~") name (str "~" name))))
|
||||||
|
(let
|
||||||
|
((val (if (env-has? env key) (env-get env key) nil)))
|
||||||
|
(if
|
||||||
|
(and
|
||||||
|
(= (type-of val) "component")
|
||||||
|
(not (nil? (component-io-refs val)))
|
||||||
|
(not (empty? (component-io-refs val))))
|
||||||
|
false
|
||||||
|
(empty? (transitive-io-refs name env io-names)))))))
|
||||||
|
(define
|
||||||
|
render-target
|
||||||
|
:effects ()
|
||||||
|
(fn
|
||||||
|
(name (env :as dict) (io-names :as list))
|
||||||
|
(let
|
||||||
|
((key (if (starts-with? name "~") name (str "~" name))))
|
||||||
|
(let
|
||||||
|
((val (if (env-has? env key) (env-get env key) nil)))
|
||||||
|
(if
|
||||||
|
(not (= (type-of val) "component"))
|
||||||
|
"server"
|
||||||
|
(let
|
||||||
|
((affinity (component-affinity val)))
|
||||||
|
(cond
|
||||||
|
(= affinity "server")
|
||||||
|
"server"
|
||||||
|
(= affinity "client")
|
||||||
|
"client"
|
||||||
|
(not (component-pure? name env io-names))
|
||||||
|
"server"
|
||||||
|
:else "client")))))))
|
||||||
|
(define
|
||||||
|
page-render-plan
|
||||||
|
:effects ()
|
||||||
|
(fn
|
||||||
|
((page-source :as string) (env :as dict) (io-names :as list))
|
||||||
|
(let
|
||||||
|
((needed (components-needed page-source env))
|
||||||
|
(comp-targets (dict))
|
||||||
|
(server-list (list))
|
||||||
|
(client-list (list))
|
||||||
|
(io-deps (list)))
|
||||||
|
(for-each
|
||||||
|
(fn
|
||||||
|
((name :as string))
|
||||||
|
(let
|
||||||
|
((target (render-target name env io-names)))
|
||||||
|
(dict-set! comp-targets name target)
|
||||||
|
(if
|
||||||
|
(= target "server")
|
||||||
|
(do
|
||||||
|
(append! server-list name)
|
||||||
|
(for-each
|
||||||
|
(fn
|
||||||
|
((io-ref :as string))
|
||||||
|
(when
|
||||||
|
(not (contains? io-deps io-ref))
|
||||||
|
(append! io-deps io-ref)))
|
||||||
|
(component-io-refs-cached name env io-names)))
|
||||||
|
(append! client-list name))))
|
||||||
|
needed)
|
||||||
|
{:io-deps io-deps :server server-list :components comp-targets :client client-list})))
|
||||||
|
(define
|
||||||
|
env-components
|
||||||
|
:effects ()
|
||||||
|
(fn
|
||||||
|
((env :as dict))
|
||||||
|
(filter
|
||||||
|
(fn
|
||||||
|
((k :as string))
|
||||||
|
(let ((v (env-get env k))) (or (component? v) (macro? v))))
|
||||||
|
(keys env)))))) ;; end define-library
|
||||||
|
|
||||||
;; Re-export to global namespace for backward compatibility
|
;; Re-export to global namespace for backward compatibility
|
||||||
(import (web deps))
|
(import (web deps))
|
||||||
|
|||||||
1565
web/engine.sx
1565
web/engine.sx
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user