summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKarl Berry <karl@freefriends.org>2024-03-20 19:42:29 +0000
committerKarl Berry <karl@freefriends.org>2024-03-20 19:42:29 +0000
commit237d9a31d48a632df9efb71b7de3cfec72c1903f (patch)
treeae4e5963a1fcae9b2f331fc345926e94d26890f0
parentb0164f8266e58fb6a4977fefa14bf8ccc56cd128 (diff)
ideavault (20mar24)
git-svn-id: svn://tug.org/texlive/trunk@70711 c570f23f-e606-0410-a88d-b1316a301751
-rw-r--r--Master/texmf-dist/doc/lualatex/ideavault/README.md27
-rw-r--r--Master/texmf-dist/doc/lualatex/ideavault/ideavault-doc.pdfbin0 -> 35243 bytes
-rw-r--r--Master/texmf-dist/doc/lualatex/ideavault/ideavault-doc.tex178
-rw-r--r--Master/texmf-dist/tex/lualatex/ideavault/ideavault-lua.lua355
-rw-r--r--Master/texmf-dist/tex/lualatex/ideavault/ideavault.sty67
-rwxr-xr-xMaster/tlpkg/bin/tlpkg-ctan-check2
-rw-r--r--Master/tlpkg/tlpsrc/collection-luatex.tlpsrc1
-rw-r--r--Master/tlpkg/tlpsrc/ideavault.tlpsrc0
8 files changed, 629 insertions, 1 deletions
diff --git a/Master/texmf-dist/doc/lualatex/ideavault/README.md b/Master/texmf-dist/doc/lualatex/ideavault/README.md
new file mode 100644
index 00000000000..9a1327791aa
--- /dev/null
+++ b/Master/texmf-dist/doc/lualatex/ideavault/README.md
@@ -0,0 +1,27 @@
+Idea Vault
+
+## Description
+
+Package for management and reuse LaTeX content (ideas).
+
+It allows idea declaration and invocation, while also supporting dependencies and idea tagging.
+
+It is LuaLaTeX package -- it depends on Lua and cannot be used without it.
+
+## LICENCE
+
+Copyright 2024 Tomasz M. Czarkowski
+
+This work may be distributed and/or modified under the
+conditions of the LaTeX Project Public License, either version 1.3c
+of this license or any later version.
+The latest version of this license is in
+ https://www.latex-project.org/lppl.txt
+and version 1.3c or later is part of all distributions of LaTeX
+version 2008 or later.
+
+This work has the LPPL maintenance status `maintained'.
+
+The Current Maintainer of this work is Tomasz M. Czarkowski
+
+This work consists of the files ideavault.sty and ideavault-lua.lua \ No newline at end of file
diff --git a/Master/texmf-dist/doc/lualatex/ideavault/ideavault-doc.pdf b/Master/texmf-dist/doc/lualatex/ideavault/ideavault-doc.pdf
new file mode 100644
index 00000000000..4f3d1562ed0
--- /dev/null
+++ b/Master/texmf-dist/doc/lualatex/ideavault/ideavault-doc.pdf
Binary files differ
diff --git a/Master/texmf-dist/doc/lualatex/ideavault/ideavault-doc.tex b/Master/texmf-dist/doc/lualatex/ideavault/ideavault-doc.tex
new file mode 100644
index 00000000000..7bba38fc449
--- /dev/null
+++ b/Master/texmf-dist/doc/lualatex/ideavault/ideavault-doc.tex
@@ -0,0 +1,178 @@
+\documentclass[11pt]{article}
+\usepackage{ideavault}
+\author{Tomasz M. Czarkowski}
+\title{Idea Vault}
+
+\begin{document}
+\maketitle
+
+\section{Why}
+\par Assume, You are writing something large. For example, like me, a TT RPG system.
+\par Often You will find Yourself wanting to declare some concept (idea, if You indulge me) and later print it all over the place, without needing to copy and paste \textbf{it}
+\par For really complicated projects, You may want to classify some ideas using tags, or make them depend on each other.
+\par This is what the Idea Vault is used for.
+
+\section{Dependencies}
+\par Idea Vault requires some packages (most of them for styling the printed idea).
+\begin{itemize}
+\item \texttt{bookmark}
+\item \texttt{xargs}
+\item \texttt{luacode}
+\item \texttt{xpatch}
+\item \texttt{mdframed}
+\item \texttt{needspace}
+\end{itemize}
+
+\section{Usage}
+\par For idea creation there is \texttt{\textbackslash{}createIdea}, while for printing them there are a few more:
+\begin{itemize}
+\item \texttt{\textbackslash{}printIdea} -- prints one idea
+\item \texttt{\textbackslash{}printAllWithPrefix} -- prints all ideas in given prefix
+\item \texttt{\textbackslash{}printAllWithTag} -- prints all ideas in given prefix that have given tag
+\end{itemize}
+
+\subsection{\textbackslash{}createIdea}
+\begin{verbatim}
+\printIdea[prefix]{name}{content}[dependencies][tags][weight]
+\end{verbatim}
+\begin{itemize}
+\item \texttt{prefix}: prefix to locate the idea in. By default, equals to \texttt{default}.
+\item \texttt{name}: name of the idea. If such name exists in prefix, it will be overwritten
+\item \texttt{content}: valid LaTeX code for the content of idea. It will be evaluated at invocation time.
+\item \texttt{dependencies}: each dependency is Lua pair of strings, multiple dependencies are separated with comma.
+\item \texttt{tags}: each tag is Lua string, multiple tags are separated with comma.
+\item \texttt{weight}: a number used for sorting ideas when multiple are printed at the same time. By default, equal to \texttt{-1}.
+\end{itemize}
+\par Note: due to some strange problems on LaTeX/Lua connection, for ideas with exactly one dependency, the \{\} needs to be duplicated, while for multi dependency there is no need.
+\par Example:
+\begin{verbatim}
+\createIdea[somePrefix]{Not So Great Idea}{
+\par Boring content
+}[]["tag2"][1]
+
+\createIdea[somePrefix]{Great Idea}{
+ \par Long content
+ \par Very Interesting
+}[{{"somePrefix", "Not So Great Idea"}}]["tag1"][13]
+\end{verbatim}
+
+\subsection{\textbackslash{}printIdea}
+\begin{verbatim}
+\printIdea[prefix][style]{name}
+\end{verbatim}
+
+\begin{itemize}
+\item \texttt{prefix}: prefix to locate the idea in. By default, equals to \texttt{default}.
+\item \texttt{style}: style for printing the idea. For options, see later.
+\item \texttt{name}: name of the idea.
+\end{itemize}
+
+\par Example:
+\begin{verbatim}
+\printIdea[somePrefix][lcbf]{Great Idea}
+\end{verbatim}
+
+\subsection{\textbackslash{}printAllWithPrefix}
+\begin{verbatim}
+\printIdea[prefix][style]
+\end{verbatim}
+
+\begin{itemize}
+\item \texttt{prefix}: prefix to print.
+\item \texttt{style}: style for printing the idea. For options, see later.
+\end{itemize}
+
+\par Example:
+\begin{verbatim}
+\printIdeaAllWithPrefix[somePrefix][lcbf]
+\end{verbatim}
+
+\subsection{\textbackslash{}printAllWithTag}
+\begin{verbatim}
+\printIdea[prefix][style]{tag}
+\end{verbatim}
+
+\begin{itemize}
+\item \texttt{prefix}: prefix to locate the ideas in.
+\item \texttt{style}: style for printing the idea. For options, see later.
+\item \texttt{tag}: tag to print
+\end{itemize}
+
+\par Example:
+\begin{verbatim}
+\printIdeaAllWithTag[somePrefix][lcbf]{tag1}
+\end{verbatim}
+
+\section{Style}
+\par Printing style is decided by one letter flags.
+\begin{itemize}
+\item \texttt{f}: Frame whole idea
+\item \texttt{c}: Center whole idea
+\item \texttt{b}: Create bookmark leading to the idea
+\item \texttt{l}: Use large font for the name
+\item \texttt{s}: Reserve some space using needspace to avoid awkward pagebreaks
+\item \texttt{e}: emph the name
+\item \texttt{q}: do not print the name
+\end{itemize}
+
+\section{Example}
+\subsection{Code}
+\begin{verbatim}
+\createIdea[somePrefix]{Not So Great Idea}{
+\par Boring content
+}[]["tag2"][1]
+
+\createIdea[somePrefix]{Great Idea}{
+\par Long content
+\par Very Interesting
+}[{{"somePrefix", "Not So Great Idea"}}]["tag1"][13]
+
+\createIdea[somePrefix2]{Third Idea}{
+\par Third try is even longer
+\par But not very
+\par Only 3 lines
+}[{"somePrefix", "Not So Great Idea"}, {"somePrefix", "Great Idea"}][][21]
+
+\par Here is a Great Idea:
+\printIdea[somePrefix][lcf]{Great Idea}
+\vspace{1em}
+\par Here are all ideas in prefix \texttt{somePrefix}:
+\printAllWithPrefix[somePrefix][lcf]
+\vspace{1em}
+\par Here are all ideas in prefix \texttt{somePrefix} with tag \texttt{tag1}:
+\printAllWithTag[somePrefix][lcf]{tag1}
+\vspace{1em}
+\par And here is Third Idea, presenting nested dependencies:
+\printIdea[somePrefix2][lcf]{Third Idea}
+
+\end{verbatim}
+\subsection{Result}
+
+\createIdea[somePrefix]{Not So Great Idea}{
+\par Boring content
+}[]["tag2"][1]
+
+\createIdea[somePrefix]{Great Idea}{
+\par Long content
+\par Very Interesting
+}[{{"somePrefix", "Not So Great Idea"}}]["tag1"][13]
+
+\createIdea[somePrefix2]{Third Idea}{
+\par Third try is even longer
+\par But not very
+\par Only 3 lines
+}[{"somePrefix", "Not So Great Idea"}, {"somePrefix", "Great Idea"}][][21]
+
+\par Here is a Great Idea:
+\printIdea[somePrefix][lcf]{Great Idea}
+\vspace{1em}
+\par Here are all ideas in prefix \texttt{somePrefix}:
+\printAllWithPrefix[somePrefix][lcf]
+\vspace{1em}
+\par Here are all ideas in prefix \texttt{somePrefix} with tag \texttt{tag1}:
+\printAllWithTag[somePrefix][lcf]{tag1}
+\vspace{1em}
+\par And here is Third Idea, presenting nested dependencies:
+\printIdea[somePrefix2][lcf]{Third Idea}
+
+\end{document} \ No newline at end of file
diff --git a/Master/texmf-dist/tex/lualatex/ideavault/ideavault-lua.lua b/Master/texmf-dist/tex/lualatex/ideavault/ideavault-lua.lua
new file mode 100644
index 00000000000..6d4de1e383a
--- /dev/null
+++ b/Master/texmf-dist/tex/lualatex/ideavault/ideavault-lua.lua
@@ -0,0 +1,355 @@
+-- ideavault-lua.lua
+-- Copyright 2024 Tomasz M. Czarkowski
+--
+-- This work may be distributed and/or modified under the
+-- conditions of the LaTeX Project Public License, either version 1.3c
+-- of this license or any later version.
+-- The latest version of this license is in
+-- https://www.latex-project.org/lppl.txt
+-- and version 1.3c or later is part of all distributions of LaTeX
+-- version 2008 or later.
+--
+-- This work has the LPPL maintenance status `maintained'.
+--
+-- The Current Maintainer of this work is Tomasz M. Czarkowski
+--
+-- This work consists of the files ideavault.sty and ideavault-lua.lua
+
+IdeaVaultClass = {}
+IdeaVaultClass.__index = IdeaVaultClass
+
+setmetatable(IdeaVaultClass, {
+ __call = function (cls, ...)
+ return cls.new(...)
+ end,
+})
+
+function IdeaVaultClass.new()
+ local self = setmetatable({}, IdeaVaultClass)
+ self.all = {}
+ return self
+end
+
+function IdeaVaultClass:addPrefix(prefix)
+ if (self.all[prefix] == nil)
+ then
+ self.all[prefix] = {}
+ end
+end
+
+function IdeaVaultClass:set(prefix, key, value)
+ self:addPrefix(prefix)
+ self.all[prefix][key] = value
+end
+
+function IdeaVaultClass:contains(prefix, key)
+ if (self.all[prefix] == nil or self.all[prefix][key] == nil)
+ then
+ return false
+ end
+ return true
+end
+
+function IdeaVaultClass:get(prefix, key)
+ if (self.all[prefix] == nil)
+ then
+ die("Prefix: '" .. prefix .. "' does not exist!")
+ end
+ if (self.all[prefix][key] == nil)
+ then
+ die("Key: '" .. key .. "' not found in prefix: '" .. prefix .. "'!")
+ end
+
+ return self.all[prefix][key]
+end
+
+function IdeaVaultClass:getAllWithPrefix(prefix)
+ ideas = {}
+ pr = self.all[prefix]
+ for _, value in pairs(pr)
+ do
+ table.insert(ideas, value)
+ end
+ table.sort(ideas, function(a,b)
+ if b:isGreaterThan(a) then return true end
+ end)
+ return ideas
+end
+
+function IdeaVaultClass:getAllWithTag(prefix, tag)
+ ideas = {}
+ pr = self.all[prefix]
+ for _, value in pairs(pr)
+ do
+ if (value:hasTag(tag))
+ then
+ table.insert(ideas, value)
+ end
+ end
+ table.sort(ideas, function(a,b)
+ if b:isGreaterThan(a) then return true end
+ end)
+ return ideas
+end
+
+function IdeaVaultClass:printIdea(prefix, key, style)
+ idea = self:get(prefix, key)
+ idea:printSelf(style)
+end
+
+function IdeaVaultClass:printAllWithTag(prefix, tag, style)
+ ideas = self:getAllWithTag(prefix, tag)
+ for _, idea in ipairs(ideas)
+ do
+ idea:printSelf(style)
+ end
+end
+
+function IdeaVaultClass:printAllWithPrefix(prefix, style)
+ ideas = self:getAllWithPrefix(prefix)
+ for _, idea in ipairs(ideas)
+ do
+ idea:printSelf(style)
+ end
+end
+
+IdeaClass = {}
+IdeaClass.__index = IdeaClass
+
+setmetatable(IdeaClass, {
+ __call = function (cls, ...)
+ return cls.new(...)
+ end,
+})
+
+function IdeaClass.new(title, content, dependencies, tags, value)
+ local self = setmetatable({}, IdeaClass)
+ self.title = title
+ self.content = content
+ self.dependencies = dependencies or {}
+ self.tags = tags or {}
+ self.value = value or 0
+ return self
+end
+
+function IdeaClass:getTitle()
+ return self.title
+end
+
+function IdeaClass:getContent()
+ return self.content
+end
+
+function IdeaClass:getTags()
+ return self.tags
+end
+
+function IdeaClass:getDependencies()
+ return self.dependencies
+end
+
+function IdeaClass:getValue()
+ return self.value
+end
+
+function IdeaClass:isGreaterThan(idea)
+ if self:getValue() > idea:getValue()
+ then
+ return true
+ else
+ if self:getValue() == idea:getValue()
+ then
+ if self:getTitle() > idea:getTitle()
+ then
+ return true
+ else
+ return false
+ end
+ else
+ return false
+ end
+ end
+end
+
+function IdeaClass:hasTag(tag)
+ for _, value in pairs(self:getTags())
+ do
+ if (value == tag)
+ then
+ return true
+ end
+ end
+ return false
+end
+
+bookmark_counter = 0
+
+function IdeaClass:printSelf(style)
+ style = style or ""
+ texio.write("Printing idea. Title: '" .. self:getTitle() .. "', style: '" .. style .. "'.\n")
+
+ local frame = false
+ local center = false
+ local bookmark = false
+ local large = false
+ local needSpace = false
+ local emph = false
+ local quiet = false
+ for c in style:gmatch"."
+ do
+ if (c == "f") then
+ frame = true
+ elseif (c == "c") then
+ center = true
+ elseif (c == "b") then
+ bookmark = true
+ elseif (c == "l") then
+ large = true
+ elseif (c == "s") then
+ needSpace = true
+ elseif (c == "e") then
+ emph = true
+ elseif (c == "q") then
+ quiet = true
+ else
+ die("Unknown style: '" .. c .. "'")
+ end
+ end
+ if (needSpace)
+ then
+ tex.sprint("\\needspace{5cm}%")
+ end
+ if (bookmark)
+ then
+ tex.sprint("\\hypertarget{" .. bookmark_counter .. "}{}%")
+ tex.sprint("\\bookmark[dest=" .. bookmark_counter .. ", level=\\bookDepth]{" .. self:getTitle() .. "}%")
+ --tex.sprint("\\subpdfbookmark{" .. self:getTitle() .. "}{" .. bookmark_counter .. "}%")
+ bookmark_counter = bookmark_counter + 1
+ tex.sprint("\\bookDown%")
+ end
+ if (frame)
+ then
+ tex.sprint("\\begin{mdframed}%")
+ end
+ if (center)
+ then
+ tex.sprint("\\begin{center}%")
+ end
+ if (not quiet)
+ then
+ if (large)
+ then
+ tex.sprint("{\\Large%")
+ end
+ if (emph)
+ then
+ tex.sprint("\\emph{,,")
+ end
+ tex.sprint(self:getTitle())
+ if (emph)
+ then
+ tex.sprint("''}%")
+ end
+ if (large)
+ then
+ tex.sprint("}%")
+ end
+ end
+ if (center)
+ then
+ tex.sprint("\\end{center}%")
+ end
+ tex.sprint(self:getContent() .. "%")
+ if (not quiet)
+ then
+ self:printDependencies()
+ end
+ if (frame)
+ then
+ tex.sprint("\\end{mdframed}%")
+ end
+ if (bookmark)
+ then
+ tex.sprint("\\bookUp%")
+ end
+end
+
+function IdeaClass:printDependencies()
+ for _, dependency in ipairs(self:getDependencies())
+ do
+ dependency:resolve():printSelf("lcf")
+ end
+end
+
+DependencyClass = {}
+DependencyClass.__index = DependencyClass
+
+setmetatable(DependencyClass, {
+ __call = function (cls, ...)
+ return cls.new(...)
+ end,
+})
+
+function DependencyClass.new(prefix, title)
+ local self = setmetatable({}, DependencyClass)
+ self.prefix = prefix or "default"
+ self.title = title or ""
+ if (not ideaVault:contains(self.prefix, self.title))
+ then
+ die("Broken dependency! Title: '" .. self.title .. "'. Prefix: '" .. self.prefix .. "'.")
+ end
+ return self
+end
+
+function DependencyClass:getPrefix()
+ return self.prefix
+end
+
+function DependencyClass:getTitle()
+ return self.title
+end
+
+function DependencyClass:resolve()
+ return ideaVault:get(self:getPrefix(), self:getTitle())
+end
+
+function DependencyClass:isGreaterThan(idea)
+ if self:getTitle() > idea:getTitle()
+ then
+ return true
+ else
+ return false
+ end
+end
+ideaVault = IdeaVaultClass()
+
+function createIdea(prefix, key, content, dependencies, tags, value)
+ t = "["
+ for _, a in pairs(tags)
+ do
+ t = t .. a .. ", "
+ end
+ t = t .. "]"
+ texio.write("Creating idea. Title: '" .. key .. "'. Prefix: '" .. prefix .. "', tags: " .. t .. ".\n")
+ deps = {}
+ for _, val in pairs(dependencies)
+ do
+ d = DependencyClass(val[1], val[2])
+ table.insert(deps, d)
+ end
+ table.sort(deps, function(a,b)
+ if b:isGreaterThan(a) then return true end
+ end)
+ idea = IdeaClass(key, content, deps, tags, value)
+ ideaVault:set(prefix, key, idea)
+end
+
+function die(reason)
+ reason = reason or "nil"
+ texio.write_nl("Critical error: " .. reason)
+ tex.error("Critical error: " .. reason)
+end
+
+function startsWith(text, prefix)
+ return text:find(prefix, 1, true) == 1
+end
diff --git a/Master/texmf-dist/tex/lualatex/ideavault/ideavault.sty b/Master/texmf-dist/tex/lualatex/ideavault/ideavault.sty
new file mode 100644
index 00000000000..07d60006457
--- /dev/null
+++ b/Master/texmf-dist/tex/lualatex/ideavault/ideavault.sty
@@ -0,0 +1,67 @@
+% ideavault.sty
+% Copyright 2024 Tomasz M. Czarkowski
+%
+% This work may be distributed and/or modified under the
+% conditions of the LaTeX Project Public License, either version 1.3c
+% of this license or any later version.
+% The latest version of this license is in
+% https://www.latex-project.org/lppl.txt
+% and version 1.3c or later is part of all distributions of LaTeX
+% version 2008 or later.
+%
+% This work has the LPPL maintenance status `maintained'.
+%
+% The Current Maintainer of this work is Tomasz M. Czarkowski
+%
+% This work consists of the files ideavault.sty and ideavault-lua.lua
+
+
+\NeedsTeXFormat{LaTeX2e}
+\ProvidesPackage{ideavault}[1.0.1]
+\RequirePackage[open,openlevel=2,atend]{bookmark}
+\RequirePackage{xargs}
+\RequirePackage{luacode}
+\RequirePackage{xpatch}
+\RequirePackage{mdframed}
+% Magic that lets mdframed nest more than 3 times
+\makeatletter
+\xpatchcmd{\mdf@preenvsetting}
+ {\mdf@envdepth >\tw@}
+ {\mdf@envdepth >20}
+ {}
+ {}
+\makeatother
+\RequirePackage{needspace}
+
+\directlua{require("ideavault-lua")}
+
+% #1: prefix
+% #2: name
+% #3: content
+% #4: dependencies
+% #5: tags
+% #6: weight
+\newcommandx{\createIdea}[6][1=default, 4=,5=,6=-1]{\luadirect{createIdea(\luastringN{#1}, \luastringN{#2}, \luastringN{#3}, {#4}, {#5}, #6)}}
+
+% #1: prefix
+% #2: style
+% #3: name
+\newcommandx{\printIdea}[3][1=default, 2=lcf]{\luadirect{ideaVault:printIdea(\luastringN{#1}, \luastringN{#3}, \luastringN{#2})}}
+
+% #1: prefix
+% #2: style
+\newcommandx{\printAllWithPrefix}[2][1=default, 2=lcf]{\luadirect{ideaVault:printAllWithPrefix(\luastringN{#1}, \luastringN{#2})}}
+
+% #1: prefix
+% #2: style
+% #3: tag
+\newcommandx{\printAllWithTag}[3][1=default, 2=lcf]{\luadirect{ideaVault:printAllWithTag(\luastringN{#1}, \luastringN{#3}, \luastringN{#2})}}
+
+% Internal commands for managing bookmark depth
+\newcounter{bookmarkDepth}
+\newcommand{\bookDown}{\stepcounter{bookmarkDepth}}
+\newcommand{\bookUp}{\addtocounter{bookmarkDepth}{-1}}
+\newcommand{\bookSet}[1]{\setcounter{bookmarkDepth}{#1}}
+\newcommand{\bookDepth}{\arabic{bookmarkDepth}}
+
+\endinput
diff --git a/Master/tlpkg/bin/tlpkg-ctan-check b/Master/tlpkg/bin/tlpkg-ctan-check
index d08a54f851b..6650328eae6 100755
--- a/Master/tlpkg/bin/tlpkg-ctan-check
+++ b/Master/tlpkg/bin/tlpkg-ctan-check
@@ -425,7 +425,7 @@ my @TLP_working = qw(
hyph-utf8 hyphen-base
hyphenat hyphenex hyplain
ibarra ibrackets ibycus-babel ibygrk icite icsv
- identkey idxcmds idxlayout
+ ideavault identkey idxcmds idxlayout
ieeeconf ieejtran ieeepes ieeetran ietfbibs iexec
ifallfalse iffont ifmslide ifmtarg ifnextok ifoddpage
ifplatform ifptex ifsym iftex ifthenx ifxptex
diff --git a/Master/tlpkg/tlpsrc/collection-luatex.tlpsrc b/Master/tlpkg/tlpsrc/collection-luatex.tlpsrc
index 039d7e8b2d9..4daffdd74be 100644
--- a/Master/tlpkg/tlpsrc/collection-luatex.tlpsrc
+++ b/Master/tlpkg/tlpsrc/collection-luatex.tlpsrc
@@ -25,6 +25,7 @@ depend emoji
depend emojicite
depend enigma
depend gitinfo-lua
+depend ideavault
depend innerscript
depend interpreter
depend kanaparser
diff --git a/Master/tlpkg/tlpsrc/ideavault.tlpsrc b/Master/tlpkg/tlpsrc/ideavault.tlpsrc
new file mode 100644
index 00000000000..e69de29bb2d
--- /dev/null
+++ b/Master/tlpkg/tlpsrc/ideavault.tlpsrc