Draft of the SocratiQ bot
7
_extensions/quarto-ext/lightbox/_extension.yml
Normal file
@@ -0,0 +1,7 @@
|
||||
title: Lightbox
|
||||
author: Posit Software, PBC
|
||||
version: 0.1.9
|
||||
quarto-required: ">=1.2.198"
|
||||
contributes:
|
||||
filters:
|
||||
- lightbox.lua
|
||||
9
_extensions/quarto-ext/lightbox/lightbox.css
Normal file
@@ -0,0 +1,9 @@
|
||||
|
||||
|
||||
|
||||
body:not(.glightbox-mobile) div.gslide div.gslide-description,
|
||||
body:not(.glightbox-mobile) div.gslide-description .gslide-title,
|
||||
body:not(.glightbox-mobile) div.gslide-description .gslide-desc {
|
||||
color: var(--quarto-body-color);
|
||||
background-color: var(--quarto-body-bg);
|
||||
}
|
||||
251
_extensions/quarto-ext/lightbox/lightbox.lua
Normal file
@@ -0,0 +1,251 @@
|
||||
-- whether we're automatically lightboxing
|
||||
local auto = false
|
||||
|
||||
-- whether we need lightbox dependencies added
|
||||
local needsLightbox = false
|
||||
|
||||
-- a counter used to ensure each image is in its own gallery
|
||||
local imgCount = 0
|
||||
|
||||
-- attributes to forward from the image to the newly created link
|
||||
local kDescription = "description"
|
||||
local kForwardedAttr = {
|
||||
"title", kDescription, "desc-position",
|
||||
"type", "effect", "zoomable", "draggable"
|
||||
}
|
||||
|
||||
local kLightboxClass = "lightbox"
|
||||
local kNoLightboxClass = "nolightbox"
|
||||
local kGalleryPrefix = "quarto-lightbox-gallery-"
|
||||
|
||||
-- A list of images already within links that we can use to filter
|
||||
local imagesWithinLinks = pandoc.List({})
|
||||
|
||||
local function readAttrValue(el, attrName)
|
||||
if attrName == kDescription then
|
||||
local doc = pandoc.read(el.attr.attributes[attrName])
|
||||
local attrInlines = doc.blocks[1].content
|
||||
return pandoc.write(pandoc.Pandoc(attrInlines), "html")
|
||||
else
|
||||
return el[attrName]
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
return {
|
||||
{
|
||||
Meta = function(meta)
|
||||
|
||||
-- If the mode is auto, we need go ahead and
|
||||
-- run if there are any images (ideally we would)
|
||||
-- filter to images in the body, but that can be
|
||||
-- left for future me to deal with
|
||||
-- supports:
|
||||
-- lightbox: auto
|
||||
-- or
|
||||
-- lightbox:
|
||||
-- match: auto
|
||||
local lbMeta = meta.lightbox
|
||||
if lbMeta ~= nil and type(lbMeta) == 'table' then
|
||||
if lbMeta[1] ~= nil then
|
||||
if lbMeta[1]['text'] == "auto" then
|
||||
auto = true
|
||||
end
|
||||
elseif lbMeta.match ~= nil and pandoc.utils.stringify(lbMeta.match) == 'auto' then
|
||||
auto = true
|
||||
elseif lbMeta == true then
|
||||
auto = true
|
||||
end
|
||||
end
|
||||
end,
|
||||
-- Find images that are already within links
|
||||
-- we'll use this to filter out these images if
|
||||
-- the most is auto
|
||||
Link = function(linkEl)
|
||||
pandoc.walk_inline(linkEl, {
|
||||
Image = function(imageEl)
|
||||
imagesWithinLinks[#imagesWithinLinks + 1] = imageEl
|
||||
end
|
||||
})
|
||||
end
|
||||
},{
|
||||
Div = function(div)
|
||||
if div.classes:includes("cell") and div.attributes["lightbox"] ~= nil then
|
||||
meta = quarto.json.decode(div.attributes["lightbox"])
|
||||
local imgCount=0
|
||||
div = div:walk({
|
||||
Image = function(imgEl)
|
||||
imgCount = imgCount + 1
|
||||
if (type(meta) == "table" and meta[kNoLightboxClass] == true) or meta == false then
|
||||
imgEl.classes:insert(kNoLightboxClass)
|
||||
else
|
||||
if not auto and ((type(meta) == "table" and not meta[kNoLightboxClass]) or meta == true) then
|
||||
imgEl.classes:insert(kLightboxClass)
|
||||
end
|
||||
if (type(meta) == "table") then
|
||||
if meta.group then
|
||||
imgEl.attr.attributes.group = meta.group or imgEl.attr.attributes.group
|
||||
end
|
||||
for _, v in next, kForwardedAttr do
|
||||
if type(meta[v]) == "table" and #meta[v] > 1 then
|
||||
-- if list attributes it should be one per plot
|
||||
if imgCount > #meta[v] then
|
||||
quarto.log.warning("More plots than '" .. v .. "' passed in YAML chunk options.")
|
||||
else
|
||||
attrLb = meta[v][imgCount]
|
||||
end
|
||||
else
|
||||
-- Otherwise reuse the single attributes
|
||||
attrLb = meta[v]
|
||||
end
|
||||
imgEl.attr.attributes[v] = attrLb or imgEl.attr.attributes[v]
|
||||
end
|
||||
end
|
||||
end
|
||||
return imgEl
|
||||
end
|
||||
})
|
||||
div.attributes["lightbox"] = nil
|
||||
end
|
||||
return div
|
||||
end
|
||||
},
|
||||
{
|
||||
Image = function(imgEl)
|
||||
if quarto.doc.is_format("html:js") then
|
||||
local isAlreadyLinked = imagesWithinLinks:includes(imgEl)
|
||||
if (not isAlreadyLinked and auto and not imgEl.classes:includes(kNoLightboxClass))
|
||||
or imgEl.classes:includes('lightbox') then
|
||||
-- note that we need to include the dependency for lightbox
|
||||
needsLightbox = true
|
||||
imgCount = imgCount + 1
|
||||
|
||||
-- remove the class from the image
|
||||
imgEl.attr.classes = imgEl.attr.classes:filter(function(clz)
|
||||
return clz ~= kLightboxClass
|
||||
end)
|
||||
|
||||
-- attributes for the link
|
||||
local linkAttributes = {}
|
||||
|
||||
-- mark this image as a lightbox target
|
||||
linkAttributes.class = kLightboxClass
|
||||
|
||||
-- get the alt text from image and use that as title
|
||||
local title = nil
|
||||
if imgEl.caption ~= nil and #imgEl.caption > 0 then
|
||||
linkAttributes.title = pandoc.utils.stringify(imgEl.caption)
|
||||
elseif imgEl.attributes['fig-alt'] ~= nil and #imgEl.attributes['fig-alt'] > 0 then
|
||||
linkAttributes.title = pandoc.utils.stringify(imgEl.attributes['fig-alt'])
|
||||
end
|
||||
|
||||
-- move a group attribute to the link, if present
|
||||
if imgEl.attr.attributes.group ~= nil then
|
||||
linkAttributes.gallery = imgEl.attr.attributes.group
|
||||
imgEl.attr.attributes.group = nil
|
||||
else
|
||||
linkAttributes.gallery = kGalleryPrefix .. imgCount
|
||||
end
|
||||
|
||||
-- forward any other known attributes
|
||||
for i, v in ipairs(kForwardedAttr) do
|
||||
if imgEl.attr.attributes[v] ~= nil then
|
||||
-- forward the attribute
|
||||
linkAttributes[v] = readAttrValue(imgEl, v)
|
||||
|
||||
-- clear the attribute
|
||||
imgEl.attr.attributes[v] = nil
|
||||
end
|
||||
|
||||
-- clear the title
|
||||
if (imgEl.title == 'fig:') then
|
||||
imgEl.title = ""
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
-- wrap decorated images in a link with appropriate attrs
|
||||
local link = pandoc.Link({imgEl}, imgEl.src, nil, linkAttributes)
|
||||
return link
|
||||
end
|
||||
end
|
||||
end,
|
||||
Meta = function(meta)
|
||||
-- If we discovered lightbox-able images
|
||||
-- we need to include the dependencies
|
||||
if needsLightbox then
|
||||
-- add the dependency
|
||||
quarto.doc.add_html_dependency({
|
||||
name = 'glightbox',
|
||||
scripts = {'resources/js/glightbox.min.js'},
|
||||
stylesheets = {'resources/css/glightbox.min.css', 'lightbox.css'}
|
||||
})
|
||||
|
||||
-- read lightbox options
|
||||
local lbMeta = meta.lightbox
|
||||
local lbOptions = {}
|
||||
local readEffect = function(el)
|
||||
local val = pandoc.utils.stringify(el)
|
||||
if val == "fade" or val == "zoom" or val == "none" then
|
||||
return val
|
||||
else
|
||||
error("Invalid effect " + val)
|
||||
end
|
||||
end
|
||||
|
||||
-- permitted options include:
|
||||
-- lightbox:
|
||||
-- effect: zoom | fade | none
|
||||
-- desc-position: top | bottom | left |right
|
||||
-- loop: true | false
|
||||
-- class: <class-name>
|
||||
local effect = "zoom"
|
||||
local descPosition = "bottom"
|
||||
local loop = true
|
||||
local skin = nil
|
||||
|
||||
-- The selector controls which elements are targeted.
|
||||
-- currently, it always targets .lightbox elements
|
||||
-- and there is no way for the user to change this
|
||||
local selector = "." .. kLightboxClass
|
||||
|
||||
if lbMeta ~= nil and type(lbMeta) == 'table' then
|
||||
if lbMeta.effect ~= nil then
|
||||
effect = readEffect(lbMeta.effect)
|
||||
end
|
||||
|
||||
if lbMeta['desc-position'] ~= nil then
|
||||
descPosition = pandoc.utils.stringify(lbMeta['desc-position'])
|
||||
end
|
||||
|
||||
if lbMeta['css-class'] ~= nil then
|
||||
skin = pandoc.utils.stringify(lbMeta['css-class'])
|
||||
end
|
||||
|
||||
if lbMeta.loop ~= nil then
|
||||
loop = lbMeta.loop
|
||||
end
|
||||
end
|
||||
|
||||
-- Generate the options to configure lightbox
|
||||
local options = {
|
||||
selector = selector,
|
||||
closeEffect = effect,
|
||||
openEffect = effect,
|
||||
descPosition = descPosition,
|
||||
loop = loop,
|
||||
}
|
||||
if skin ~= nil then
|
||||
options.skin = skin
|
||||
end
|
||||
local optionsJson = quarto.json.encode(options)
|
||||
|
||||
-- generate the initialization script with the correct options
|
||||
local scriptTag = "<script>var lightboxQuarto = GLightbox(" .. optionsJson .. ");</script>"
|
||||
|
||||
-- inject the rendering code
|
||||
quarto.doc.include_text("after-body", scriptTag)
|
||||
|
||||
end
|
||||
end
|
||||
}}
|
||||
1
_extensions/quarto-ext/lightbox/resources/css/glightbox.min.css
vendored
Normal file
1
_extensions/quarto-ext/lightbox/resources/js/glightbox.min.js
vendored
Normal file
@@ -96,8 +96,8 @@ book:
|
||||
- contents/dedication.qmd
|
||||
- contents/core/acknowledgements/acknowledgements.qmd
|
||||
- contents/contributors.qmd
|
||||
- contents/ai/socratiq.qmd
|
||||
- contents/about.qmd
|
||||
- contents/socratiq.qmd
|
||||
- text: "---"
|
||||
- contents/core/introduction/introduction.qmd
|
||||
- contents/core/ml_systems/ml_systems.qmd
|
||||
@@ -212,6 +212,7 @@ editor:
|
||||
|
||||
format:
|
||||
html:
|
||||
lightbox: true
|
||||
theme:
|
||||
light:
|
||||
- default
|
||||
|
||||
BIN
contents/ai/images/png/badges.png
Normal file
|
After Width: | Height: | Size: 10 KiB |
BIN
contents/ai/images/png/buttons.png
Normal file
|
After Width: | Height: | Size: 4.0 KiB |
BIN
contents/ai/images/png/chat_ask.png
Normal file
|
After Width: | Height: | Size: 52 KiB |
BIN
contents/ai/images/png/chat_context.png
Normal file
|
After Width: | Height: | Size: 61 KiB |
BIN
contents/ai/images/png/chat_explanation.png
Normal file
|
After Width: | Height: | Size: 151 KiB |
BIN
contents/ai/images/png/chat_related.png
Normal file
|
After Width: | Height: | Size: 38 KiB |
BIN
contents/ai/images/png/dashboard.png
Normal file
|
After Width: | Height: | Size: 914 KiB |
BIN
contents/ai/images/png/interface.png
Normal file
|
After Width: | Height: | Size: 1.4 MiB |
BIN
contents/ai/images/png/quiz_answers.png
Normal file
|
After Width: | Height: | Size: 128 KiB |
BIN
contents/ai/images/png/quiz_button.png
Normal file
|
After Width: | Height: | Size: 53 KiB |
BIN
contents/ai/images/png/quiz_questions.png
Normal file
|
After Width: | Height: | Size: 64 KiB |
BIN
contents/ai/images/png/settings.png
Normal file
|
After Width: | Height: | Size: 12 KiB |
BIN
contents/ai/images/png/shortcut.png
Normal file
|
After Width: | Height: | Size: 11 KiB |
84
contents/ai/socratiq.qmd
Normal file
@@ -0,0 +1,84 @@
|
||||
# 🤖 SocratiQ AI Tutor {.unnumbered}
|
||||
|
||||
## Welcome to SocratiQ: Your AI Learning Assistant
|
||||
|
||||
We're excited to introduce SocratiQ (pronounced "Socratic"), an innovative AI learning assistant integrated throughout this textbook. SocratiQ helps reinforce your learning through interactive quizzes and personalized assistance. SocratiQ's goal is to adapt to your needs while generating targeted questions and engaging in meaningful dialogue about the material.
|
||||
|
||||
Please note that this is an experimental feature. We are experimenting with the idea of creating a dynamic and personalized learning experience by harnessing the power of generative AI. We hope that this approach will transform how you interact with and absorb the complex concepts.
|
||||
|
||||
:::{.callout-warning}
|
||||
**About AI Responses**: While SocratiQ uses advanced AI to generate quizzes and provide assistance, like all AI systems, it may occasionally provide imperfect or incomplete answers. However, we've designed and tested it to ensure it's effective for supporting your learning journey. If you're unsure about any response, refer to the textbook content or consult your instructor.
|
||||
:::
|
||||
|
||||
You can access SocratiQ at any time using a keyboard shortcut shown in @fig-main-shortcut, which brings up the interface shown in @fig-main-interface.
|
||||
|
||||
{#fig-main-shortcut}
|
||||
|
||||
{#fig-main-interface}
|
||||
|
||||
|
||||
## Personalizing Your Learning Experience
|
||||
|
||||
Before diving into your studies, take a moment to configure SocratiQ for your academic level. This initial setup ensures that all interactions, from quiz questions to explanations, are tailored to your background knowledge. @fig-settings-panel shows where you can adjust these preferences.
|
||||
|
||||
{#fig-settings-panel}
|
||||
|
||||
## Learning with SocratiQ
|
||||
|
||||
As you progress through each section of the textbook, you'll find automatically generated quizzes designed to reinforce key concepts. These quizzes typically contain 3-5 questions, carefully crafted to check your understanding of the material you've just covered, as shown in @fig-quiz-interface.
|
||||
|
||||
{#fig-quiz-interface}
|
||||
|
||||
After submitting your answers, SocratiQ provides immediate feedback and detailed explanations for each question, as demonstrated in @fig-quiz-feedback.
|
||||
|
||||
{#fig-quiz-feedback}
|
||||
|
||||
## Getting Help with Concepts
|
||||
|
||||
When you encounter challenging concepts, SocratiQ offers two powerful ways to get help. First, you can select any text from the textbook and ask for a detailed explanation, as demonstrated in @fig-text-selection.
|
||||
|
||||
{#fig-text-selection}
|
||||
|
||||
Once you've selected the text, you can ask questions about it, and SocratiQ will provide detailed explanations based on that context, as illustrated in @fig-context-explanation.
|
||||
|
||||
{#fig-context-explanation}
|
||||
|
||||
@fig-interactive-chat shows the response for the ask in @fig-context-explanation.
|
||||
|
||||
{#fig-interactive-chat}
|
||||
|
||||
To enhance your learning experience, SocratiQ doesn't just answer your questions---it also suggests related content from the textbook that might be helpful for deeper understanding, as shown in @fig-related-content.
|
||||
|
||||
{#fig-related-content}
|
||||
|
||||
## Tracking Your Progress
|
||||
|
||||
SocratiQ maintains a comprehensive record of your learning journey. The progress dashboard, shown in @fig-progress-dashboard, displays your quiz performance statistics, learning streaks, and achievement badges.
|
||||
|
||||
{#fig-progress-dashboard}
|
||||
|
||||
As you continue to engage with the material and complete quizzes, you'll earn various badges that recognize your progress, as shown in @fig-achievement-badges.
|
||||
|
||||
{#fig-achievement-badges}
|
||||
|
||||
## Data Storage
|
||||
|
||||
:::{.callout-important}
|
||||
**Important Note**: All progress data is stored locally in your browser. Clearing your browser history or cache will erase your entire learning history, including quiz scores, streaks, and achievement badges. Please keep this in mind before clearing browser data.
|
||||
:::
|
||||
|
||||
## Technical Requirements
|
||||
|
||||
To use SocratiQ effectively, you'll need:
|
||||
|
||||
- Chrome or Safari browser
|
||||
- JavaScript enabled
|
||||
- Stable internet connection
|
||||
|
||||
## Providing Feedback
|
||||
|
||||
Your feedback helps us improve SocratiQ. You can report technical issues, suggest improvements to quiz questions, or share thoughts about AI responses using the feedback buttons located throughout the interface, as shown in @fig-feedback-interface.
|
||||
|
||||
{#fig-feedback-interface}
|
||||
|
||||
Remember: SocratiQ is designed to help you learn effectively. By consistently engaging with the quizzes, asking questions when needed, and tracking your progress, you'll get the most out of this AI learning assistant.
|
||||
|
Before Width: | Height: | Size: 13 KiB |
@@ -1,9 +0,0 @@
|
||||
# SocratiQ {.unnumbered}
|
||||
|
||||
Coming soon.
|
||||
|
||||
## Generative Learning
|
||||
|
||||
...
|
||||
|
||||
## How to Use the AI Bot
|
||||